HotPDF Component henter ut Unicode-tekst fra enhver PDF du laster inn i Delphi gjennom to kall: ExtractLoadedPageText returnerer leseflyt-teksten på en side, og ExtractLoadedPageTextLayout (lagt til i v2.263.0) gjenoppretter sidens visuelle arrangement som ren tekst, slik at kolonner, innrykk og tabelljustering overlever i utdataene. Begge fungerer på dokumenter som HotPDF ikke har opprettet, noe som er tilfellet som faktisk betyr noe: fakturaen en kunde sendte deg på e-post, rapporten et skannebyrå leverte, eller kontrakten generert av programvare som ingen lenger husker navnet på
Å komme dit krevde mer maskineri enn de to signaturene antyder, fordi en PDF ikke lagrer tekst på samme måte som en tekstfil gjør. Denne artikkelen går gjennom begge uthentingsmodusene, og åpner deretter panseret på de tre delene under — CMap-leseren, innholdsstrømtolken og fontdekodings-fallback-kjeden — fordi det å vite hvordan kartleggingen fungerer er forskjellen mellom å trekke på skuldrene av søppelutdata og å diagnostisere det
Hvorfor er tekstuthenting vanskeligere enn å lese strenger ut av filen?
En PDF-innholdsstrøm registrerer tegnkoder, ikke tegn. Operatorene Tj og TJ (ISO 32000-1 §9.4.3) bærer strenger av byte hvis betydning helt avhenger av fonten valgt av den foregående Tf: byte 0x41 kan være bokstaven A under WinAnsi, en vilkårlig glyf i en delsett-font, eller halvparten av en to-byte CID i en sammensatt CJK-font. ISO 32000-1 §9.10 definerer tekstuthenting som akkurat dette dekodingsproblemet — å kartlegge hver kode tilbake til Unicode ved hjelp av informasjonen fontordboken gir — og standarden er eksplisitt på at en samsvarende fil ikke kreves å gi nok informasjon til å gjøre det
Den siste setningen forklarer alle feilrapporter av typen "hvorfor produserer klipp og lim inn fra denne PDF-en bare tullball" du noen gang har sett. En produsent som bygger inn en delsett-font uten en /ToUnicode-tabell har skrevet en fil som vises perfekt, men som hentes ut som nonsens, fordi kartleggingen fra kode til glyf eksisterer, men kartleggingen fra kode til Unicode ble aldri sendt med. Ethvert ærlig uthentings-API is therefore a best-effort chain of fallbacks, and the useful question is how deep the chain goes
Leseflyt-uthenting med ExtractLoadedPageText
For søkeindeksering, søkeordsmatching eller mating av tekst to an analysis pipeline, ExtractLoadedPageText is the call you want. The signature is function ExtractLoadedPageText(PageIndex: Integer; out AText: UnicodeString): boolean — page indexes are zero-based, the result arrives as a native Delphi UnicodeString, and the function returns False when the page has no readable content stream rather than raising
Linjeskift i utdataene kommer fra en bevisst enkel heuristikk: når en glyfs vertikale utgangspunkt flytter seg med mer enn halvparten av den gjeldende skriftstørrelsen — signaturen til et Td- eller T*-trinn i innholdsstrømmen — settes det inn et linjeskift. Tegn som dekoderen ikke kan løse opp blir til mellomrom i stedet for å forsvinne, slik at ordgrenser overlever selv om enkelte glyfer ikke gjør det. Det denne modusen ikke prøver på er leserekkefølge-klynging eller multikolonne-deteksjon: en side med to kolonner kommer ut flettet i innholdsstrøm-rekkefølge, noe som vanligvis, men ikke alltid, er visuell rekkefølge
Når bør du bruke layout-bevarende uthenting i stedet?
ExtractLoadedPageTextLayout er det riktige kallet når posisjon har betydning: tabeller, skjemaer, kodelister, alt du har tenkt å sammenligne (diff), søke i (grep) eller tolke kolonnevis. I stedet for å flate ut glyfer til en strøm, samler den dem inn i grunnlinjer, sorterer hver grunnlinje etter X, og reproduserer horisontalt og vertikalt tomrom på et tegnrutenett med fast tegnbredde (monospace) dimensjonert ut fra median glyffremrykk og skriftstørrelse. Brede gap mellom kjøringer på samme grunnlinje blir til sekvenser av mellomrom; store gap mellom grunnlinjer blir til tomme linjer. Resultatet leses slik siden ser ut
Hvordan dekoder HotPDF tegnkoder til Unicode?
HotPDF Component løser opp hver tegnkode gjennom en prioritetsordnet fallback-kjede: fontens innebygde /ToUnicode-CMap først, deretter /Encoding-oppføringen (strøm eller navngitt CMap), så — for sammensatte fonter — Adobe standard CMap-filer for tegnkolleksjoner som Adobe-GB1, Adobe-CNS1, Adobe-Japan1 og Adobe-KR, og til slutt de innebygde WinAnsi- og MacRoman-tabellene for enkle fonter. En strategi som ikke kan levere et svar faller lydløst tilbake på den neste i stedet for å utløse et unntak, og en kode som uttømmer hele kjeden løses til 0 slik at kaller kan telle bommer i stedet for å gjette
/ToUnicode-CMap (ISO 32000-1 §9.10.3) ligger først fordi det er kartleggingen produsenten skrev spesifikt for uthenting. Den standard Adobe CMap-banen betyr noe for CJK-dokumenter som bruker forhåndsdefinerte CMaps som UniGB-UTF16-H i stedet for å bygge inn noe: HotPDF leverer samlingsfilene under sin resources\CMap-katalog, finner dem i forhold til den kjørbare filen ved kjøring, og bufrer hvert analyserte kart per prosess — verdt å vite fordi den største av dem, Adobe-GB1-kartet, er omtrent 2 MB kildetekst du ikke ønsker å analysere på nytt per side. Hvis katalogen mangler, hopper dekoderen ganske enkelt over diskbaserte CMaps og jobber med innebygde tabeller pluss de innebygde kodingene. Dette er lesespeilet til formingsproblemet som dekkes i kompleks skrifttegnforming med HotPDF, der den samme forskjellen mellom kode og glyf oppstår ved skriving
To CMap-syntaksfeller verdt å kjenne til
CMap-filer ser ut til å være enkle å analysere, men er det ikke, og to detaljer står for de fleste feil ved første forsøk på å skrive en tolk. Det første er at postantallet kommer før seksjonsnøkkelordet: en seksjon lyder 2 beginbfchar, ikke beginbfchar 2. En tolk som forventer antallet etter nøkkelordet vil konsumere tallet som et løst token, og deretter finne null oppføringer i hver seksjon. Den robuste tilnærmingen — den HotPDF-leseren landet på — er å ignorere antallet folkens fullstendig og gå i løkke til det matchende endbfchar- / endbfrange-nøkkelordet, noe som har fordelen av å tolerere virkelige filer der antallet rett og slett er feil
Den andre fellen er at målene for bfchar og bfrange er UTF-16BE-strenger, ikke heltall. Målet <D83DDE00> betyr U+1F600 — et surrogatpar som må settes sammen igjen til ett enkelt kodepunkt — og å lese disse fire bytene som et big-endian-heltall produserer en meningsløs verdi for alle kodepunkter utenfor det grunnleggende flerspråklige planet (BMP). Emoji i PDF-er er ikke lenger eksotisk, så en dekoder som hopper over surrogatsammensetting feiler på filer brukerne dine faktisk har. HotPDF tolker heksadesimalliteralen til rå byte først, og setter deretter sammen UTF-16BE-kodeenheter igjen, noe som også dekker målene med flere tegn som ligaturkartlegginger produserer
Gå ned på glyfnivå med ExtractLoadedPageGlyphs
Begge tekstkallene er bygd på ExtractLoadedPageGlyphs, og den underliggende THPDFGlyphArray er også tilgjengelig for din kode. Hver THPDFGlyphRecord bærer det oppløste Unicode-kodepunktet sammen med den rå tegnkoden, tegnets bytebredde (1, 2 eller 4, bestemt av CMap-ens codespacerange), den aktive fontressursnøkkelen og -størrelsen, X- og Y-aksens utgangspunkt i brukerrommet samt det horisontale fremrykket. Det er nok til å bygge ordgrensedeteksjon, posisjonert utheving eller en tilpasset layout-algoritme uten å berøre innholdsstrømmen selv
var
Glyphs: THPDFGlyphArray;
I, Unresolved: Integer;
begin
if Pdf.ExtractLoadedPageGlyphs(0, Glyphs) then
begin
Unresolved := 0;
for I := 0 to High(Glyphs) do
if Glyphs[I].Unicode = 0 then
Inc(Unresolved);
if Unresolved > 0 then
ShowMessageFmt('%d of %d glyphs have no Unicode mapping',
[Unresolved, Length(Glyphs)]);
end;
end;
Hvilke PDF-er vil ikke gi fra seg teksten sin?
Noen filer overvinner enhver ekstraktor, og det er bedre å oppdage dem enn å sende videre utdataene deres. Skannede dokumenter er det klareste tilfellet: en side som er ett stort bilde inneholder ingen tekstoperatorer i det hele tatt, så uthentingen returnerer helt riktig en tom streng — løsningen er OCR, og uthenting av sidebildene fra den lastede PDF-en er det første trinnet i den pipelinen. Delsett-fonter uten en /ToUnicode-tabell er det vanskeligere tilfellet: hvis /Encoding-banen og standard CMaps også blir tomme, løses disse glyfene til 0 og fremstår som mellomrom i tekstkallene. Krypterte dokumenter hentes ut som normalt, forutsatt at du laster dem med passordet deres via LoadFromFile-overlastingen, slik at strømmene dekrypteres før tolken ser dem
En smalere grense er verdt å si tydelig: dekodingskjeden leser CMap- og innholdsstrømmer gjennom HotPDFs Flate-bane, så en font hvis ToUnicode-strøm bruker et uvanlig filter faller tilbake på neste strategi i stedet for å feile siden. I praksis dekker FlateDecode nesten alt som er produsert de siste to tiårene, og tilbakefallet er lydløst av design — du får den beste teksten filen tillater i stedet for et unntak. Det samme objektmaskineriet på lesesiden som løser opp fontordbøker her, driver også redigering av metadata i lastede dokumenter, slik at en pipeline for dokumentmottak kan hente ut, inspisere og annotere i én operasjon
Tekstuthenting, layout-bevarende rendring, tilgang på glyfnivå, og søk-og-erstatt-funksjonene bygd på dem er alle en del av standard HotPDF Component for Delphi og C++Builder — ingen eksterne DLL-er, ingen OS-teksttjenester, just Object Pascal you can step through when a strange file lands in your queue