Text is lost when a `TJ` array is split across two content streams of a page
I maintainer di solito rispondono entro 5 giorni
Valutazione
- Difficoltà
- 4/5
- Tempo stimato
- 3-5 giorni
- Idoneità per principianti
- 25/100
Direzione di ricerca
Start with pdf_decoder< PAGE >::decode_contents in src/parse/pdf_decoders/page.h, identified as the source of the issue, and trace how it tokenizes each stream in a page’s /Contents array. Reproduce with the provided Python example, especially the split after [. Done when text in a TJ array spanning two streams is preserved; an open linked pull request (#451) is already addressing the issue.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Summary
When the /Contents of a page is an array of streams, one stream can end inside a token: for example after the [ of a TJ array, with the strings and ] TJ at the start of the next stream. ISO 32000-1 (7.8.2) allows this, because the streams are one content stream that is divided at token boundaries. docling-parse loses the text of such a TJ. pypdfium2 keeps it.
A split between two operators, or between the array and TJ, is read correctly.
The cause is in pdf_decoder<PAGE>::decode_contents (src/parse/pdf_decoders/page.h): it tokenizes each stream of the array on its own, so an array that is open at the end of a stream is not completed by the next stream.
I can open a PR.
Reproduction
Self-contained, docling-parse 7.24.2 (Python 3.12, macOS arm64):
import pypdfium2
from docling_parse.pdf_parser import ContentConfig, ContentLevel, DoclingPdfParser
def make_pdf(path, first, second):
# One page whose /Contents is an array of two streams.
objs = [b"<< /Type /Catalog /Pages 2 0 R >>", b"<< /Type /Pages /Kids [3 0 R] /Count 1 >>",
b"<< /Type /Page /Parent 2 0 R /MediaBox [0 0 595 842] /Contents [4 0 R 5 0 R] "
b"/Resources << /Font << /F1 6 0 R >> >> >>",
b"<< /Length %d >>\nstream\n" % len(first) + first + b"\nendstream",
b"<< /Length %d >>\nstream\n" % len(second) + second + b"\nendstream",
b"<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>"]
out, offsets = bytearray(b"%PDF-1.5\n"), []
for i, o in enumerate(objs, 1):
offsets.append(len(out))
out += b"%d 0 obj\n" % i + o + b"\nendobj\n"
xref = len(out)
out += b"xref\n0 %d\n0000000000 65535 f \n" % (len(objs) + 1)
for o in offsets:
out += b"%010d 00000 n \n" % o
out += b"trailer\n<< /Size %d /Root 1 0 R >>\nstartxref\n%d\n%%%%EOF\n" % (len(objs) + 1, xref)
open(path, "wb").write(out)
head = b"BT /F1 12 Tf 72 720 Td [(Line one)] TJ 0 -20 Td "
tail = b" 0 -20 Td [(Line three)] TJ ET"
for name, first, second in [
('split after "["', head + b"[", b"(Line two)] TJ" + tail),
('split before "TJ"', head + b"[(Line two)]", b"TJ" + tail),
("split between operators", head + b"[(Line two)] TJ", tail),
]:
make_pdf("/tmp/split.pdf", first, second)
page = DoclingPdfParser(loglevel="fatal").load("/tmp/split.pdf").get_page(
1, content_config=ContentConfig(line_cells_content_level=ContentLevel.COMPUTE_AND_MATERIALIZE)
)
pdfium = pypdfium2.PdfDocument("/tmp/split.pdf")[0].get_textpage().get_text_range().splitlines()
print(f"{name:24} docling-parse: {[c.text for c in page.textline_cells]} pypdfium2: {pdfium}")
Output:
split after "[" docling-parse: ['Line one', 'Line three'] pypdfium2: ['Line one', 'Line two', 'Line three']
split before "TJ" docling-parse: ['Line one', 'Line two', 'Line three'] pypdfium2: ['Line one', 'Line two', 'Line three']
split between operators docling-parse: ['Line one', 'Line two', 'Line three'] pypdfium2: ['Line one', 'Line two', 'Line three']
Where we see it
In the regression dataset, 11169157880668894066-1.pdf, page 1: content stream 1 ends with [, and the word fever(A:1), is lost.
In decisions of the Supreme Court of Norway, published as PDF on domstol.no. In each, a content stream of page 1 ends with [, and the lost text is a judge's name or a part of the case line:
| Document | Text lost on page 1 |
|---|---|
| HR-2019-1786-A | dommer Wilhelm Matheson |
| HR-2026-1454-S | dommer Bergljot Webster |
| HR-2026-780-A | Anke over Borgarting lagmannsretts dom 11. april 2025 |
| HR-2023-753-U | Natt til søndag 6. februar 2022 ca. kl. 02.30 utenfo (the line starts with r Bunnpris Torggaten i) |
| HR-2025-1883-U | U, (sak nr. 25 (the line is HR-2025-1883- and -137197STR-HRET), straffesak, anke over beslutning:) |
These five PDFs are made by Microsoft Word 2016, and page 1 of each has 8 content streams.
- Lingua principale
- C++
- Stelle
- 346
- Fork
- 92
- Merge medio
- 4g 3h
- PR unite (30g)
- 26
Preparare l'ambiente
- Nessun Dockerfile né file Docker Compose
- Ha un modello di pull request
- Leggi la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di docling-project/docling-parse
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 85/100
docling-project/docling-parse#448 ·
I maintainer di solito rispondono entro 5 giorni
-
Difficoltà 4/5 3-5 giorni Idoneità per principianti 58/100
docling-project/docling-parse#449 ·
I maintainer di solito rispondono entro 5 giorni
-
Difficoltà 3/5 1-2 giorni Idoneità per principianti 58/100
docling-project/docling-parse#447 ·
I maintainer di solito rispondono entro 5 giorni
-
Page geometry reports the crop box as the trim, bleed and art boxes (regression in 6.0.0)Forse già presa @RaphaelFakhri l’ha presa 4 giorni fa. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 35/100
docling-project/docling-parse#445 ·
I maintainer di solito rispondono entro 5 giorni
-
SIGTRAP rendering a page with an MMR-coded JBIG2 image (CJBig2_GRDProc::StartDecodeMMR → FaxModule::FaxG4Decode)Forse già presa @wittjeff l’ha presa 5 giorni fa. Aperta
Difficoltà 4/5 3-5 giorni Idoneità per principianti 25/100
docling-project/docling-parse#440 · 1 commento ·
I maintainer di solito rispondono entro 5 giorni
Tutte le issue di docling-project/docling-parse
Issue simili
-
status:needs-triage
Difficoltà 2/5 1-3 ore Idoneità per principianti 74/100
PX4/PX4-Autopilot#29698 ·
I maintainer di solito rispondono entro 1 giorno
-
go perft exits when EvalFile is unavailableForse già presa @Muawiya-contact l’ha presa oggi. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
official-stockfish/Stockfish#7292 · 2 commenti ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 77/100
arvidn/libtorrent#9149 ·
I maintainer di solito rispondono entro 1 giorno
-
Upstream report: MSVC rejects a designated initializer in a dependent template argument (C2131)Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 75/100
rhalbersma/tabula#72 ·
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 76/100
esp8266/Arduino#9342 · 2 commenti ·
I maintainer di solito rispondono entro 1 giorno