Dash pattern operator `d` appends to dash_array instead of replacing it: memory grows quadratically on CAD drawings
I maintainer di solito rispondono entro 5 giorni
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 2/5
- Tempo stimato
- 1-3 ore
- Idoneità per principianti
- 85/100
Direzione di ricerca
Leggi src/parse/pdf_states/grph.h intorno alla riga 626, dove l’operatore d riempie dash_array. Conferma la semantica del motivo tratteggiato PDF descritta nell’issue, incluso il modo in cui un array vuoto reimposta il motivo, quindi riproduci il problema con lo script fornito. Il risultato è raggiunto quando ogni operatore d sostituisce il motivo memorizzato e la riproduzione non mostra più una crescita quadratica della memoria.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Summary
pdf_state<GRPH>::d appends the operands of every d operator to the existing dash_array instead of replacing it. Since every shape keeps a copy of the current dash array, memory grows with shapes × number of d operators since the last Q. CAD exports (seen from Creo Elements/Direct Drafting, SOLIDWORKS PDF Publisher, Ghostscript and 7-PDF) set a dash pattern before nearly every path and never use q/Q. A single A3 drawing with ~167k paths and ~4.4k d operators takes the process past 40 GB within about 12 s, even with OCR and table structure disabled. With pdf_backend: pypdfium2 the same file needs 2.7 GB.
Where
src/parse/pdf_states/grph.h (main at 1b7a5fe5e4, line 626):
if(arr.isArray())
{
for(int l=0; l<arr.getArrayNItems(); l++)
{
...
dash_array.push_back(val); // never cleared before the loop
}
}
Per ISO 32000-1 §8.4.3.6, d sets the dash pattern. As a consequence of the append, [] 0 d doesn't reset to a solid line either: the loop runs zero times and the old pattern stays. Only Q restores it.
Reproduction
Self-contained script that writes a one-page PDF with n short lines and parses it:
import resource, sys, time
from docling_parse.pdf_parser import DoclingPdfParser
def make_pdf(path, n, wrap_in_q=False, dash_once=False):
lines = []
for i in range(n):
dash = b"" if (dash_once and i) else b"[18 2 3 2] 0 d\n"
line = dash + b"%d %d m %d %d l S\n" % (i % 1000, i // 10 % 800, i % 1000 + 1, i // 10 % 800)
lines.append(b"q\n" + line + b"Q\n" if wrap_in_q else line)
content = b"".join(lines)
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 1190 842] /Contents 4 0 R >>",
b"<< /Length %d >>\nstream\n" % len(content) + content + b"\nendstream"]
out, offsets = bytearray(b"%PDF-1.4\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)
variant, n = sys.argv[1], int(sys.argv[2]) # variant: each | q | once
make_pdf("/tmp/t.pdf", n, wrap_in_q=variant == "q", dash_once=variant == "once")
t0 = time.time()
page = DoclingPdfParser().load("/tmp/t.pdf").get_page(1)
print(f"{variant:6} n={n:5} shapes={len(page.shapes)} {time.time() - t0:.1f}s "
f"peak RSS {resource.getrusage(resource.RUSAGE_SELF).ru_maxrss / 2**20:.2f} GiB")
Output with docling-parse 7.24.2 (Python 3.12, Linux x86_64):
each n= 4000 shapes=4000 1.0s peak RSS 2.34 GiB
each n= 8000 shapes=8000 3.7s peak RSS 8.88 GiB
q n= 8000 shapes=8000 0.1s peak RSS 0.20 GiB
With 7.22.3 the numbers are the same, plus once n=8000, which sets the pattern once before all lines: 0.19 GiB. Doubling the lines quadruples the memory (each). With the same lines wrapped in q … Q, or with a single d, it stays at 0.2 GiB.
Suggested fix
Clear the array before filling it, so that d replaces the pattern and [] 0 d resets it:
if(arr.isArray())
{
dash_array.clear();
for(int l=0; l<arr.getArrayNItems(); l++)
Impact in practice
We run docling-serve 1.37.0 as a shared service. Eight real one-page drawings (A4 to A0, under 17 MB each) each took the worker into the container memory limit. Every such crash also killed the conversion running in parallel. As a workaround, we now compute the accumulated dash length per page from the pdf.js operator list on the client side and skip Docling above a threshold.
- 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
-
Text is lost when a `TJ` array is split across two content streams of a pageForse già presa @morten-lagabote l’ha presa 2 giorni fa. Aperta
Difficoltà 4/5 3-5 giorni Idoneità per principianti 25/100
docling-project/docling-parse#450 ·
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