MCPcopy Create free account
hub / github.com/LibPDF-js/core / parseXRefChain

Method parseXRefChain

src/parser/document-parser.ts:332–421  ·  view source on GitHub ↗

* Parse the XRef chain, following /Prev links for incremental updates. * * Handles hybrid-reference files (PDF 1.7 §7.5.8.4): when a traditional * xref table's trailer carries a /XRefStm entry, it points to a * supplementary xref stream containing entries for compressed objects * (whi

(
    xrefParser: XRefParser,
    startOffset: number,
  )

Source from the content-addressed store, hash-verified

330 * walks every revision.
331 */
332 private parseXRefChain(
333 xrefParser: XRefParser,
334 startOffset: number,
335 ): { xref: Map<number, XRefEntry>; trailer: PdfDict } {
336 const combinedXRef = new Map<number, XRefEntry>();
337 let firstTrailer: PdfDict | null = null;
338
339 // Track visited offsets to prevent infinite loops
340 const visited = new Set<number>();
341 const queue: number[] = [startOffset];
342
343 while (queue.length > 0) {
344 const offset = queue.shift();
345
346 if (offset === undefined) {
347 break;
348 }
349
350 // Circular reference check
351 if (visited.has(offset)) {
352 this.warnings.push(`Circular xref reference at offset ${offset}`);
353 continue;
354 }
355
356 visited.add(offset);
357
358 try {
359 const xrefData = xrefParser.parseAt(offset);
360
361 // Hybrid xref: if this revision has a /XRefStm entry, it points to a
362 // supplementary xref stream whose entries override the table for the
363 // object numbers it covers. Apply the stream BEFORE the table so its
364 // entries win the "first-seen" merge below.
365 const xrefStmOffset = xrefData.trailer.getNumber("XRefStm")?.value;
366
367 if (xrefStmOffset !== undefined && !visited.has(xrefStmOffset)) {
368 visited.add(xrefStmOffset);
369
370 try {
371 const stmData = xrefParser.parseAt(xrefStmOffset);
372
373 for (const [objNum, entry] of stmData.entries) {
374 if (!combinedXRef.has(objNum)) {
375 combinedXRef.set(objNum, entry);
376 }
377 }
378 } catch (error) {
379 // Hybrid stream is optional from the legacy table's perspective;
380 // if it's malformed, fall back to the table alone with a warning.
381 const message = error instanceof Error ? error.message : String(error);
382
383 this.warnings.push(`Error parsing /XRefStm at ${xrefStmOffset}: ${message}`);
384 }
385 }
386
387 // Merge table entries (first definition wins — covers anything the
388 // stream above didn't override, and any object not in the stream).
389 for (const [objNum, entry] of xrefData.entries) {

Callers 1

parseNormalMethod · 0.95

Calls 7

shiftMethod · 0.80
pushMethod · 0.80
parseAtMethod · 0.80
hasMethod · 0.45
addMethod · 0.45
getNumberMethod · 0.45
setMethod · 0.45

Tested by

no test coverage detected