Tehnični članak

Sploščevanje hiperpovezav bogatega besedila XFA v povezave PDF v Delphi

XFA (arhitektura obrazcev XML) je opuščena. ISO 32000-1 jo vsebuje v §12.7 z opombo, da je odstranjena iz PDF 2.0, sodobni pregledovalniki pa enega za drugim opuščajo svoje pogone XFA. Nič od tega ni izpraznilo arhivov. Vladne obrazce za sprejem, zavarovalniške vloge in bančne izpiske so avtorji pisali kot XFA dobri dve desetletji, te datoteke pa danes še vedno prihajajo v nabiralnike in dokumentne cevovode. Ko pregledovalnik, ki jih je nekoč izrisal, to preneha početi, se obrazec pretvori v prazno stran z oznako "prosim, odprite v drugem bralniku". Trajna rešitev je sploščiti XFA v statično vsebino PDF, ki jo lahko naslika kateri koli bralnik

Težek del sploščevanja niso polja. Besedilna polja in potrditvena polja se dovolj čisto preslikajo na gradnike AcroForm. Težek del je bogato besedilo, ki ga XFA shranjuje znotraj elementa draw, v bloku <exData contentType="text/html">. Ta blok je podnabor HTML s vgrajenim slogom in pogosto sidrnimi elementi. Da bi ga dali na stran, je treba reproducirati tako oblikovano besedilo kot žive hiperpovezave - in prav pri hiperpovezavah večina implementacij tiho obupa

Kako je videti bogato besedilo XFA

Telo exData je majhen rezin XHTML. Odstavek je <p>; obarvan razpon znakov je <span> z lastnim vgrajenim CSS za krepkost, poševnost, barvo in velikost; hiperpovezava pa je <a href="...">, ki ovija vidno besedilo. Ena sama vrstica lahko vsebuje več razponov zapored, vsak z drugačnim slogom, eden od njih pa je lahko sidrni element. Oblikovanje ni dekoracija, ki bi jo mogli opustiti. Klavzula, izrisana v krepki rdeči barvi, ker je pravno opozorilo, mora ostati krepka in rdeča po sploščevanju, sicer sploščen dokument napačno predstavlja izvirnika

Zato stroj za sploščevanje ne more obravnavati bloka kot en niz. Mora prehoditi vgrajeno strukturo, razrešiti veljavni slog vsakega poteka z nanašanjem vgrajenega CSS razpona na osnovno pisavo elementa draw ter razporediti poteke drug za drugim čez vrstico. HotPDF vsak od teh razporejenih fragmentov modelira kot interni zapis TXFARichRun. Zapis nosi besedilo poteka, razrešeni slog, izmerjeno okno in - za sidrni element - Href, na katerega kaže

Razporejanje potekov od leve proti desni

Pozicioniranje je tisto, kjer bogato besedilo preneha biti problem razčlenjevanja in postane tipografski problem. Poteki si delijo vrstico, zato vsak poteče tam, kjer se je prejšnji končal. Nobena oznaka ne beleži teh položajev; izmeriti jih je treba. Interna rutina LayoutRichText stroja izmeri vsak potek z enakimi metrikami pisave, ki ga bodo pozneje narisale, nato pa nastavi vodoravni odmik poteka na tekočo vsoto vseh predhodnih širin potekov. Potek ena se začne pri izhodišču elementa draw, potek dva pri širini poteka ena, potek tri pri skupni širini prvih dveh in tako naprej čez vrstico

Zato je poravnava pisave za merjenje tako pomembna. Prehod postavitve meri napredke; ločen prehod izrisovanja izriše glifi. Če se ta dva prehoda ne strinjata glede pisave, okna, ki jih je izračunala postavitev, ne bodo ležala pod glifi, ki jih izriše izrisovalnik. HotPDF ju ohranja usklajena z preslikavo razrešenega sloga vsakega poteka na specifikacijo pisave - prek internega pomočnika RunStyleToFontSpec - ki ustreza privzetim vrednostim izrisovalnika Arial pri 10 točkah. Izmerjen napredek in narisano besedilo se potem strinjata in izmerjeno okno poteka resnično pokriva znake, ki jih bralec vidi

