Jeśli kiedykolwiek próbowałeś wyciągnąć pogrubiony tekst lub kursywę z pliku PDF, prawdopodobnie zacząłeś od wyrażenia regularnego (regex). Wydaje się to oczywistym rozwiązaniem. Przeszukujesz nazwę czcionki w poszukiwaniu słowa „Bold”, oznaczasz tekst i idziesz dalej. Ta strategia działa wystarczająco długo, by dać ci fałszywe poczucie pewności. Potem ktoś wyeksportuje ten sam dokument z innej wersji Acrobat, z LibreOffice lub za pomocą sterownika „drukuj do PDF” i każde założenie, które zakodowałeś, legnie w gruzach.
Dlaczego nazwy czcionek kłamią
PDF.js poda ci ciągi znaków typu ABCDEF+TimesNewRomanPS-BoldMT. Pierwszych sześć znaków to losowy prefiks wstrzyknięty podczas eksportu, który zmienia się za każdym razem, gdy plik jest generowany ponownie. Opieranie parsera na tym prefiksie to stawianie na szum. Inni eksportery są jeszcze mniej pomocni. Niektórzy emitują same identyfik
To rozróżnienie jest istotne, ponieważ samo odczytywanie deskryptorów czcionki nie ujawni podkreślenia. Należy przyjrzeć się surowym operatorom rysowania na stronie, lub krótko mówiąc, wynikom na poziomie geometrii, w poszukiwaniu krótkich, poziomych odcinków linii biegnących równolegle do linii bazowej w odpowiedniej odległości. Gdy silnik ekstrakcji wykryje taki segment pod ciągiem tekstu, oznacza ten ciąg jako podkreślony. Traktowanie tego jako oddzielnej warstwy detekcji pozwala zachować rzetelność modelu danych: pogrubienie i kursywa są immanentnymi cechami czcionki, podczas gdy podkreślenie jest zewnętrzną dekoracją renderowaną przez dokument.
Działający pipeline
Czysty system ekstrakcji rozdziela odpowiedzialności na wyraźne warstwy. Najpierw worker geometrii analizuje stronę. Odpytuje page.commonObjs, aby zbudować fontStyleMap, sprawdza tablicę transformacji każdego elementu tekstowego, aby wykryć syntetyczną kursywę, i skanuje pobliskie ścieżki wektorowe w celu znalezienia podkreśleń. Jego wynikiem jest czysta struktura pośrednia, nazwijmy ją textMeta, w której każdy ciąg tekstu posiada trzy proste wartości logiczne: bold, italic i underline.
Następnie rebuilder tekstu przetwarza tę strukturę i generuje wynik z oznaczonym formatowaniem. Kolejność zagnieżdżania ma tutaj znaczenie. Poprawna hierarchia umieszcza podkreślenie na zewnątrz, następnie kursywę, a na samym końcu pogrubienie. Oznacza to, że w pełni sformatowany ciąg staje się <u><i><b>text</b></i></u>. Taka kolejność zapobiega nieprawidłowym nakładaniom HTML i zapewnia spójne renderowanie w przeglądarkach oraz konwerterach dokumentów. Odzwierciedla to również logikę typograficzną: dekoracja otacza wyróżnienie semantyczne, a wyróżnienie semantyczne otacza wagę strukturalną.
Potęga tego podejścia polega na tym, że wykorzystuje ono tylko to, co PDF już wie o samym sobie. Nie ma tu mowy o OCR, usługach chmurowych typu cloud vision ani modelach uczenia maszynowego zgadujących style na podstawie rastrowych pikseli. Odczytujesz własną warstwę semantyczną pliku, udostępnioną poprzez geometrię i metadane, które zostały już obliczone przez aplikację tworzącą dokument. Wynikiem jest szybkość, determinizm i dokładność w chaotycznym świecie generatorów PDF.
Kluczowy wniosek
Używanie wyrażeń regularnych (Regex) względem nazw czcionek to pułapka. Wydaje się to skrótem, ponieważ działa na tym jednym pliku, który przetestowałeś, ale zawodzi przy najmniejszym obciążeniu podczas drugiego eksportu. Prawdziwe informacje znajdują się już wewnątrz PDF, w deskryptorach, macierzach i ścieżkach wektorowych. Zbuduj swój potok przetwarzania w oparciu o te fakty. Zapytaj obiekt czcionki, czy jest pogrubiony. Sprawdź macierz transformacji pod kątem pochylenia (shear). Szukaj podkreśleń w narysowanych segmentach. Jeśli będziesz odpytywać wewnętrzną strukturę inżynieryjną dokumentu zamiast jedynie zdrapywać jego powierzchowne etykiety, uzyskasz style, które przetrwają przejście z jednego generatora PDF do drugiego.
