Merge pull request #8950 from overleaf/bg-use-qpdf-xref-table

[clsi] parse xref table from qpdf output

GitOrigin-RevId: cc400c65082c7c8c28c21037b97b4fde66a85835
This commit is contained in:
Jakob Ackermann 2022-08-02 10:09:22 +01:00 committed by Copybot
parent a2010bf901
commit 18768d3f1b
38 changed files with 880 additions and 262 deletions

16
package-lock.json generated
View file

@ -23390,14 +23390,6 @@
"resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz",
"integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10=" "integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10="
}, },
"node_modules/pdfjs-dist": {
"version": "2.7.570",
"resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-2.7.570.tgz",
"integrity": "sha512-/ZkA1FwkEOyDaq11JhMLazdwQAA0F9uwrP7h/1L9Akt9KWh1G5/tkzS+bPuUELq2s2GDFnaT+kooN/aSjT7DXQ==",
"peerDependencies": {
"worker-loader": "^3.0.7"
}
},
"node_modules/pdfjs-dist210": { "node_modules/pdfjs-dist210": {
"name": "pdfjs-dist", "name": "pdfjs-dist",
"version": "2.10.377", "version": "2.10.377",
@ -31849,7 +31841,6 @@
"lockfile": "^1.0.4", "lockfile": "^1.0.4",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"p-limit": "^3.1.0", "p-limit": "^3.1.0",
"pdfjs-dist": "~2.7.570",
"request": "^2.88.2", "request": "^2.88.2",
"send": "^0.17.1", "send": "^0.17.1",
"workerpool": "^6.1.5" "workerpool": "^6.1.5"
@ -39703,7 +39694,6 @@
"mocha": "^8.4.0", "mocha": "^8.4.0",
"mock-fs": "^5.1.2", "mock-fs": "^5.1.2",
"p-limit": "^3.1.0", "p-limit": "^3.1.0",
"pdfjs-dist": "~2.7.570",
"request": "^2.88.2", "request": "^2.88.2",
"sandboxed-module": "^2.0.4", "sandboxed-module": "^2.0.4",
"send": "^0.17.1", "send": "^0.17.1",
@ -59303,12 +59293,6 @@
"resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz",
"integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10=" "integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10="
}, },
"pdfjs-dist": {
"version": "2.7.570",
"resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-2.7.570.tgz",
"integrity": "sha512-/ZkA1FwkEOyDaq11JhMLazdwQAA0F9uwrP7h/1L9Akt9KWh1G5/tkzS+bPuUELq2s2GDFnaT+kooN/aSjT7DXQ==",
"requires": {}
},
"pdfjs-dist210": { "pdfjs-dist210": {
"version": "npm:pdfjs-dist@2.10.377", "version": "npm:pdfjs-dist@2.10.377",
"resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-2.10.377.tgz", "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-2.10.377.tgz",

View file

@ -9,8 +9,12 @@ const Path = require('path')
const Settings = require('@overleaf/settings') const Settings = require('@overleaf/settings')
const OError = require('@overleaf/o-error') const OError = require('@overleaf/o-error')
const pLimit = require('p-limit') const pLimit = require('p-limit')
const { parseXrefTable } = require('../lib/pdfjs/parseXrefTable') const { parseXrefTable } = require('./XrefParser')
const { QueueLimitReachedError, TimedOutError } = require('./Errors') const {
QueueLimitReachedError,
TimedOutError,
NoXrefTableError,
} = require('./Errors')
const workerpool = require('workerpool') const workerpool = require('workerpool')
const Metrics = require('@overleaf/metrics') const Metrics = require('@overleaf/metrics')
@ -89,6 +93,9 @@ async function updateOtherEventLoop(contentDir, filePath, size, compileTime) {
if (e.message?.includes?.('Max queue size of ')) { if (e.message?.includes?.('Max queue size of ')) {
throw new QueueLimitReachedError() throw new QueueLimitReachedError()
} }
if (e.message?.includes?.('xref')) {
throw new NoXrefTableError(e.message)
}
throw e throw e
} }
} }
@ -115,6 +122,7 @@ async function updateSameEventLoop(contentDir, filePath, size, compileTime) {
size, size,
checkDeadline checkDeadline
) )
xRefEntries.sort((a, b) => { xRefEntries.sort((a, b) => {
return a.offset - b.offset return a.offset - b.offset
}) })

View file

