HotPDF v2.743.0 splošči opombe PDF, ki nimajo prikaznega toka /AP, namesto da bi jih tiho preskočil. FlattenLoadedAnnotations zdaj pripomoček brez prikaza pošlje skozi EnsureLoadedFieldAppearanceStream, za oznake brez prikaza pa iz lastnosti same opombe zgradi Form XObject, zato vrednosti, vpisane v obrazec /NeedAppearances, preživijo v vsebini strani namesto da izginejo ob sploščitvi. Napaka, ki je izsilila to spremembo, je videti kot neaktivnost. Stranka pošlje izpolnjen obrazec za prijavo, natisnjen v PDF iz brskalnika. Naložite ga v HotPDF, pokličete FlattenLoadedAnnotations, dobite 0, shranite in dostavite dokument s praznimi polji, kjer je prosilec vpisal ime in znesek. Nič se ne sproži in nič se ne zabeleži. Vrednosti so bile ves čas v datoteki, v vnosu /V vsakega polja, prehod za sploščitev pa je šel naravnost mimo njih, ker noben od teh pripomočkov ni imel prikaznega toka za vdelavo
Zakaj sploščitev obrazca, natisnjenega iz brskalnika, izgubi vpisane vrednosti?
Ker obrazec /NeedAppearances shrani vrednost, ne shrani pa slike vrednosti. ISO 32000-1 12.7.2 dovoljuje interaktivnemu obrazcu, da v slovar AcroForm nastavi /NeedAppearances true, kar pregledovalniku pove, naj ob odpiranju iz /V, /DA in /Q zgradi vizualno površino vsakega polja. Izdelovalci, ki obrazce ustvarjajo poceni — poti tiskanja iz brskalnika, polnilniki na strežniku in nekateri sprednji deli za skeniranje — to možnost izkoristijo in sploh ne zapišejo /AP. Sploščitev je po algoritmu videza v ISO 32000-1 12.5.5 prepisovalno opravilo: vzemi običajni prikazni tok opombe, preslikaj njegov /BBox na /Rect, ga pokliči iz vsebinskega toka strani z operatorjem Do in nato izbriši opombo. Brez izvornega toka ni ničesar za prepisati. Izvorna implementacija HotPDF iz v2.386.0 je to obravnavala kot »preskoči«, kar je samo zase zagovarjano, v celoti pa katastrofalno: dokumenti, ki najverjetneje potrebujejo sploščitev, najmanj verjetno vsebujejo prikaze. Ista vrzel je pogoltnila tudi oznake — Highlight iz orodja za pregled, Square iz rdečega popravka ali podpis Ink — kadar se je izdelovalec zanašal na to, da jih bo izrisal pregledovalnik
Kje HotPDF vključi sintezo v FlattenLoadedAnnotations
Točka priklopa je namerno pozna: šele po neuspešnem iskanju prikaza, ne prej. FlattenLoadedAnnotations še vedno najprej zahteva običajni prikaz od GetLoadedAnnotationAppearanceStream, opomba, ki ga že ima, pa je vdelana natanko tako kot v v2.386.0. Samo rezultat nil pri opombi z nedegeneriranim /Rect in brez zastavice hidden vstopi v pot sinteze. Vrstni red je pomemben: avtor dokumenta, ki se je potrudil zapisati /AP, dobi svoje bajte nazaj, ne HotPDF-ove rekonstrukcije
NStrm:= GetLoadedAnnotationAppearanceStream(Indices[PgI], AnI, aakNormal);
if (NStrm= nil) and (RR> RL) and (RT> RB) and ((FlagsValue and 2)= 0) then
begin
if Subtype= 'Widget' then
begin
FieldIdx:= GetLoadedFormFieldIndexForAnnotation(Indices[PgI], AnI, WidgetIdx);
if FieldIdx>= 0 then
EnsureLoadedFieldAppearanceStream(FieldIdx);
// ponovi poizvedbo: generator je pripomočku dodal /AP /N
NStrm:= GetLoadedAnnotationAppearanceStream(Indices[PgI], AnI, aakNormal);
end
else
NStrm:= SynthesizeMarkupAppearance(AnnotDict, Subtype, RL, RB, RR, RT);
end;
Od tod se družini opomb razcepita. Pripomoček se prek GetLoadedFormFieldIndexForAnnotation razreši nazaj na lastniško polje in pošlje v EnsureLoadedFieldAppearanceStream, generator videza polj, ki je v tej knjižnici PDF za Delphi od v2.328.0. Ponovna uporaba tega namesto pisanja drugega izrisovalnika polj je bistvo — že pokriva pisave Type0, prelamljanje vrstic, poravnavo, stanja /AS potrditvenih polj in izbirnih gumbov ter zasuk /MK, isto mehaniko, ki stoji za dodajanjem polj AcroForm v že naložen PDF. Vse drugo gre v sintetizator oznak. Za klicatelja se nič ne spremeni: isti enovrstični klic sploščitve zdaj vrne neničelno število pri dokumentih, ki so prej vrnili nič
Doc:= THotPDF.Create(nil);
try
Doc.LoadFromFile('needappearances-form.pdf');
// v2.743.0: pripomočki in oznake brez AP se sintetizirajo, nato vdelajo
Flattened:= Doc.FlattenLoadedAnnotations; // vse strani, vse podvrste
// Flattened:= Doc.FlattenLoadedAnnotations('1-3', 'Highlight');
if Flattened= 0 then
raise Exception.Create('nothing was flattened');
Doc.SaveLoadedDocument('flattened.pdf');
finally
Doc.Free;
end;
Zakaj QuadPoints in InkList pristaneta na napačnem mestu?
Ker so te koordinate v uporabniškem prostoru strani, sintetizirani prikazni tok pa riše v lastnem prostoru /BBox, izhodišči pa nista ista točka. ISO 32000-1 Preglednica 176 definira /QuadPoints za opombe besedilnih oznak v privzetem uporabniškem prostoru, Preglednica 174 pa enako za krajišči /L opombe črte; /InkList sledi isti konvenciji. HotPDF sintetiziranemu obrazcu dodeli /BBox [0 0 W H], katerega izhodišče sedi v spodnjem levem kotu /Rect. Zato je treba vsako točko, vzeto iz /QuadPoints, /L ali /InkList, pred zapisom v vsebinski tok premakniti za negirano spodnje levo krajišče /Rect. Če to zgrešite, se poudarek na vrstici 700 točk više na strani nariše 700 točk nad lastnim poljem, kar v praksi pomeni, da se ne nariše nikjer. Popravek je eno odštevanje na koordinato in se sestavi s cm, ki ga odda vdelava — ta matrika preslika /BBox nazaj na /Rect, zato se koraka izničita v pravilno absolutno geometrijo
// Krajišča /L so v uporabniškem prostoru strani (ISO 32000-1 Preglednica 174);
// izvor BBox je v spodnjem levem kotu /Rect, zato premakni za -(RL, RB)
X1:= ArrNum(LA, 0, 0)- RL;
Y1:= ArrNum(LA, 1, 0)- RB;
X2:= ArrNum(LA, 2, 0)- RL;
Y2:= ArrNum(LA, 3, 0)- RB;
StrokeOp:= ColorOp(DArr('C'), true);
if StrokeOp= '' then
StrokeOp:= '0 G';
Result:= _FloatToStrR(BW)+ ' w '#10+ StrokeOp+ #10+
_FloatToStrR(X1)+ ' '+ _FloatToStrR(Y1)+ ' m '+
_FloatToStrR(X2)+ ' '+ _FloatToStrR(Y2)+ ' l S'#10;
Kaj sintetizirani prikaz oznake dejansko nariše
Sintetizator oznak bere slovar opombe in ničesar drugega, zato je izhod predvidljiv in pošten glede tega, česar ne more vedeti. FreeText in Stamp izrišeta /Contents s pisavo in barvo, razčlenjenima iz /DA, poravnano po /Q in z 2 pt odmika. Square in Circle izrišeta obris re ali Bezierjev obris s štirimi loki, potezen v /C, zapolnjen z /IC, ko je ta prisoten, s širino iz /BS /W. Line in Ink potežeta svoja oglišča. Highlight zapolni vsak štirikotnik, Underline, StrikeOut in Squiggly pa potežejo črto na dnu štirikotnika, v njegovi sredini ali kot cikcak z eno točko. /CA, manjši od 1, postane ExtGState z vnosom ca, na začetku toka pa se sklicuje kot /GSA gs
Kodiranje besedila določi vnos /DR /Font AcroForma, ki ga poimenuje /DA. Če je /Subtype te pisave Type0, HotPDF niz zapiše kot šestnajstiški literal UTF-16BE z oznako vrstnega reda bajtov FEFF; sicer zapiše ubežni literalni niz, pri čemer ubeži oklepaje in poševnice, bajte nad 126 pa zapiše osmiško. Operator Tf iz /DA se odda pred BT, kar je dovoljeno, ker stanje besedila ostane med mejama besedilnega objekta, hkrati pa ni treba razstaviti niza /DA. Dve omejitvi je treba povedati naravnost. Širina vrstice za prelamljanje in poravnavo je ocenjena s hevristiko polovice em-a in celotnega em-a namesto z resničnimi metrikami pisave, zato je poravnava pri sorazmerni pisavi blizu, ne pa natančna. Podvrsta, ki nima ničesar sintetizabilnega — Popup, Link ali Stamp, katerega edina vsebina je ime ikone — vrne nil in ostane nespremenjena, natanko kot prej
Začasna zamenjava /Annots, ki kaznuje koristno čiščenje
FlattenOneWidget, pot po pripomočku, ki jo uporablja FlattenLoadedFormFields, je past vzdevkov, ki jo mora spoštovati vsaka sprememba znotraj deljene zanke sploščitve. Vrednost /Annots strani začasno zamenja z enočlenskim poljem, da generični prehod sploščitve deluje nad enim samim pripomočkom, nato pa v bloku finally obnovi izvirni kazalec PHPDFDictionaryItem. Obnova se zapiše nazaj v mesto slovarja, zajeto pred klicem
DictItem:= PHPDFDictionaryItem(PageObj.Items.Items[AnnotsIndex]);
Item:= DictItem^.Value;
TemporaryAnnots:= THPDFArrayObject.Create(nil);
TemporaryAnnots.AddObject(Target);
DictItem^.Value:= TemporaryAnnots;
try
Result:= FlattenLoadedAnnotations(IntToStr(PageIndex+ 1), 'Widget')= 1;
finally
DictItem^.Value:= Item; // viseče, če je notranja zanka sprostila ta element
TemporaryAnnots.Free;
end;
V deljeno notranjo zanko dodajte na videz razumno pospravljanje — DeleteValue('Annots'), ko se polje izprazni, da shranjena stran ne bo nosila ostanka praznega polja — in ta klic sprosti prav slovarski element, na katerega kaže DictItem. finally nato zapiše skozi viseči kazalec in proces umre z »Invalid pointer operation«. Dva obstoječa testa sta to takoj ujela, in samo zato je to opomba, ne prijava podpori. Pravilo je splošno: preden deljeni zanki dodate čiščenje, preverite klicatelje glede pogodb o vzdevku ali zamenjavi. Preostalo prazno polje /Annots je kozmetična napaka in ni vredno menjave zagotovila življenjske dobe kazalca
Kaj ostane nevdelano in koliko stane sploščitev
Skrite opombe so namerno izključene. Opomba, katere celo število /F ima nastavljen bit na položaju 2, je po ISO 32000-1 12.5.3 skrita, in ko hkrati nima /AP, obstaja resnična skušnjava, da bi eno sintetizirali in vdelali kot vse druge. To bi bila napaka z varnostnimi posledicami: vdelava nevidne opombe v vsebino strani jo naredi vidno vsem, ki odprejo datoteko. HotPDF te opombe pusti natanko tam, kjer so, in jih ne šteje v vrnjeno vrednost. Enako jasno uporabnikom povejte ceno opomb, ki so vdelane. Sploščitev je nepovratna — opomba se izbriše iz polja strani /Annots, njen vizualni videz pa je zdaj vsebina strani, zato ni več urejanja vrednosti polja, niti niti komentarjev, preklapljanja stanja /AS in ni mogoče obnoviti strukturiranih podatkov brez izvirne datoteke. Sploščite kopijo, izvirnik ohranite in po njem posezite šele, ko dokument preneha biti obrazec in postane zapis. Če je vaš problem podprt z XFA namesto brez prikaza, je ločena pot sploščitve XFA v AcroForm v HotPDF pravo izhodišče, če obrazec še gradite, pa zapiski o povezovanju dejanj in preverjanja polj AcroForm pokrivajo stran za zapisovanje
Še ena opomba o preverjanju, sicer vam bo vzela popoldne. ExtractLoadedPageGlyphs ne sestopi v Form XObject, v njem pa živi vdelani prikaz — tok vsebine strani vsebuje samo zaporedje q ... cm /FlatAn<n> Do Q. Izločanje glifov na sploščeni strani zato ne poroča ničesar, kar je pravilno vedenje, ne izgubljena vdelava. Preverite na ravni bajtov, tako da iščete ime vira /FlatAn, klic Do in /Subtype /Form, ali pa skozi cevovod izrisovanja, ki XObject razširi
Sploščitev opomb je videti kot tri vrstice prepisovanja, dokler ne srečate dokumentov, ki jih ljudje dejansko ustvarjajo. Če v Delphiju ali C++Builderju delate z izpolnjenimi obrazci, oznakami pregleda ali arhivskim izhodom, je vredno prebrati, kako komponenta HotPDF Delphi PDF obravnava stran naloženih dokumentov AcroForms in opomb, preden na njej zgradite lasten generator prikazov