𝗦𝘁𝗼𝗽 𝗣𝗮𝗿𝘀𝗶𝗻𝗴 𝗣𝗗𝗙𝘀 𝗮𝘁 𝗥𝗲𝗻𝗱𝗲𝗿 𝗧𝗶𝗺𝗲

Most developers build PDF extraction tools the wrong way.

They try to guess document structure from the visual output. They render a page to a canvas and look at pixel positions. They use computer vision to find columns or tables.

This approach is backwards.

A PDF already contains the structure you need in the operator stream.

A table is not just a set of pixels. It is a set of path operators like moveTo, lineTo, and rectangle. Zone boundaries are encoded in the CTM stack. You do not need to reconstruct what is already there.

Stop using visual heuristics. Use the source data.

I previously tried using De Casteljau subdivision for bounding boxes. I rejected it during testing.

De Casteljau is a subdivision algorithm. You split curves until the segments are small enough. This works for rendering, but it is bad for bounding boxes.

You have to choose a tolerance. If the tolerance is too loose, the box is wrong. If it is too tight, you waste resources on recursion. There is a better way. An analytical solution using the quadratic formula is exact. It does not recurse. It does not allocate segments.

The same logic applies to zone detection.

Many tools calculate zone boundaries by finding the midpoint between two text groups. This is a visual guess. It is not structural.

If you use midpoints, sub-pixel rounding will place regions in the wrong zones.

The fix is simple. Use the top edge of the bounding box. A region belongs to a zone based on where it starts. Use the actual Y-coordinate of the top edge.

Building a real PDF extractor is harder. You must:

This is more work than pixel-based guessing. But it produces deterministic results.

A pixel-based tool gives different results at 100% zoom than it does at 150% zoom. It is pattern-matching visual artifacts, not extracting structure.

If you do not parse the operator stream, you are building a demo. It might work on your test files, but it will fail on real user uploads.

The path through the operator stream is difficult. You must understand the fill and stroke state machines and the PDF specification. But you only have to learn it once. Then it works for every PDF.

Przestań parsować pliki PDF w czasie renderowania: Lepsza architektura dla ustrukturyzowanej ekstrakcji

Wielu programistów popełnia ten sam błąd: parsowanie plików PDF dopiero w momencie, gdy muszą je wyświetlić lub wyodrębnić z nich dane dla konkretnego widoku. To podejście prowadzi do wąskich gardeł wydajnościowych i tworzy niestabilny kod.

W tym artykule omówię, dlaczego parsowanie w czasie renderowania jest problematyczne i przedstawię lepszą architekturę opartą na ekstrakcji w czasie wprowadzania danych (ingestion-time extraction).

Problem: Parsowanie w czasie renderowania

Wyobraź sobie następujący scenariusz: użytkownik przesyła plik PDF, a Twoja aplikacja musi natychmiast wyświetlić tabelę z danymi zawartymi w tym dokumencie. Aby to zrobić, musisz:

  1. Pobrać plik PDF.
  2. Uruchomić silnik parsowania (np. pdf-parse, PyPDF2 lub coś bardziej zaawansowanego jak Amazon Textract).
  3. Przetworzyć tekst i strukturę.
  4. Przekształcić to w format JSON.
  5. Przesłać dane do frontendu.

To wszystko dzieje się w trakcie cyklu żądanie-odpowiedź (request-response). Jeśli plik jest duży lub silnik parsowania jest wolny, użytkownik czeka na odpowiedź przez kilka lub kilkanaście sekund. Co gorsza, jeśli parsowanie się nie powiedzie, cała próba wyświetlenia strony kończy się błędem.

Rozwiązanie: Ekstrakcja w czasie wprowadzania danych

Zamiast traktować PDF jako źródło prawdy w czasie rzeczywistym, potraktuj go jako surowy materiał wejściowy, który należy przetworzyć natychmiast po przesłaniu.

Kluczem jest oddzielenie procesu parsowania od procesu wyświetlania.

Proponowana architektura

Oto jak powinna wyglądać nowoczesna architektura do obsługi dokumentów:

1. Ingestia (Wprowadzanie danych)

Gdy użytkownik przesyła plik, zapisujesz go w bezpiecznym miejscu (np. Amazon S3 lub Google Cloud Storage). W tym samym momencie tworzysz zadanie w kolejce (np. RabbitMQ, Redis lub AWS SQS).

2. Ekstrakcja (Asynchroniczny proces)

Asynchroniczny worker odbiera zadanie z kolejki. To tutaj dzieje się "ciężka praca":

3. Przechowywanie

Wynik ekstrakcji (JSON) jest zapisywany w bazie danych (np. PostgreSQL, MongoDB), a oryginalny plik PDF pozostaje w storage'u. Teraz masz dwa źródła: surowy plik i ustrukturyzowane dane.

4. Renderowanie

Kiedy użytkownik chce zobaczyć dane, aplikacja nie dotyka już pliku PDF. Zamiast tego wykonuje proste zapytanie do bazy danych: SELECT * FROM document_data WHERE doc_id = .... Odpowiedź jest natychmiastowa, ponieważ dane są już gotowe i ustrukturyzowane.

Dlaczego to jest lepsze?

  1. Wydajność (Performance): Renderowanie jest błyskawiczne. Użytkownik nie czeka na procesy wymagające dużej mocy obliczeniowej (CPU-intensive).
  2. Skalowalność (Scalability): Możesz łatwo skalować liczbę workerów odpowiedzialnych za ekstrakcję, nie wpływając na szybkość działania interfejsu użytkownika.
  3. Niezawodność (Reliability): Jeśli proces ekstrakcji zawiedzie, możesz go powtórzyć (retry) bez wpływu na dostępność aplikacji.
  4. Wyszukiwalność (Searchability): Ponieważ dane są w bazie, możesz je łatwo indeksować i przeszukiwać (np. za pomocą Elasticsearch).
  5. Elastyczność (Flexibility): Jeśli w przyszłości będziesz chciał zmienić model AI lub bibliotekę do parsowania, po prostu uruchom ponownie proces ekstrakcji dla starych plików. Twoja aplikacja kliencka nawet tego nie zauważy.

Podsumowanie

Przestań traktować PDF jako dynamiczne źródło danych w czasie renderowania. Zamiast tego, zbuduj potok (pipeline) przetwarzania danych, który zamieni nieustrukturyzowane pliki w ustrukturyzowane rekordy w Twojej bazie danych. Twoi użytkownicy (i Twój serwer) Ci za to podziękują.