En napačen znak v številki računa in edini razpoložljivi primitiv za urejanje prepiše celoten besedilni izsek. PDF Library for Delphi to vrzel zapolni: GetTextBlockCharContentLocation vsak izločeni položaj UTF-16 preslika nazaj na ukaz vsebinskega toka, operand in območje kodiranih bajtov, ki ga je ustvarilo, ReplaceTextBlockCharSourceBytes pa prepiše samo to območje. Izločanje besedila običajno zavrže vse, kar bi za to potrebovali. Dobite Unicode, širine in geometrijo, izvor pa izgine, zato je znak na položaju 7 bloka 3 preprosto znak. Kateri tok ga je ustvaril, kateri ukaz, kateri operand in kateri bajt znotraj operanda: vse je izgubljeno. Vsaka strategija urejanja posamezne točke, zgrajena na tem, mora ugibati, običajno tako, da išče podniz v dekodirani vsebini in upa, da se pojavi natanko enkrat. Na resnični strani se ne
Zakaj prepis celotnega besedilnega izseka uniči stran?
Ker izsek ni samo besedilo. Operatorji za prikaz besedila v ISO 32000-1 §9.4.3 vključujejo TJ, katerega operand je polje, ki prepleta nize s številskimi prilagoditvami, te številke pa so stavljenje. V vrstici, postavljeni kot [(AB) -120 (CD)] TJ, je med nizoma zasleditev velikosti 120 tisočink em-a. Če izdate nov Tj s povezanim besedilom, zasleditev izgine, vrstica se za las preoblikuje in vrednost na obrazcu zdrsne iz svojega polja. Enak ugovor velja za pisavo: bajti operanda so kode v kodiranju, ki ga je izbral Tf, ne Unicode, pri sestavljeni pisavi pa so lahko dvobajtni CID-i brez povezave z znakom, ki ga preberete iz izločevalnika. Znova ustvarite izsek in pravilni morate biti glede kodiranja pisave, njenega zemljevida /ToUnicode in pokritosti glifov. Urejanje posameznih bajtov se vsemu temu izogne, ker nikoli ne zapusti bajtne domene
Kaj vrne GetTextBlockCharContentLocation?
Metoda en znak razreši v zapis z devetimi polji, pri čemer je vsako polje naslov, ne vrednost. ContentLayer je indeks od 1 naprej v polju strani /Contents oziroma 0, ko je znak prišel iz ugnezdene vsebine. StreamObjectNumber in StreamGeneration določata vsebovani tok. InstructionIndex je položaj od 0 v dekodiranem programu vsebine, OperandIndex je operand besedilnega niza, ArrayElementIndex pa element znotraj polja TJ ali -1 pri neposrednem operandu niza. SourceByteOffset in SourceByteLength nato poimenujeta območje bajtov znotraj tega dekodiranega niza
Var
Lib: TPDFlib;
ListID, Block, CharPos: Integer;
ContentLayer, StreamObjectNumber, StreamGeneration: Integer;
InstructionIndex, OperandIndex, ArrayElementIndex: Integer;
SourceByteOffset, SourceByteLength, Flags: Integer;
Begin
Lib:= TPDFlib.Create;
Try
Lib.LoadFromFile('invoice.pdf', '');
Lib.SelectPage(1);
ListID:= Lib.ExtractPageTextBlocks(3);
Try
// Block in CharPos prideta iz vašega lastnega pregleda GetTextBlockText
If Lib.GetTextBlockCharContentLocation(ListID, Block, CharPos,
ContentLayer, StreamObjectNumber, StreamGeneration,
InstructionIndex, OperandIndex, ArrayElementIndex,
SourceByteOffset, SourceByteLength, Flags)= 1 Then
Begin
// ContentLayer = 0 pomeni, da je glif v ugnezdenem Form XObject
// ArrayElementIndex = -1 pomeni navaden operand Tj, ne polje TJ
End;
Finally
Lib.ReleaseTextBlocks(ListID);
End;
Finally
Lib.Free;
End;
End;
Poizvedba ob klicu ne stane ničesar. Medtem ko izrisovalnik dekodira vsako plast vsebine, registrira logične razpone, po katerih hodi, zato je poizvedba po položaju binarno iskanje po urejenem seznamu intervalov, ne linearni pregled vsakega razpona vsebine za vsak znak. Ko vprašate, se nič ne razčleni znova; preslikava je bila izdelana med prehodom izločanja, ki ste ga že plačali. Če že naštevate zadetke z iskom besedila PDF, ki vrača koordinate zadetkov, je dodajanje lokacije vsebine za vsak zadetek skoraj zastonj
Urejanje bajtov, ne Unicode
ReplaceTextBlockCharSourceBytes sprejme AnsiString surovih nadomestnih bajtov v aktivnem kodiranju pisave PDF. To je celotna zasnova in je namerna. Ni transkodiranja, ni ponovnega kodiranja in ni ugibanja o pisavi. Knjižnica vaše bajte vstavi čez imenovano območje ciljnega niza in znova odda vsebovano plast vsebine. Sosednji nizi v istem polju TJ in številske zasleditve med njimi ostanejo bajtno enaki. Vzemite zgornjo razporeditev: iskanje B v [(AB) -120 (CD)] TJ vrne ArrayElementIndex 0, SourceByteOffset 1 in SourceByteLength 1. Zamenjajte ga z Z in oddana vsebina vsebuje (AZ), za njo pa sta še vedno -120 in (CD), oba nedotaknjena. Regresijska zbirka to preverja dobesedno, saj je »ohranili smo zasleditev« trditev, ki lahko neopazno preneha veljati
Function EditableHere(Flags: Integer): Boolean;
Begin
Result:= ((Flags and PDF_TEXT_CHAR_CONTENT_LOCATION_VALID)<> 0)and
((Flags and (PDF_TEXT_CHAR_CONTENT_LOCATION_GENERATED or
PDF_TEXT_CHAR_CONTENT_LOCATION_ACTUALTEXT or
PDF_TEXT_CHAR_CONTENT_LOCATION_NESTED or
PDF_TEXT_CHAR_CONTENT_LOCATION_CROSS_LAYER or
PDF_TEXT_CHAR_CONTENT_LOCATION_TRANSCODED))= 0);
End;
// ...
If EditableHere(Flags) Then
Begin
If Lib.ReplaceTextBlockCharSourceBytes(ListID, Block, CharPos, 'Z')= 1 Then
Begin
// Vsaka lokacija na starem seznamu je zdaj zastarela. Izločite znova.
Lib.ReleaseTextBlocks(ListID);
ListID:= Lib.ExtractPageTextBlocks(3);
End
Else If Lib.LastErrorCode= PDFLIB_ERROR_TEXT_LOCATION_STALE Then
// Plast se je od izločanja spremenila
Else If Lib.LastErrorCode= PDFLIB_ERROR_TEXT_LOCATION_READ_ONLY Then
// Zastavica, ki je nismo preverili, ali zastavica, dodana v poznejši različici
End;
Vredno si je zapomniti dve operativni podrobnosti. Klic začasno preklopi na stran, iz katere je bil izločen seznam besedila, in ob uspehu ter ob napaki obnovi prej izbrano stran, zato ne premakne tiho vašega kazalca. Ob uspehu tudi izbriše posnetke elementov strani, kar razveljavi vse ročaje, ki ste jih imeli iz prejšnjega prehoda naštevanja
Katerih znakov ni mogoče urejati?
Šest kategorij, knjižnica pa vsako poimenuje v bitni maski Flags, namesto da bi nedoločno odpovedala. To je pomembnejše od srečne poti, saj so primeri brez preslikave v resničnih dokumentih pogosti in ima vsak svoj razlog
PDF_TEXT_CHAR_CONTENT_LOCATION_LIGATURE: več izločenih položajev UTF-16 se razširi iz enega izvornega glifa. Vnos/ToUnicode, ki eno kodo preslika vfi, vam da dva znaka, ki si delita isto območje bajtov, zato ju obravnavajte kot en izvorni glif in območje uredite enkratPDF_TEXT_CHAR_CONTENT_LOCATION_GENERATED: znak je bil sintetiziran med postavitvijo. Ugibani presledki med besedami so običajen primer, izvornih bajtov pa sploh nimajo, zatoSourceByteOffsetvrne -1 inSourceByteLength0PDF_TEXT_CHAR_CONTENT_LOCATION_ACTUALTEXT: prebrano besedilo je prišlo iz zamenjave/ActualText. Enolične povratne preslikave iz zamenjanega niza v izvorne bajte ni, zato je lokacija samo diagnostičnaPDF_TEXT_CHAR_CONTENT_LOCATION_NESTED: glif je znotraj Form XObject. Bajti so naslovljivi, vendar lahko obrazec izriše več strani, zato bi bilo njegovo urejanje prek vmesnika visoke ravni urejanje, ki ga niste zahtevaliPDF_TEXT_CHAR_CONTENT_LOCATION_TRANSCODED: operand je bil šestnajstiški niz z označbo vrstnega reda bajtov UTF-16BE, ki ga obstoječa pot izločanja dekodira pred preslikavo pisave. Odmiki v dekodiranem rezultatu ne naslavljajo več izvornih bajtov, zato je zastavica veljavnosti odstranjenaPDF_TEXT_CHAR_CONTENT_LOCATION_CROSS_LAYER: operand niza in njegov operator za prikaz besedila sta v dveh različnih tokovih
Ta zadnji primer si zasluži lasten stavek, ker inženirji pogosto domnevajo, da se ne more zgoditi. ISO 32000-1 §7.8.2 pravi, da so tokovi v polju strani /Contents združeni, meja med njimi pa mora pasti le na leksikalno mejo. Zato sta BT /F1 16 Tf 220 340 Td (CrossLayer) v enem toku in Tj ET v naslednjem povsem veljavna stran. Preslikava ohrani diagnostični položaj, vendar ga označi kot samo za branje, saj indeks ukaza pripada drugi plasti kot bajti operanda in bi uporaba enega za naslavljanje druge pokvarila datoteko
Kako knjižnica ve, ali je preslikava še veljavna?
Prstni odtisi, preverjeni neposredno pred pisanjem. Vsak seznam izločanja zabeleži izvorno stran ter za vsako plast vsebine dolžino plasti in dve neodvisni drseči zgoščevalni vrednosti: zgoščevanje FNV-1a in XOR-zgoščevanje v slogu DJB2. Preden ReplaceTextBlockCharSourceBytes karkoli razčleni, znova prebere ciljno plast in primerja vse tri vrednosti. Vsaka sprememba bajta kjer koli v tej plasti vrne PDFLIB_ERROR_TEXT_LOCATION_STALE in pisanje se ne izvede. To je namerno konservativno: preverjanje velja za plast, ne za ukaz, zato tudi nepovezano urejanje drugje v istem vsebinskem toku razveljavi vašo lokacijo. To je prava kompromisna odločitev: odmik v toku, ki se je premaknil za en sam bajt, ni skoraj pravilen, temveč tiha okvara. Enaka disciplina velja za preostalo površino urejanja, vključno s sledilnikom stanja vsebinskega toka za CTM in izrezovanje. Po vsaki uspešni zamenjavi seznam zavrzite in ga izločite znova
Preslikava samo za branje prek Direct Access
DAGetTextBlockCharContentLocation vrne enak zapis za stran, odprto prek poti Direct Access, z enakim besediščem zastavic. Po konstrukciji je samo diagnostična: ReplaceTextBlockCharSourceBytes deluje nad izbranim dokumentom, Direct Access pa je bralna pot. Podatki o lokaciji ostanejo v seznamu besedilnih blokov tudi po zaprtju ročaja datoteke, zato jih je mogoče uporabiti za naknadno revizijo
FileHandle:= Lib.DAOpenFileReadOnly('audit.pdf', '');
Try
PageRef:= Lib.DAFindPage(FileHandle, 1);
DirectList:= Lib.DAExtractPageTextBlocks(FileHandle, PageRef, 3);
Try
Lib.DAGetTextBlockCharContentLocation(DirectList, Block, 1,
ContentLayer, StreamObjectNumber, StreamGeneration,
InstructionIndex, OperandIndex, ArrayElementIndex,
SourceByteOffset, SourceByteLength, Flags);
// Lokacije ostanejo berljive po DACloseFile
Finally
Lib.DAReleaseTextBlocks(DirectList);
End;
Finally
Lib.DACloseFile(FileHandle);
End;
Uporabite ga za odgovore na vprašanja, ne za spreminjanje stvari. Katere strani vsebujejo besedilo, ki ga ni mogoče nikoli urejati na mestu? Kolikšen delež korpusa pride z zamenjavami /ActualText? Izhod katerega ponudnika razdeli operatorje čez plasti vsebine? To so poceni poizvedbe, ko ima vsak znak naslov, in vredno jih je izvesti, preden se zavežete popravljalnemu cevovodu
Kje se urejanje posamezne točke konča
Urejanje posamezne točke je skalpel, ne besedilni pogon. Bajte spremeni na mestu, zato se nadomestno besedilo, ki je širše ali ožje od izvirnika, ne bo prelomilo v novo vrstico, ne bo znova ovilo in ne bo posodobilo zasleditev okoli sebe. Zamenjava ene številke z drugo v polju z enakomerno širino je dober primer. Ponovno tipkanje odstavka ni. In to izrazito ni varnostno orodje: prepis bajtov glifov pusti izvirne bajte obnovljive iz zgodovine revizij datoteke, zato vse, kar zahteva zaupnost, spada v pravo redakcijo, ki vsebino odstrani, namesto da jo prekrije. V zameno za te omejitve dobite poštenost. Vsak znak ima bodisi bajtni naslov, na katerem lahko ukrepate, bodisi imenovano zastavico, ki pove, zakaj ga nima, preverjanje prstnega odtisa pa zastarelo preslikavo spremeni v trdo napako namesto v pokvarjeno stran. Preslikava znaka v vsebinske bajte in zamenjava izvornih bajtov na mestu sta del površine za izločanje besedila in urejanje vsebine v PDF Library for Delphi, izvorni knjižnici PDF za Delphi, C++Builder in Lazarus