@ -33,6 +33,7 @@ AlreadyCompilingError.prototype.__proto__ = Error.prototype
class QueueLimitReachedError extends OError {} class QueueLimitReachedError extends OError {}
class TimedOutError extends OError {} class TimedOutError extends OError {}
class NoXrefTableError extends OError {}
module.exports = Errors = { module.exports = Errors = {
QueueLimitReachedError, QueueLimitReachedError,
@ -40,4 +41,5 @@ module.exports = Errors = {
NotFoundError, NotFoundError,
FilesOutOfSyncError, FilesOutOfSyncError,
AlreadyCompilingError, AlreadyCompilingError,
NoXrefTableError,
} }

View file

@ -24,7 +24,11 @@ const Metrics = require('./Metrics')
const OutputFileOptimiser = require('./OutputFileOptimiser') const OutputFileOptimiser = require('./OutputFileOptimiser')
const ContentCacheManager = require('./ContentCacheManager') const ContentCacheManager = require('./ContentCacheManager')
const { QueueLimitReachedError, TimedOutError } = require('./Errors') const {
QueueLimitReachedError,
TimedOutError,
NoXrefTableError,
} = require('./Errors')
const OLDEST_BUILD_DIR = new Map() const OLDEST_BUILD_DIR = new Map()
const PENDING_PROJECT_ACTIONS = new Map() const PENDING_PROJECT_ACTIONS = new Map()
@ -397,6 +401,9 @@ module.exports = OutputCacheManager = {
pdfSize, pdfSize,
timings.compile, timings.compile,
function (err, result) { function (err, result) {
if (err && err instanceof NoXrefTableError) {
return callback(null, err.message)
}
if (err && err instanceof QueueLimitReachedError) { if (err && err instanceof QueueLimitReachedError) {
logger.warn({ err, outputDir }, 'pdf caching queue limit reached') logger.warn({ err, outputDir }, 'pdf caching queue limit reached')
stats['pdf-caching-queue-limit-reached'] = 1 stats['pdf-caching-queue-limit-reached'] = 1

View file

@ -0,0 +1,68 @@
const { NoXrefTableError } = require('./Errors')
const fs = require('fs')
const { O_RDONLY, O_NOFOLLOW } = fs.constants
const MAX_XREF_FILE_SIZE = 1024 * 1024
/** Parse qpdf --show-xref output to get a table of xref entries
*
* @param {string} filePath
* @param {integer} pdfFileSize
* @returns
*/
async function parseXrefTable(filePath, pdfFileSize) {
try {
// the xref table will be written to output.pdfxref when available
const xRefFilePath = filePath + 'xref'
// check the size of the file (as it is untrusted)
const stats = await fs.promises.stat(xRefFilePath)
if (!stats.isFile()) {
throw new NoXrefTableError('xref file invalid type')
}
if (stats.size === 0) {
throw new NoXrefTableError('xref file empty')
}
if (stats.size > MAX_XREF_FILE_SIZE) {
throw new NoXrefTableError('xref file too large')
}
const content = await fs.promises.readFile(xRefFilePath, {
encoding: 'ascii',
flag: O_RDONLY | O_NOFOLLOW,
})
// the qpdf xref table output looks like this:
//
// 3/0: uncompressed; offset = 194159
//
// we only need the uncompressed objects
const matches = content.matchAll(
// put an upper limit of 10^10 on all the matched numbers for safety
// ignore the generation id in "id/gen"
// in a linearized pdf all objects must have generation number 0
/^(\d{1,9})\/\d{1,9}: uncompressed; offset = (\d{1,9})$/gm
)
// include a zero-index object for backwards compatibility with
// our existing xref table parsing code
const xRefEntries = [{ offset: 0 }]
// extract all the xref table entries
for (const match of matches) {
const id = parseInt(match[1], 10) // must convert from strings to integers
const offset = parseInt(match[2], 10)
xRefEntries[id] = { offset, uncompressed: true }
}
if (xRefEntries.length === 1) {
throw new NoXrefTableError('xref file has no objects')
}
return { xRefEntries }
} catch (err) {
if (err instanceof NoXrefTableError) {
throw err
} else if (err.code) {
throw new NoXrefTableError(`xref file error ${err.code}`)
} else {
throw new NoXrefTableError('xref file parse error')
}
}
}
module.exports = {
parseXrefTable,
}

View file

@ -1,43 +0,0 @@
const { PDFDocument } = require('pdfjs-dist/lib/core/document')
const { LocalPdfManager } = require('pdfjs-dist/lib/core/pdf_manager')
const { MissingDataException } = require('pdfjs-dist/lib/core/core_utils')
const { FSStream } = require('./FSStream')
class FSPdfManager extends LocalPdfManager {
constructor(docId, { fh, size, checkDeadline }) {
const nonEmptyDummyBuffer = Buffer.alloc(1, 0)
super(docId, nonEmptyDummyBuffer)
this.stream = new FSStream(fh, 0, size, null, null, checkDeadline)
this.pdfDocument = new PDFDocument(this, this.stream)
}
async ensure(obj, prop, args) {
try {
const value = obj[prop]
if (typeof value === 'function') {
return value.apply(obj, args)
}
return value
} catch (ex) {
if (!(ex instanceof MissingDataException)) {
throw ex
}
await this.requestRange(ex.begin, ex.end)
return this.ensure(obj, prop, args)
}
}
requestRange(begin, end) {
return this.stream.requestRange(begin, end)
}
requestLoadedStream() {}
onLoadedStream() {}
terminate(reason) {}
}
module.exports = {
FSPdfManager,
}

View file

@ -1,148 +0,0 @@
const { Stream } = require('pdfjs-dist/lib/core/stream')
const { MissingDataException } = require('pdfjs-dist/lib/core/core_utils')
const BUF_SIZE = 1024 // read from the file in 1024 byte pages
class FSStream extends Stream {
constructor(fh, start, length, dict, cachedBytes, checkDeadline) {
const nonEmptyDummyBuffer = Buffer.alloc(1, 0)
super(nonEmptyDummyBuffer, start, length, dict)
delete this.bytes
this.fh = fh
this.checkDeadline = checkDeadline
this.cachedBytes = cachedBytes || []
}
get length() {
return this.end - this.start
}
get isEmpty() {
return this.length === 0
}
// Manage cached reads from the file
requestRange(begin, end) {
this.checkDeadline(`request range ${begin} - ${end}`)
// expand small ranges to read a larger amount
if (end - begin < BUF_SIZE) {
end = begin + BUF_SIZE
}
end = Math.min(end, this.length)
// keep a cache of previous reads with {begin,end,buffer} values
const result = {
begin,
end,
buffer: Buffer.alloc(end - begin, 0),
}
this.cachedBytes.push(result)
return this.fh.read(result.buffer, 0, end - begin, begin)
}
_ensureGetPos(pos) {
const found = this.cachedBytes.find(x => {
return x.begin <= pos && pos < x.end
})
if (!found) {
throw new MissingDataException(pos, pos + 1)
}
return found
}
_ensureGetRange(begin, end) {
end = Math.min(end, this.length) // BG: handle overflow case
const found = this.cachedBytes.find(x => {
return x.begin <= begin && end <= x.end
})
if (!found) {
throw new MissingDataException(begin, end)
}
return found
}
_readByte(found, pos) {
return found.buffer[pos - found.begin]
}
_readBytes(found, pos, end) {
return found.buffer.subarray(pos - found.begin, end - found.begin)
}
// handle accesses to the bytes
ensureByte(pos) {
this._ensureGetPos(pos) // may throw a MissingDataException
}
getByte() {
const pos = this.pos
if (this.pos >= this.end) {
return -1
}
const found = this._ensureGetPos(pos)
return this._readByte(found, this.pos++)
}
// BG: for a range, end is not included (see Buffer.subarray for example)
ensureBytes(length, forceClamped = false) {
const pos = this.pos
this._ensureGetRange(pos, pos + length)
}
getBytes(length, forceClamped = false) {
const pos = this.pos
const strEnd = this.end
const found = this._ensureGetRange(pos, pos + length)
if (!length) {
const subarray = this._readBytes(found, pos, strEnd)
// `this.bytes` is always a `Uint8Array` here.
return forceClamped ? new Uint8ClampedArray(subarray) : subarray
}
let end = pos + length
if (end > strEnd) {
end = strEnd
}
this.pos = end
const subarray = this._readBytes(found, pos, end)
// `this.bytes` is always a `Uint8Array` here.
return forceClamped ? new Uint8ClampedArray(subarray) : subarray
}
getByteRange() {
// BG: this isn't needed as far as I can tell
throw new Error('not implemented')
}
reset() {
this.pos = this.start
}
moveStart() {
this.start = this.pos
}
makeSubStream(start, length, dict = null) {
this.checkDeadline(`make sub stream start=${start}/length=${length}`)
// BG: had to add this check for null length, it is being called with only
// the start value at one point in the xref decoding. The intent is clear
// enough
// - a null length means "to the end of the file" -- not sure how it is
// working in the existing pdfjs code without this.
if (!length) {
length = this.end - start
}
return new FSStream(
this.fh,
start,
length,
dict,
this.cachedBytes,
this.checkDeadline
)
}
}
module.exports = { FSStream }

View file

@ -1,29 +0,0 @@
const fs = require('fs')
const { FSPdfManager } = require('./FSPdfManager')
async function parseXrefTable(path, size, checkDeadline) {
if (size === 0) {
return []
}
const file = await fs.promises.open(path)
try {
const manager = new FSPdfManager(0, { fh: file, size, checkDeadline })
await manager.ensureDoc('checkHeader')
checkDeadline('pdfjs: after checkHeader')
await manager.ensureDoc('parseStartXRef')
checkDeadline('pdfjs: after parseStartXRef')
await manager.ensureDoc('parse')
checkDeadline('pdfjs: after parse')
const xRefEntries = manager.pdfDocument.xref.entries || []
const startXRefTable = manager.pdfDocument.xref.topDict?.get('Prev')
return { xRefEntries, startXRefTable }
} finally {
file.close()
}
}
module.exports = {
parseXrefTable,
}

View file

@ -30,7 +30,6 @@
"lockfile": "^1.0.4", "lockfile": "^1.0.4",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"p-limit": "^3.1.0", "p-limit": "^3.1.0",
"pdfjs-dist": "~2.7.570",
"request": "^2.88.2", "request": "^2.88.2",
"send": "^0.17.1", "send": "^0.17.1",
"workerpool": "^6.1.5" "workerpool": "^6.1.5"

View file

@ -0,0 +1,81 @@
1/0: uncompressed; offset = 123103
2/0: uncompressed; offset = 123422
3/0: uncompressed; offset = 15
4/0: uncompressed; offset = 216
5/0: uncompressed; offset = 1084
6/0: uncompressed; offset = 1244
7/0: uncompressed; offset = 4001
8/0: uncompressed; offset = 4155
9/0: uncompressed; offset = 4297
10/0: uncompressed; offset = 4933
11/0: uncompressed; offset = 5309
12/0: uncompressed; offset = 5498
13/0: uncompressed; offset = 30250
14/0: uncompressed; offset = 31471
15/0: uncompressed; offset = 38404
16/0: uncompressed; offset = 39046
17/0: uncompressed; offset = 40166
18/0: uncompressed; offset = 40906
19/0: uncompressed; offset = 65560
20/0: uncompressed; offset = 74702
21/0: uncompressed; offset = 81705
22/0: uncompressed; offset = 97182
23/0: uncompressed; offset = 104117
24/0: uncompressed; offset = 111195
25/0: uncompressed; offset = 118571
26/0: compressed; stream = 6, index = 0
27/0: compressed; stream = 6, index = 1
28/0: compressed; stream = 6, index = 2
29/0: compressed; stream = 6, index = 3
30/0: compressed; stream = 6, index = 4
31/0: compressed; stream = 6, index = 5
32/0: compressed; stream = 6, index = 6
33/0: compressed; stream = 6, index = 7
34/0: compressed; stream = 6, index = 8
35/0: compressed; stream = 6, index = 9
36/0: compressed; stream = 6, index = 10
37/0: compressed; stream = 6, index = 11
38/0: compressed; stream = 6, index = 12
39/0: compressed; stream = 6, index = 13
40/0: compressed; stream = 6, index = 14
41/0: compressed; stream = 6, index = 15
42/0: compressed; stream = 6, index = 16
43/0: compressed; stream = 6, index = 17
44/0: compressed; stream = 6, index = 18
45/0: compressed; stream = 6, index = 19
46/0: compressed; stream = 6, index = 20
47/0: compressed; stream = 6, index = 21
48/0: compressed; stream = 6, index = 22
49/0: compressed; stream = 6, index = 23
50/0: compressed; stream = 6, index = 24
51/0: compressed; stream = 6, index = 25
52/0: compressed; stream = 6, index = 26
53/0: compressed; stream = 6, index = 27
54/0: compressed; stream = 6, index = 28
55/0: compressed; stream = 6, index = 29
56/0: compressed; stream = 6, index = 30
57/0: compressed; stream = 6, index = 31
58/0: compressed; stream = 6, index = 32
59/0: compressed; stream = 6, index = 33
60/0: compressed; stream = 6, index = 34
61/0: compressed; stream = 6, index = 35
62/0: compressed; stream = 6, index = 36
63/0: compressed; stream = 6, index = 37
64/0: compressed; stream = 6, index = 38
65/0: compressed; stream = 6, index = 39
66/0: compressed; stream = 6, index = 40
67/0: compressed; stream = 6, index = 41
68/0: compressed; stream = 6, index = 42
69/0: compressed; stream = 6, index = 43
70/0: compressed; stream = 6, index = 44
71/0: compressed; stream = 6, index = 45
72/0: compressed; stream = 6, index = 46
73/0: compressed; stream = 6, index = 47
74/0: compressed; stream = 6, index = 48
75/0: compressed; stream = 6, index = 49
76/0: compressed; stream = 6, index = 50
77/0: compressed; stream = 6, index = 51
78/0: compressed; stream = 6, index = 52
79/0: compressed; stream = 6, index = 53
80/0: compressed; stream = 6, index = 54
81/0: compressed; stream = 6, index = 55

View file

@ -0,0 +1,31 @@
1/0: uncompressed; offset = 59313
2/0: uncompressed; offset = 59561
3/0: uncompressed; offset = 15
4/0: uncompressed; offset = 216
5/0: uncompressed; offset = 734
6/0: uncompressed; offset = 784
7/0: uncompressed; offset = 913
8/0: uncompressed; offset = 1028
9/0: uncompressed; offset = 1528
10/0: uncompressed; offset = 9787
11/0: uncompressed; offset = 18282
12/0: uncompressed; offset = 33607
13/0: uncompressed; offset = 45579
14/0: uncompressed; offset = 58005
15/0: compressed; stream = 14, index = 0
16/0: compressed; stream = 14, index = 1
17/0: compressed; stream = 14, index = 2
18/0: compressed; stream = 14, index = 3
19/0: compressed; stream = 14, index = 4
20/0: compressed; stream = 14, index = 5
21/0: compressed; stream = 14, index = 6
22/0: compressed; stream = 14, index = 7
23/0: compressed; stream = 14, index = 8
24/0: compressed; stream = 14, index = 9
25/0: compressed; stream = 14, index = 10
26/0: compressed; stream = 14, index = 11
27/0: compressed; stream = 14, index = 12
28/0: compressed; stream = 14, index = 13
29/0: compressed; stream = 14, index = 14
30/0: compressed; stream = 14, index = 15
31/0: compressed; stream = 14, index = 16

View file

@ -0,0 +1,39 @@
1/0: uncompressed; offset = 67338
2/0: uncompressed; offset = 67606
3/0: uncompressed; offset = 15
4/0: uncompressed; offset = 216
5/0: uncompressed; offset = 790
6/0: uncompressed; offset = 840
7/0: uncompressed; offset = 975
8/0: uncompressed; offset = 1083
9/0: uncompressed; offset = 1578
10/0: uncompressed; offset = 9881
11/0: uncompressed; offset = 17868
12/0: uncompressed; offset = 29906
13/0: uncompressed; offset = 38400
14/0: uncompressed; offset = 46656
15/0: uncompressed; offset = 56198
16/0: uncompressed; offset = 65682
17/0: compressed; stream = 16, index = 0
18/0: compressed; stream = 16, index = 1
19/0: compressed; stream = 16, index = 2
20/0: compressed; stream = 16, index = 3
21/0: compressed; stream = 16, index = 4
22/0: compressed; stream = 16, index = 5
23/0: compressed; stream = 16, index = 6
24/0: compressed; stream = 16, index = 7
25/0: compressed; stream = 16, index = 8
26/0: compressed; stream = 16, index = 9
27/0: compressed; stream = 16, index = 10
28/0: compressed; stream = 16, index = 11
29/0: compressed; stream = 16, index = 12
30/0: compressed; stream = 16, index = 13
31/0: compressed; stream = 16, index = 14
32/0: compressed; stream = 16, index = 15
33/0: compressed; stream = 16, index = 16
34/0: compressed; stream = 16, index = 17
35/0: compressed; stream = 16, index = 18
36/0: compressed; stream = 16, index = 19
37/0: compressed; stream = 16, index = 20
38/0: compressed; stream = 16, index = 21
39/0: compressed; stream = 16, index = 22

View file

@ -0,0 +1,50 @@
1/0: uncompressed; offset = 69708
2/0: uncompressed; offset = 70038
3/0: uncompressed; offset = 15
4/0: uncompressed; offset = 216
5/0: uncompressed; offset = 867
6/0: uncompressed; offset = 990
7/0: uncompressed; offset = 1143
8/0: uncompressed; offset = 1251
9/0: uncompressed; offset = 1834
10/0: uncompressed; offset = 10137
11/0: uncompressed; offset = 18124
12/0: uncompressed; offset = 31939
13/0: uncompressed; offset = 40433
14/0: uncompressed; offset = 48689
15/0: uncompressed; offset = 58231
16/0: uncompressed; offset = 67715
17/0: compressed; stream = 16, index = 0
18/0: compressed; stream = 16, index = 1
19/0: compressed; stream = 16, index = 2
20/0: compressed; stream = 16, index = 3
21/0: compressed; stream = 16, index = 4
22/0: compressed; stream = 16, index = 5
23/0: compressed; stream = 16, index = 6
24/0: compressed; stream = 16, index = 7
25/0: compressed; stream = 16, index = 8
26/0: compressed; stream = 16, index = 9
27/0: compressed; stream = 16, index = 10
28/0: compressed; stream = 16, index = 11
29/0: compressed; stream = 16, index = 12
30/0: compressed; stream = 16, index = 13
31/0: compressed; stream = 16, index = 14
32/0: compressed; stream = 16, index = 15
33/0: compressed; stream = 16, index = 16
34/0: compressed; stream = 16, index = 17
35/0: compressed; stream = 16, index = 18
36/0: compressed; stream = 16, index = 19
37/0: compressed; stream = 16, index = 20
38/0: compressed; stream = 16, index = 21
39/0: compressed; stream = 16, index = 22
40/0: compressed; stream = 16, index = 23
41/0: compressed; stream = 16, index = 24
42/0: compressed; stream = 16, index = 25
43/0: compressed; stream = 16, index = 26
44/0: compressed; stream = 16, index = 27
45/0: compressed; stream = 16, index = 28
46/0: compressed; stream = 16, index = 29
47/0: compressed; stream = 16, index = 30
48/0: compressed; stream = 16, index = 31
49/0: compressed; stream = 16, index = 32
50/0: compressed; stream = 16, index = 33

View file

@ -0,0 +1,30 @@
1/0: uncompressed; offset = 31354
2/0: uncompressed; offset = 31614
3/0: uncompressed; offset = 15
4/0: uncompressed; offset = 216
5/0: uncompressed; offset = 727
6/0: uncompressed; offset = 777
7/0: uncompressed; offset = 909
8/0: uncompressed; offset = 1017
9/0: uncompressed; offset = 19127
10/0: uncompressed; offset = 19313
11/0: uncompressed; offset = 19557
12/0: uncompressed; offset = 19948
13/0: uncompressed; offset = 20677
14/0: uncompressed; offset = 23321
15/0: uncompressed; offset = 30318
16/0: compressed; stream = 15, index = 0
17/0: compressed; stream = 15, index = 1
18/0: compressed; stream = 15, index = 2
19/0: compressed; stream = 15, index = 3
20/0: compressed; stream = 15, index = 4
21/0: compressed; stream = 15, index = 5
22/0: compressed; stream = 15, index = 6
23/0: compressed; stream = 15, index = 7
24/0: compressed; stream = 15, index = 8
25/0: compressed; stream = 15, index = 9
26/0: compressed; stream = 15, index = 10
27/0: compressed; stream = 15, index = 11
28/0: compressed; stream = 15, index = 12
29/0: compressed; stream = 15, index = 13
30/0: compressed; stream = 15, index = 14

View file

@ -0,0 +1,28 @@
1/0: uncompressed; offset = 27064
2/0: uncompressed; offset = 27312
3/0: uncompressed; offset = 15
4/0: uncompressed; offset = 216
5/0: uncompressed; offset = 713
6/0: uncompressed; offset = 763
7/0: uncompressed; offset = 892
8/0: uncompressed; offset = 1007
9/0: uncompressed; offset = 1235
10/0: uncompressed; offset = 4832
11/0: uncompressed; offset = 12199
12/0: uncompressed; offset = 19196
13/0: uncompressed; offset = 26341
14/0: compressed; stream = 13, index = 0
15/0: compressed; stream = 13, index = 1
16/0: compressed; stream = 13, index = 2
17/0: compressed; stream = 13, index = 3
18/0: compressed; stream = 13, index = 4
19/0: compressed; stream = 13, index = 5
20/0: compressed; stream = 13, index = 6
21/0: compressed; stream = 13, index = 7
22/0: compressed; stream = 13, index = 8
23/0: compressed; stream = 13, index = 9
24/0: compressed; stream = 13, index = 10
25/0: compressed; stream = 13, index = 11
26/0: compressed; stream = 13, index = 12
27/0: compressed; stream = 13, index = 13
28/0: compressed; stream = 13, index = 14

View file

@ -0,0 +1,20 @@
1/0: uncompressed; offset = 4964
2/0: uncompressed; offset = 5023
3/0: uncompressed; offset = 5234
4/0: uncompressed; offset = 15
5/0: uncompressed; offset = 734
6/0: uncompressed; offset = 799
7/0: uncompressed; offset = 933
8/0: uncompressed; offset = 1104
9/0: uncompressed; offset = 1947
10/0: uncompressed; offset = 1992
11/0: uncompressed; offset = 2182
12/0: uncompressed; offset = 2427
13/0: uncompressed; offset = 2597
14/0: uncompressed; offset = 2822
15/0: uncompressed; offset = 2989
16/0: uncompressed; offset = 3239
17/0: uncompressed; offset = 3271
18/0: uncompressed; offset = 3328
19/0: uncompressed; offset = 3740
20/0: uncompressed; offset = 4270

View file

@ -0,0 +1,23 @@
1/0: uncompressed; offset = 31058
2/0: uncompressed; offset = 31307
3/0: uncompressed; offset = 15
4/0: uncompressed; offset = 216
5/0: uncompressed; offset = 678
6/0: uncompressed; offset = 728
7/0: uncompressed; offset = 855
8/0: uncompressed; offset = 970
9/0: uncompressed; offset = 1203
10/0: uncompressed; offset = 18852
11/0: uncompressed; offset = 30165
12/0: compressed; stream = 11, index = 0
13/0: compressed; stream = 11, index = 1
14/0: compressed; stream = 11, index = 2
15/0: compressed; stream = 11, index = 3
16/0: compressed; stream = 11, index = 4
17/0: compressed; stream = 11, index = 5
18/0: compressed; stream = 11, index = 6
19/0: compressed; stream = 11, index = 7
20/0: compressed; stream = 11, index = 8
21/0: compressed; stream = 11, index = 9
22/0: compressed; stream = 11, index = 10
23/0: compressed; stream = 11, index = 11

View file

@ -0,0 +1,26 @@
1/0: uncompressed; offset = 6344
2/0: uncompressed; offset = 15
3/0: uncompressed; offset = 216
4/0: uncompressed; offset = 707
5/0: uncompressed; offset = 757
6/0: uncompressed; offset = 887
7/0: uncompressed; offset = 990
8/0: uncompressed; offset = 1257
9/0: uncompressed; offset = 1679
10/0: uncompressed; offset = 2052
11/0: uncompressed; offset = 4249
12/0: uncompressed; offset = 4343
13/0: uncompressed; offset = 5387
14/0: uncompressed; offset = 5481
15/0: uncompressed; offset = 5519
16/0: compressed; stream = 15, index = 0
17/0: compressed; stream = 15, index = 1
18/0: compressed; stream = 15, index = 2
19/0: compressed; stream = 15, index = 3
20/0: compressed; stream = 15, index = 4
21/0: compressed; stream = 15, index = 5
22/0: compressed; stream = 15, index = 6
23/0: compressed; stream = 15, index = 7
24/0: compressed; stream = 15, index = 8
25/0: compressed; stream = 15, index = 9
26/0: compressed; stream = 15, index = 10

View file

@ -0,0 +1,23 @@
1/0: uncompressed; offset = 34767
2/0: uncompressed; offset = 35015
3/0: uncompressed; offset = 15
4/0: uncompressed; offset = 216
5/0: uncompressed; offset = 678
6/0: uncompressed; offset = 728
7/0: uncompressed; offset = 856
8/0: uncompressed; offset = 971
9/0: uncompressed; offset = 1394
10/0: uncompressed; offset = 10990
11/0: uncompressed; offset = 19087
12/0: uncompressed; offset = 33769
13/0: compressed; stream = 12, index = 0
14/0: compressed; stream = 12, index = 1
15/0: compressed; stream = 12, index = 2
16/0: compressed; stream = 12, index = 3
17/0: compressed; stream = 12, index = 4
18/0: compressed; stream = 12, index = 5
19/0: compressed; stream = 12, index = 6
20/0: compressed; stream = 12, index = 7
21/0: compressed; stream = 12, index = 8
22/0: compressed; stream = 12, index = 9
23/0: compressed; stream = 12, index = 10

View file

@ -0,0 +1,22 @@
1/0: uncompressed; offset = 23295
2/0: uncompressed; offset = 23543
3/0: uncompressed; offset = 15
4/0: uncompressed; offset = 216
5/0: uncompressed; offset = 671
6/0: uncompressed; offset = 721
7/0: uncompressed; offset = 847
8/0: uncompressed; offset = 955
9/0: uncompressed; offset = 7385
10/0: uncompressed; offset = 15752
11/0: uncompressed; offset = 22721
12/0: compressed; stream = 11, index = 0
13/0: compressed; stream = 11, index = 1
14/0: compressed; stream = 11, index = 2
15/0: compressed; stream = 11, index = 3
16/0: compressed; stream = 11, index = 4
17/0: compressed; stream = 11, index = 5
18/0: compressed; stream = 11, index = 6
19/0: compressed; stream = 11, index = 7
20/0: compressed; stream = 11, index = 8
21/0: compressed; stream = 11, index = 9
22/0: compressed; stream = 11, index = 10

View file

@ -0,0 +1,20 @@
1/0: uncompressed; offset = 24490
2/0: uncompressed; offset = 24739
3/0: uncompressed; offset = 15
4/0: uncompressed; offset = 216
5/0: uncompressed; offset = 657
6/0: uncompressed; offset = 707
7/0: uncompressed; offset = 833
8/0: uncompressed; offset = 948
9/0: uncompressed; offset = 1290
10/0: uncompressed; offset = 13083
11/0: uncompressed; offset = 23411
12/0: compressed; stream = 11, index = 0
13/0: compressed; stream = 11, index = 1
14/0: compressed; stream = 11, index = 2
15/0: compressed; stream = 11, index = 3
16/0: compressed; stream = 11, index = 4
17/0: compressed; stream = 11, index = 5
18/0: compressed; stream = 11, index = 6
19/0: compressed; stream = 11, index = 7
20/0: compressed; stream = 11, index = 8

View file

@ -0,0 +1,31 @@
1/0: uncompressed; offset = 43550
2/0: uncompressed; offset = 43799
3/0: uncompressed; offset = 15
4/0: uncompressed; offset = 216
5/0: uncompressed; offset = 734
6/0: uncompressed; offset = 784
7/0: uncompressed; offset = 913
8/0: uncompressed; offset = 1021
9/0: uncompressed; offset = 1546
10/0: uncompressed; offset = 5794
11/0: uncompressed; offset = 12915
12/0: uncompressed; offset = 23660
13/0: uncompressed; offset = 30657
14/0: uncompressed; offset = 42604
15/0: compressed; stream = 14, index = 0
16/0: compressed; stream = 14, index = 1
17/0: compressed; stream = 14, index = 2
18/0: compressed; stream = 14, index = 3
19/0: compressed; stream = 14, index = 4
20/0: compressed; stream = 14, index = 5
21/0: compressed; stream = 14, index = 6
22/0: compressed; stream = 14, index = 7
23/0: compressed; stream = 14, index = 8
24/0: compressed; stream = 14, index = 9
25/0: compressed; stream = 14, index = 10
26/0: compressed; stream = 14, index = 11
27/0: compressed; stream = 14, index = 12
28/0: compressed; stream = 14, index = 13
29/0: compressed; stream = 14, index = 14
30/0: compressed; stream = 14, index = 15
31/0: compressed; stream = 14, index = 16

View file

@ -0,0 +1,39 @@
1/0: uncompressed; offset = 75299
2/0: uncompressed; offset = 75548
3/0: uncompressed; offset = 15
4/0: uncompressed; offset = 216
5/0: uncompressed; offset = 790
6/0: uncompressed; offset = 840
7/0: uncompressed; offset = 975
8/0: uncompressed; offset = 1083
9/0: uncompressed; offset = 2128
10/0: uncompressed; offset = 13799
11/0: uncompressed; offset = 23682
12/0: uncompressed; offset = 31867
13/0: uncompressed; offset = 36116
14/0: uncompressed; offset = 50352
15/0: uncompressed; offset = 61569
16/0: uncompressed; offset = 73516
17/0: compressed; stream = 16, index = 0
18/0: compressed; stream = 16, index = 1
19/0: compressed; stream = 16, index = 2
20/0: compressed; stream = 16, index = 3
21/0: compressed; stream = 16, index = 4
22/0: compressed; stream = 16, index = 5
23/0: compressed; stream = 16, index = 6
24/0: compressed; stream = 16, index = 7
25/0: compressed; stream = 16, index = 8
26/0: compressed; stream = 16, index = 9
27/0: compressed; stream = 16, index = 10
28/0: compressed; stream = 16, index = 11
29/0: compressed; stream = 16, index = 12
30/0: compressed; stream = 16, index = 13
31/0: compressed; stream = 16, index = 14
32/0: compressed; stream = 16, index = 15
33/0: compressed; stream = 16, index = 16
34/0: compressed; stream = 16, index = 17
35/0: compressed; stream = 16, index = 18
36/0: compressed; stream = 16, index = 19
37/0: compressed; stream = 16, index = 20
38/0: compressed; stream = 16, index = 21
39/0: compressed; stream = 16, index = 22

View file

@ -0,0 +1,26 @@
1/0: uncompressed; offset = 25097
2/0: uncompressed; offset = 25156
3/0: uncompressed; offset = 25367
4/0: uncompressed; offset = 15
5/0: uncompressed; offset = 854
6/0: uncompressed; offset = 919
7/0: uncompressed; offset = 1074
8/0: uncompressed; offset = 1245
9/0: uncompressed; offset = 18343
10/0: uncompressed; offset = 18388
11/0: uncompressed; offset = 18752
12/0: uncompressed; offset = 19071
13/0: uncompressed; offset = 19360
14/0: uncompressed; offset = 19604
15/0: uncompressed; offset = 19770
16/0: uncompressed; offset = 20007
17/0: uncompressed; offset = 20174
18/0: uncompressed; offset = 20424
19/0: uncompressed; offset = 20456
20/0: uncompressed; offset = 20525
21/0: uncompressed; offset = 23109
22/0: uncompressed; offset = 23500
23/0: uncompressed; offset = 24229
24/0: uncompressed; offset = 24641
25/0: uncompressed; offset = 24741
26/0: uncompressed; offset = 24985

View file

@ -0,0 +1,18 @@
1/0: uncompressed; offset = 3568
2/0: uncompressed; offset = 3777
3/0: uncompressed; offset = 15
4/0: uncompressed; offset = 216
5/0: uncompressed; offset = 643
6/0: uncompressed; offset = 693
7/0: uncompressed; offset = 819
8/0: uncompressed; offset = 934
9/0: uncompressed; offset = 1118
10/0: uncompressed; offset = 1210
11/0: uncompressed; offset = 2555
12/0: uncompressed; offset = 3030
13/0: compressed; stream = 12, index = 0
14/0: compressed; stream = 12, index = 1
15/0: compressed; stream = 12, index = 2
16/0: compressed; stream = 12, index = 3
17/0: compressed; stream = 12, index = 4
18/0: compressed; stream = 12, index = 5

View file

@ -0,0 +1,26 @@
1/0: uncompressed; offset = 16762
2/0: uncompressed; offset = 16877
3/0: uncompressed; offset = 17142
4/0: uncompressed; offset = 24335
5/0: uncompressed; offset = 32164
6/0: uncompressed; offset = 32412
7/0: uncompressed; offset = 15
8/0: uncompressed; offset = 216
9/0: uncompressed; offset = 671
10/0: uncompressed; offset = 721
11/0: uncompressed; offset = 856
12/0: uncompressed; offset = 973
13/0: uncompressed; offset = 1318
14/0: uncompressed; offset = 2218
15/0: compressed; stream = 13, index = 0
16/0: compressed; stream = 13, index = 1
17/0: compressed; stream = 13, index = 2
18/0: compressed; stream = 13, index = 3
19/0: compressed; stream = 13, index = 4
20/0: compressed; stream = 13, index = 5
21/0: compressed; stream = 13, index = 6
22/0: compressed; stream = 13, index = 7
23/0: compressed; stream = 13, index = 8
24/0: compressed; stream = 13, index = 9
25/0: compressed; stream = 13, index = 10
26/0: compressed; stream = 13, index = 11

View file

@ -0,0 +1,22 @@
1/0: uncompressed; offset = 16778
2/0: uncompressed; offset = 16893
3/0: uncompressed; offset = 17109
4/0: uncompressed; offset = 24938
5/0: uncompressed; offset = 25186
6/0: uncompressed; offset = 15
7/0: uncompressed; offset = 216
8/0: uncompressed; offset = 650
9/0: uncompressed; offset = 700
10/0: uncompressed; offset = 836
11/0: uncompressed; offset = 953
12/0: uncompressed; offset = 1298
13/0: uncompressed; offset = 2103
14/0: compressed; stream = 12, index = 0
15/0: compressed; stream = 12, index = 1
16/0: compressed; stream = 12, index = 2
17/0: compressed; stream = 12, index = 3
18/0: compressed; stream = 12, index = 4
19/0: compressed; stream = 12, index = 5
20/0: compressed; stream = 12, index = 6
21/0: compressed; stream = 12, index = 7
22/0: compressed; stream = 12, index = 8

View file

@ -0,0 +1,19 @@
1/0: uncompressed; offset = 20679
2/0: uncompressed; offset = 20927
3/0: uncompressed; offset = 15
4/0: uncompressed; offset = 216
5/0: uncompressed; offset = 650
6/0: uncompressed; offset = 700
7/0: uncompressed; offset = 826
8/0: uncompressed; offset = 934
9/0: uncompressed; offset = 1252
10/0: uncompressed; offset = 8248
11/0: uncompressed; offset = 20115
12/0: compressed; stream = 11, index = 0
13/0: compressed; stream = 11, index = 1
14/0: compressed; stream = 11, index = 2
15/0: compressed; stream = 11, index = 3
16/0: compressed; stream = 11, index = 4
17/0: compressed; stream = 11, index = 5
18/0: compressed; stream = 11, index = 6
19/0: compressed; stream = 11, index = 7

View file

@ -0,0 +1,32 @@
1/0: uncompressed; offset = 29506
2/0: uncompressed; offset = 29621
3/0: uncompressed; offset = 29918
4/0: uncompressed; offset = 30033
5/0: uncompressed; offset = 30274
6/0: uncompressed; offset = 30389
7/0: uncompressed; offset = 30644
8/0: uncompressed; offset = 42802
9/0: uncompressed; offset = 43050
10/0: uncompressed; offset = 15
11/0: uncompressed; offset = 216
12/0: uncompressed; offset = 695
13/0: uncompressed; offset = 746
14/0: uncompressed; offset = 900
15/0: uncompressed; offset = 1017
16/0: uncompressed; offset = 1286
17/0: uncompressed; offset = 2443
18/0: uncompressed; offset = 13147
19/0: compressed; stream = 16, index = 0
20/0: compressed; stream = 16, index = 1
21/0: compressed; stream = 16, index = 2
22/0: compressed; stream = 16, index = 3
23/0: compressed; stream = 16, index = 4
24/0: compressed; stream = 16, index = 5
25/0: compressed; stream = 16, index = 6
26/0: compressed; stream = 16, index = 7
27/0: compressed; stream = 16, index = 8
28/0: compressed; stream = 16, index = 9
29/0: compressed; stream = 16, index = 10
30/0: compressed; stream = 16, index = 11
31/0: compressed; stream = 16, index = 12
32/0: compressed; stream = 16, index = 13

View file

@ -0,0 +1,23 @@
1/0: uncompressed; offset = 34102
2/0: uncompressed; offset = 34350
3/0: uncompressed; offset = 15
4/0: uncompressed; offset = 216
5/0: uncompressed; offset = 678
6/0: uncompressed; offset = 728
7/0: uncompressed; offset = 856
8/0: uncompressed; offset = 971
9/0: uncompressed; offset = 1514
10/0: uncompressed; offset = 10973
11/0: uncompressed; offset = 19139
12/0: uncompressed; offset = 33047
13/0: compressed; stream = 12, index = 0
14/0: compressed; stream = 12, index = 1
15/0: compressed; stream = 12, index = 2
16/0: compressed; stream = 12, index = 3
17/0: compressed; stream = 12, index = 4
18/0: compressed; stream = 12, index = 5
19/0: compressed; stream = 12, index = 6
20/0: compressed; stream = 12, index = 7
21/0: compressed; stream = 12, index = 8
22/0: compressed; stream = 12, index = 9
23/0: compressed; stream = 12, index = 10

View file

@ -0,0 +1,22 @@
1/0: uncompressed; offset = 9449
2/0: uncompressed; offset = 9564
3/0: uncompressed; offset = 9730
4/0: uncompressed; offset = 17293
5/0: uncompressed; offset = 17541
6/0: uncompressed; offset = 15
7/0: uncompressed; offset = 216
8/0: uncompressed; offset = 650
9/0: uncompressed; offset = 700
10/0: uncompressed; offset = 835
11/0: uncompressed; offset = 952
12/0: uncompressed; offset = 1097
13/0: uncompressed; offset = 1758
14/0: compressed; stream = 12, index = 0
15/0: compressed; stream = 12, index = 1
16/0: compressed; stream = 12, index = 2
17/0: compressed; stream = 12, index = 3
18/0: compressed; stream = 12, index = 4
19/0: compressed; stream = 12, index = 5
20/0: compressed; stream = 12, index = 6
21/0: compressed; stream = 12, index = 7
22/0: compressed; stream = 12, index = 8

View file

@ -0,0 +1,23 @@
1/0: uncompressed; offset = 37282
2/0: uncompressed; offset = 37530
3/0: uncompressed; offset = 15
4/0: uncompressed; offset = 216
5/0: uncompressed; offset = 678
6/0: uncompressed; offset = 728
7/0: uncompressed; offset = 856
8/0: uncompressed; offset = 971
9/0: uncompressed; offset = 1322
10/0: uncompressed; offset = 9581
11/0: uncompressed; offset = 24286
12/0: uncompressed; offset = 36258
13/0: compressed; stream = 12, index = 0
14/0: compressed; stream = 12, index = 1
15/0: compressed; stream = 12, index = 2
16/0: compressed; stream = 12, index = 3
17/0: compressed; stream = 12, index = 4
18/0: compressed; stream = 12, index = 5
19/0: compressed; stream = 12, index = 6
20/0: compressed; stream = 12, index = 7
21/0: compressed; stream = 12, index = 8
22/0: compressed; stream = 12, index = 9
23/0: compressed; stream = 12, index = 10

View file

@ -0,0 +1,26 @@
1/0: uncompressed; offset = 48194
2/0: uncompressed; offset = 48442
3/0: uncompressed; offset = 15
4/0: uncompressed; offset = 216
5/0: uncompressed; offset = 699
6/0: uncompressed; offset = 749
7/0: uncompressed; offset = 878
8/0: uncompressed; offset = 1000
9/0: uncompressed; offset = 8546
10/0: uncompressed; offset = 9072
11/0: uncompressed; offset = 10659
12/0: uncompressed; offset = 18919
13/0: uncompressed; offset = 35129
14/0: uncompressed; offset = 47101
15/0: compressed; stream = 14, index = 0
16/0: compressed; stream = 14, index = 1
17/0: compressed; stream = 14, index = 2
18/0: compressed; stream = 14, index = 3
19/0: compressed; stream = 14, index = 4
20/0: compressed; stream = 14, index = 5
21/0: compressed; stream = 14, index = 6
22/0: compressed; stream = 14, index = 7
23/0: compressed; stream = 14, index = 8
24/0: compressed; stream = 14, index = 9
25/0: compressed; stream = 14, index = 10
26/0: compressed; stream = 14, index = 11

View file

@ -0,0 +1,35 @@
1/0: uncompressed; offset = 2924
2/0: uncompressed; offset = 3039
3/0: uncompressed; offset = 4606
4/0: uncompressed; offset = 4721
5/0: uncompressed; offset = 7754
6/0: uncompressed; offset = 7870
7/0: uncompressed; offset = 11668
8/0: uncompressed; offset = 21077
9/0: uncompressed; offset = 28498
10/0: uncompressed; offset = 35464
11/0: uncompressed; offset = 35699
12/0: uncompressed; offset = 15
13/0: uncompressed; offset = 216
14/0: uncompressed; offset = 703
15/0: uncompressed; offset = 754
16/0: uncompressed; offset = 909
17/0: uncompressed; offset = 1026
18/0: uncompressed; offset = 2161
19/0: compressed; stream = 18, index = 0
20/0: compressed; stream = 18, index = 1
21/0: compressed; stream = 18, index = 2
22/0: compressed; stream = 18, index = 3
23/0: compressed; stream = 18, index = 4
24/0: compressed; stream = 18, index = 5
25/0: compressed; stream = 18, index = 6
26/0: compressed; stream = 18, index = 7
27/0: compressed; stream = 18, index = 8
28/0: compressed; stream = 18, index = 9
29/0: compressed; stream = 18, index = 10
30/0: compressed; stream = 18, index = 11
31/0: compressed; stream = 18, index = 12
32/0: compressed; stream = 18, index = 13
33/0: compressed; stream = 18, index = 14
34/0: compressed; stream = 18, index = 15
35/0: compressed; stream = 18, index = 16

View file

@ -0,0 +1,19 @@
1/0: uncompressed; offset = 8578
2/0: uncompressed; offset = 15
3/0: uncompressed; offset = 216
4/0: uncompressed; offset = 658
5/0: uncompressed; offset = 708
6/0: uncompressed; offset = 837
7/0: uncompressed; offset = 940
8/0: uncompressed; offset = 1191
9/0: uncompressed; offset = 1627
10/0: uncompressed; offset = 7676
11/0: uncompressed; offset = 7784
12/0: uncompressed; offset = 7822
13/0: compressed; stream = 12, index = 0
14/0: compressed; stream = 12, index = 1
15/0: compressed; stream = 12, index = 2
16/0: compressed; stream = 12, index = 3
17/0: compressed; stream = 12, index = 4
18/0: compressed; stream = 12, index = 5
19/0: compressed; stream = 12, index = 6

View file

@ -0,0 +1,15 @@
1/0: uncompressed; offset = 11784
2/0: uncompressed; offset = 12033
3/0: uncompressed; offset = 15
4/0: uncompressed; offset = 216
5/0: uncompressed; offset = 621
6/0: uncompressed; offset = 671
7/0: uncompressed; offset = 791
8/0: uncompressed; offset = 906
9/0: uncompressed; offset = 1070
10/0: uncompressed; offset = 11235
11/0: compressed; stream = 10, index = 0
12/0: compressed; stream = 10, index = 1
13/0: compressed; stream = 10, index = 2
14/0: compressed; stream = 10, index = 3
15/0: compressed; stream = 10, index = 4

View file

@ -5,7 +5,7 @@ const { expect } = require('chai')
const MODULE_PATH = '../../../app/js/ContentCacheManager' const MODULE_PATH = '../../../app/js/ContentCacheManager'
describe('ContentCacheManager', function () { describe('ContentCacheManager', function () {
let contentDir, pdfPath let contentDir, pdfPath, xrefPath
let ContentCacheManager, files, Settings let ContentCacheManager, files, Settings
before(function () { before(function () {
Settings = require('@overleaf/settings') Settings = require('@overleaf/settings')
@ -38,6 +38,8 @@ describe('ContentCacheManager', function () {
'/overleaf/services/clsi/output/602cee6f6460fca0ba7921e6/content/1797a7f48f9-5abc1998509dea1f' '/overleaf/services/clsi/output/602cee6f6460fca0ba7921e6/content/1797a7f48f9-5abc1998509dea1f'
pdfPath = pdfPath =
'/overleaf/services/clsi/output/602cee6f6460fca0ba7921e6/generated-files/1797a7f48ea-8ac6805139f43351/output.pdf' '/overleaf/services/clsi/output/602cee6f6460fca0ba7921e6/generated-files/1797a7f48ea-8ac6805139f43351/output.pdf'
xrefPath =
'/overleaf/services/clsi/output/602cee6f6460fca0ba7921e6/generated-files/1797a7f48ea-8ac6805139f43351/output.pdfxref'
reclaimed = 0 reclaimed = 0
Settings.pdfCachingMinChunkSize = 1024 Settings.pdfCachingMinChunkSize = 1024
@ -50,7 +52,8 @@ describe('ContentCacheManager', function () {
}) })
describe('minimal', function () { describe('minimal', function () {
const PATH_MINIMAL = 'test/acceptance/fixtures/minimal.pdf' const PATH_MINIMAL_PDF = 'test/acceptance/fixtures/minimal.pdf'
const PATH_MINIMAL_XREF = 'test/acceptance/fixtures/minimal.pdfxref'
const OBJECT_ID_1 = '9 0 ' const OBJECT_ID_1 = '9 0 '
const HASH_LARGE = const HASH_LARGE =
'd7cfc73ad2fba4578a437517923e3714927bbf35e63ea88bd93c7a8076cf1fcd' 'd7cfc73ad2fba4578a437517923e3714927bbf35e63ea88bd93c7a8076cf1fcd'
@ -62,9 +65,10 @@ describe('ContentCacheManager', function () {
} }
let MINIMAL_SIZE, RANGE_1, RANGE_2, h1, h2, START_1, START_2, END_1, END_2 let MINIMAL_SIZE, RANGE_1, RANGE_2, h1, h2, START_1, START_2, END_1, END_2
before(async function () { before(async function () {
await fs.promises.copyFile(PATH_MINIMAL, pdfPath) await fs.promises.copyFile(PATH_MINIMAL_PDF, pdfPath)
const MINIMAL = await fs.promises.readFile(PATH_MINIMAL) await fs.promises.copyFile(PATH_MINIMAL_XREF, xrefPath)
MINIMAL_SIZE = (await fs.promises.stat(PATH_MINIMAL)).size const MINIMAL = await fs.promises.readFile(PATH_MINIMAL_PDF)
MINIMAL_SIZE = (await fs.promises.stat(PATH_MINIMAL_PDF)).size
RANGE_1 = await fs.promises.readFile(getChunkPath(HASH_LARGE)) RANGE_1 = await fs.promises.readFile(getChunkPath(HASH_LARGE))
RANGE_2 = await fs.promises.readFile(getChunkPath(HASH_SMALL)) RANGE_2 = await fs.promises.readFile(getChunkPath(HASH_SMALL))
h1 = HASH_LARGE h1 = HASH_LARGE

View file

@ -1,7 +1,8 @@
const fs = require('fs') const fs = require('fs')
const Path = require('path') const Path = require('path')
const { expect } = require('chai') const { expect } = require('chai')
const { parseXrefTable } = require('../../../app/lib/pdfjs/parseXrefTable') const { parseXrefTable } = require('../../../app/js/XrefParser')
const { NoXrefTableError } = require('../../../app/js/Errors')
const PATH_EXAMPLES = 'test/acceptance/fixtures/examples/' const PATH_EXAMPLES = 'test/acceptance/fixtures/examples/'
const PATH_SNAPSHOTS = 'test/unit/js/snapshots/pdfjs/' const PATH_SNAPSHOTS = 'test/unit/js/snapshots/pdfjs/'
const EXAMPLES = fs.readdirSync(PATH_EXAMPLES) const EXAMPLES = fs.readdirSync(PATH_EXAMPLES)
@ -48,24 +49,11 @@ describe('pdfjs', function () {
describe('when the pdf is an empty file', function () { describe('when the pdf is an empty file', function () {
it('should yield no entries', async function () { it('should yield no entries', async function () {
const path = 'does/not/matter.pdf' const path = 'does/not/matter.pdf'
const table = await parseXrefTable(path, 0)
expect(table).to.deep.equal([])
})
})
describe('when the operation times out', function () {
it('should bail out', async function () {
const path = pdfPath(EXAMPLES[0])
const { size } = await loadContext(EXAMPLES[0])
const err = new Error()
let table let table
try { try {
table = await parseXrefTable(path, size, () => { table = await parseXrefTable(path, 0)
throw err
})
} catch (e) { } catch (e) {
expect(e).to.equal(err) expect(e).to.be.an.instanceof(NoXrefTableError)
return
} }
expect(table).to.not.exist expect(table).to.not.exist
}) })
@ -89,7 +77,16 @@ describe('pdfjs', function () {
it('should produce the expected xRef table', async function () { it('should produce the expected xRef table', async function () {
const table = await parseXrefTable(pdfPath(example), size, () => {}) const table = await parseXrefTable(pdfPath(example), size, () => {})
expect(table).to.deep.equal(snapshot) // compare the essential parts of the xref table only
expect(table.xRefEntries[0]).to.include({ offset: 0 })
expect(table.xRefEntries.slice(1)).to.deep.equal(
snapshot.xRefEntries
.slice(1)
.filter(xref => xref.uncompressed) // we only use the uncompressed fields
.map(xref => {
return { offset: xref.offset, uncompressed: xref.uncompressed } // ignore unused gen field
})
)
}) })
}) })
} }