// Conceptual shape of one laid-out run. The engine builds an array of these
// internally; you never construct them yourself, but the fields explain how a
// link's hit box is derived from measured geometry rather than from text.
type
  TRichRunInfo = record
    Dx, Dy : Double;       // top-left, relative to the draw-box origin
    W, H   : Double;       // measured run box (width from the layout pass)
    Text   : AnsiString;   // the run's visible characters
    Href   : AnsiString;   // URI target for an <a> run, '' otherwise
  end;

Od poteka sidrnega elementa do pripombe povezave PDF

Hiperpovezava v dokončanem PDF ni del vsebine strani. Je ločen objekt - pripomba Povezave, opisana v ISO 32000-1 §12.5.6.5. Pripomba ima /Rect, ki določa klikabilni pravokotnik na strani, in dejanje, ki se sproži ob kliku na pravokotnik. Za zunanjo povezavo je dejanje URI dejanje: /S /URI s ciljnim naslovom kot nizom /URI. Vidno besedilo pod njo je navadna vsebina strani; pripomba je nevidno vroče področje, položeno nad njim

Pot sploščevanja natanko sledi temu modelu. Ko potek nosi Href, HotPDF najprej nariše oblikovano besedilo, nato pa zgradi pripombo Povezave nad oknom poteka. Javna vstopna točka za to pripombo je metoda strani AddURILink, ki ustvari objekt /Type /Annot /Subtype /Link z dejanjem /URI in vrne slovar pripombo. Njen pravokotnik je izmerjeno okno poteka, prevedeno iz lokalnih koordinat elementa draw v koordinate strani. Rezultat je povezava, ki pade natanko na besedilo sidrnega elementa in nikamor drugam

// The same public API the flatten path uses for each anchor run. It produces
// an ISO 32000-1 12.5.6.5 Link annotation: /Subtype /Link with a /URI action
// over the given rectangle. The optional description fills /Contents so a
// screen reader can announce the target.
var
  LinkRect: TRect;
  Annot: THPDFDictionaryObject;
begin
  LinkRect := Rect(72, 690, 268, 706);  // page-space hit box for the run
  Annot := Pdf.CurrentPage.AddURILink(LinkRect,
    'https://www.example.gov/appeal', 'File an appeal online');
end;

Zakaj mora vroče področje izhajati iz izmerjenih širin

Skušnjava je, da bi lokacijo povezave poiskali z iskanjem vidnega besedila na strani in risanjem pravokotnika okrog tega, kar najdemo. To ne deluje in razlog je temeljni za to, kako je sploščeno besedilo shranjeno. Oblikovani poteki so naslikani z vgrajenimi podnabori pisav. Podnabojna pisava preštevilči glifi, ki jih ohrani, zato tok vsebine strani vsebuje šestnajstiške kode CID in ne izvirnih kod znakov. Bajti na strani niso črke, ki jih bere človek, in niso iskalni kot besedilo. Iskanje napisa sidra ne najde ničesar, ker ta napis nikjer v toku ne obstaja kot dobesedno besedilo

Edino zanesljivo sidro za pravokotnik je geometrija, ki jo je prehod postavitve že ustvaril. Odmik in izmerjena širina vsakega poteka sta bila izračunana med razporejanjem vrstice, preden je bil katerikoli glif preštevilčen, in opisujeta, kje bo besedilo fizično prikazano. HotPDF zato vzame pravokotnik povezave neposredno iz postavljenega okna poteka namesto iz katerega koli iskanja besedila. Ker je meritev uporabila pisavo za izrisovanje, je okno pravilno ne glede na podbor. Geometrija preživi kodiranje; besedilo ne. To je cel argument za pozicioniranje po izmerjeni širini in razlog, zakaj sploščevalnik, ki poskuša naknadno dodati povezave z iskanjem besedila, ustvari vroča področja, ki se zamikajo ali izginejo

