[Bug]: One stray ")" in a page dictionary truncates numPages and makes all later pages unreachable.
Los mantenedores suelen responder en 1 día
Nadie ha tomado este issue todavía.
Evaluación
- Dificultad
- 3/5
- Tiempo estimado
- 1-2 días
- Aptitud para principiantes
- 65/100
- Tipo de issue
- Error
- Claridad
- Bien especificado
- Estado de actividad
- Activo
- Stack tecnológico
- javascript
- Área
- backend-api-design
Línea de trabajo
The issue is in the lexer's handling of a stray ')', which triggers a FormatError in Lexer.getObj. The recovery path in Catalog.getAllPageDicts breaks early, truncating the page count. Start by examining src/core/lexer.js around case 0x29, then trace through src/core/catalog.js's getAllPageDicts method. The fix likely involves adjusting the error handling to not break the iteration on this specific error, or to treat it as a recoverable condition. Run the provided test PDF through the test suite to verify the fix restores numPages to 57.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
Attach (recommended) or Link to PDF file
repro-57-pages-stray-paren.pdf
Web browser and its version
Chrome 153.0.0 (also reproduces in Node 23.11.1, so not browser-specific)
Operating system and its version
macOS 27.0 (26A428)
PDF.js version
5.4.296
Is the bug present in the latest PDF.js version?
Yes
Is a browser extension
No
Steps to reproduce the problem
The attached PDF declares 57 pages (/Count 57 in the root /Pages). Every page is well formed except page 40, whose dictionary contains a single stray ) before its >>.
- Open the attached file in the viewer.
- Read the page counter: it shows 40, not 57.
- Enter 41 in the page number box. The page cannot be reached.
Same result through the API, no viewer involved:
import { readFileSync } from "node:fs";
import { getDocument } from "pdfjs-dist/legacy/build/pdf.mjs";
const pdf = await getDocument({
data: new Uint8Array(readFileSync("repro-57-pages-stray-paren.pdf")),
}).promise;
console.log(pdf.numPages); // 40, the file declares 57
await pdf.getPage(39); // ok
await pdf.getPage(40); // UnknownErrorException: Illegal character: 41
await pdf.getPage(41); // Error: Invalid page request.
What is the expected behavior?
numPages should be 57.
A page whose dictionary cannot be parsed may reasonably reject on its own getPage() call, but it should not remove the 17 well-formed pages that follow it from the document.
Two other implementations read all 57 pages from this file:
- macOS PDFKit / Preview:
mdls -name kMDItemNumberOfPagesreturns 57 - Chrome's built-in viewer (PDFium): opens and renders all 57
What went wrong?
numPages is 40. Pages 1-39 load. Page 40 rejects with:
UnknownErrorException: Illegal character: 41
details: "FormatError: Illegal character: 41"
Pages 41 to 57 then reject with Error: Invalid page request. and stay unreachable for the lifetime of the document. 18 of 57 pages are lost.
Nothing in the public API signals this. getDocument() resolves successfully and numPages simply returns a smaller number, so a caller cannot tell a 40 page document apart from a 57 page document that silently lost 17 pages.
Link to a viewer
No response
Additional context
Where it appears to come from, reading the bundled worker (the code below is identical in 5.4.296 and 6.3.289):
Lexer.getObjhascase 0x29:which throwsFormatError("Illegal character: " + ch). 0x29 is). The neighbouring cases for>,{and}return aCmdinstead of throwing, so)is the only delimiter in that switch that is fatal.PDFDocument.checkLastPagecatches it and enters the recovery pathCatalog.getAllPageDicts(recoveryMode).getAllPageDictswalks the page tree andbreaks at the first kid that throws, then takes the page count from the partial map. That is where 57 becomes 40.
recoveryMode never engages here: every gate that enables it tests reason instanceof XRefEntryException, and a FormatError raised by the lexer is not one, so the truncated count stands.
Why it matters in practice: we hit this on a real 57 page document produced by a third-party authoring tool. Users were shown a 40 page document with no error of any kind, and had no way to know 17 pages were missing. The same file opens completely in Chrome and in Preview, so it appeared incomplete only where pdf.js was used.
The attached file is a minimal synthetic stand-in, 19 KB, with no third-party content. It produces the same result as the real document on 6.3.289 and on 5.4.296:
| the real document | attached file | |
|---|---|---|
| declared /Count | 57 | 57 |
| pdf.js numPages | 40 | 40 |
| pages that load | 1-39 | 1-39 |
| page 40 | Illegal character: 41 | Illegal character: 41 |
| pages 41-57 | Invalid page request. | Invalid page request. |
- Lenguaje dominante
- JavaScript
- Estrellas
- 53.9k
- Forks
- 10.7k
- Merge medio
- 23 h 33 min
- PR fusionados (30 d)
- 122
Preparar el entorno
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de mozilla/pdf.js
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 84/100
mozilla/pdf.js#22023 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
other
Dificultad 1/5 Menos de una hora Aptitud para principiantes 85/100
mozilla/pdf.js#21645 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
chrome-specific safari-specific upstream
Dificultad 2/5 1-3 horas Aptitud para principiantes 65/100
Los mantenedores suelen responder en 1 día
-
addon chrome-specific printing
Dificultad 1/5 Menos de una hora Aptitud para principiantes 70/100
mozilla/pdf.js#12278 · 3 comentarios ·
Los mantenedores suelen responder en 1 día
-
Dificultad 3/5 1-2 días Aptitud para principiantes 76/100
Los mantenedores suelen responder en 1 día
Todos los issues de mozilla/pdf.js
Issues similares
-
[Bug] @deck.gl/arcgis dist import resolves to unpublished @deck.gl/core source path (9.3.11, 9.4.0)Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 76/100
stac-utils/stac-fields#42 ·
-
[sec-check] SVG remote-reference gate misses src attributes, link hrefs and bare-string @importAbiertoagent/security hive/hosted-available-lke648397-260827-5n31 security
Dificultad 2/5 1-3 horas Aptitud para principiantes 88/100
Los mantenedores suelen responder en 1 día
-
Fix #3381 does not fix #3343: watch() returns the ref, not the value, so setup() never re-runsAbierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 86/100
Los mantenedores suelen responder en 1 día
-
Add: Eurosport 1 PolskaAbiertochannels:add check:passed
Dificultad 1/5 Menos de una hora Aptitud para principiantes 75/100
Los mantenedores suelen responder en 2 días