Poganjanje sploščevanja iz vaše kode

Za PDF, ki že vsebuje paket XFA, je vstopna točka FlattenLoadedXFA. Naložite dokument, pokličite metodo in shranite rezultat. Parameter Editable odloča, kaj se zgodi s polji obrazca: posredujte True, da jih ohranite kot izpolnljive gradnike AcroForm, ali False, da vsak gradnik označite samo za branje, tako da je izhod zamrznjen zapis. Bloki bogatega besedila z njihovimi oblikovanimi poteki in pripombami povezav so ustvarjeni v vsakem primeru. Funkcija vrne število gradnikov, ki jih je oddala

var
  Pdf: THotPDF;
  Emitted, i: Integer;
begin
  Pdf := THotPDF.Create(nil);
  try
    Pdf.LoadFromFile('xfa_appeal_form.pdf');
    // True keeps fields fillable; False freezes them read-only.
    Emitted := Pdf.FlattenLoadedXFA(True);

    // Anything the engine could not map is reported, not raised.
    for i := 0 to Pdf.XFAFlattenWarnings.Count - 1 do
      Writeln('XFA warning: ', Pdf.XFAFlattenWarnings[i]);

    Pdf.SaveLoadedDocument('appeal_form_flat.pdf');
    Writeln('Widgets emitted: ', Emitted);
  finally
    Pdf.Free;
  end;
end;

Po klicu vedno preberite XFAFlattenWarnings. Seznam je počiščen na začetku vsakega sploščevanja in za vsak element, ki ga stroj ni mogel izrisati, nabira vrstico: nepodprta vrsta polja, slika draw, ki se ni dala dekodirati, blok exData brez uporabnih potekov. Nobena od teh ne sproži izjeme, zato prazen seznam opozoril dokazuje, da se je vse preslikalo, neprazen pa vam pove točno, katera izvirna mesta je treba pregledati. Ko imate surove bajte XFA kot bajte XDP namesto naloženega PDF, sprejme sorodna metoda ApplyXFAAsAcroForm te bajte neposredno in deli enako kodno pot ter enako vedenje opozoril. Komplementarna metoda AddXFAPacket gre v nasprotno smer in vgradi paket XFA v dokument, ki ga gradite

Potrjevanje rezultata v bralniku

Odprite sploščeno datoteko v Acrobatu ali katerem koli sodobnem bralniku in preverite dve stvari. Najprej, da je bogato besedilo izrisano z ohranjenim oblikovanjem: krepki poteki so krepki, obarvani poteki nosijo svojo barvo, in razponi sedijo v pravem vrstnem redu na vrstici namesto da bi se prekrivali ali tekli čez okno. Drugič, da so hiperpovezave žive. Premaknite se nad sidrni element in vrstica za stanje mora pokazati ciljni naslov; kliknite in dejanje URI ga mora odpreti. Z inšpektorjem pripomb bralnika preverite, da je vsaka prava pripomba /Link, katere /Rect se tesno drži besedila sidrnega elementa, ležeče nad vsebino, ki je zdaj navadni naslikani glifi namesto z XFA izrisane oblike. Ta kombinacija - oblikovano statično besedilo in prave pripombe Povezave na pravih pravokotnikih - je tisto, kar naredi sploščen dokument preživeti pogone XFA, ki jih ne potrebuje več

Sploščevanje samih polj - besedilnih polj, potrditvenih polj in seznamov izbire, ki obkrožajo to bogato besedilo - je opisano v našem vodičem po sploščevanju obrazcev XFA v gradnike AcroForm. Za širšo zgodbo o ročnem gradnji in postavljanju pripomb Povezave, zunaj tistih, ki jih ustvari pot sploščevanja, si oglejte delo s pripombami PDF v HotPDF. Oba gradita na enakem modelu pripomb in obrazcev, ki je na voljo z komponento HotPDF za Delphi in C++Builder