Tehnički članak

Poravnanje XFA hiperlinkova obogacenog teksta u PDF linkove u Delphi-ju

XFA, odnosno XML Forms Architecture, je zastarela. ISO 32000-1 je navodi u paragrafu 12.7 sa napomenom da je uklonjena iz PDF 2.0 standarda, a moderni citaci gase svoje XFA mehanizme jedan po jedan. Nista od toga nije ispraznilo arhive. Vladini obrasci za unos podataka, prijave osiguranja i bankovni izvodi pisani su kao XFA tokom boljeg dela dve decenije, i te datoteke i danas stizu u sanducice i procese obrade dokumenata. Kada citac koji ih je ranije renderovao prestane to da radi, obrazac se pretvara u praznu stranicu sa rezervisanim mestom "molimo otvorite u drugom citacu". Trajno resenje je poravnanje XFA u staticni PDF sadrzaj koji svaki citac moze da iscrta

Tezan deo tog poravnanja nisu polja. Polja za tekst i polja za potvrdu se sasvim cisto mapiraju na AcroForm vidzete. Tezan deo je obogaceni tekst koji XFA cuva unutar elementa za crtanje, u bloku <exData contentType="text/html">. Taj blok je podskup HTML-a sa ugradenim stilovima i, cesto, sidrima. Prenosenje toga na stranicu znaci reprodukciju i stilizovanog teksta i aktivnih hiperlinkova, a hiperlinkovi su tacka na kojoj vecina implementacija tihо odustaje

Kako XFA obogaceni tekst zapravo izgleda

Telo exData je mali deo XHTML-a. Pasus je <p>; stilizovani opseg karaktera je <span> sa sopstvenim ugradenim CSS-om za tezinu, stil, boju i velicinu; a hiperlink je <a href="..."> koji obmotava svoj vidljivi tekst. Jedna linija moze da sadrzi nekoliko opsega zaredom, svaki sa razlicitim stilom, a jedan od njih moze biti sidro. Stilizovanje nije ukras koji se moze odbaciti. Klauzula prikazana podebljano crvenom bojom jer predstavlja pravno upozorenje mora ostati podebljana i crvena i nakon poravnanja, inace poravnati dokument pogresno predstavlja original

Dakle, mehanizam za poravnanje ne moze da tretira blok kao jedan tekstualni niz. On mora da prode kroz unutrasnju strukturu, resi efektivni stil svake staze slaganjem ugradenog CSS-a opsega preko osnovnog fonta elementa crtanja i rasporedi staze jednu za drugom duz linije. HotPDF modelira svaki od ovih rasporedenih fragmenata kao interni zapis TXFARichRun. Zapis nosi tekst staze, njen reseni stil, njenu izmerenu kutiju i, za sidro, Href na koji ukazuje

Rasporedivanje staza sleva nadesno

Pozicioniranje je faza u kojoj obogaceni tekst prestaje da bude problem parsiranja i postaje problem slaganja teksta. Staze dele liniju, tako da svaka staza pocinje tamo gde se prethodna zavrsila. Ne postoji oznaka koja belezi те pozicije; one se moraju izmeriti. Interna rutina mehanizma LayoutRichText meri svaku stazu istim metrikama fonta kojima ce je kasnije oslikati, a zatim postavlja horizontalni pomak staze na tekuci zbir svih prethodnih sirina staza. Prva staza pocinje na pocetku kutije za crtanje, druga staza pocinje na sirini prve staze, treca na kombinovanoj sirini prve dve, i tako dalje duz linije

Zbog toga je poravnanje fonta pri merenju veoma vazno. Prolaz rasporeda meri pomake; odvojeni prolaz renderovanja crta glifove. Ako se ova dva prolaza ne slazu oko fonta, kutije koje je raspored izracunao nece se nalaziti ispod glifova koje renderer crta. HotPDF ih drzi usklade enim tako sto mapira reseni stil svake staze na specifikaciju fonta, kroz interni pomocnik RunStyleToFontSpec, koji odgovara sopstvenim podrazumevanim vrednostima renderera za Arial na 10 tacaka. Izmereni pomak i nacrtani tekst se tada slazu, a izracunata kutija staze zaista pokriva karaktere koje citaoc 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 staze sidra do PDF Link anotacije

Hiperlink u gotovom PDF-u nije deo sadrzaja stranice. To je poseban objekat, Link anotacija, opisan u standardu ISO 32000-1 paragraf 12.5.6.5. Anotacija ima /Rect koji definise pravougaonik na stranici na koji se moze kliknuti i akciju koja se pokrace kada se klikne na pravougaonik. Za spoljni link akcija je URI akcija: /S /URI sa ciljnom adresom kao tekstualnim nizom /URI. Vidljivi tekst ispod je obican sadrzaj stranice; anotacija je nevidljiva aktivna zona postavljena preko njega

Putanja poravnanja prati upravo ovaj model. Kada staza nosi Href, HotPDF prvo crta stilizovani tekst, a zatim gradi Link anotaciju preko kutije te staze. Javna pristupna tacka za tu anotaciju je metoda stranice AddURILink, koja kreira objekat /Type /Annot /Subtype /Link sa akcijom /URI i vraca recnik anotacije. Njen pravougaonik je izmerena kutija staze, prevedena iz lokalnih koordinata elementa crtanja u koordinate stranice. Rezultat je link koji lezi precizno na tekstu sidra i nigde drugde

// 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;

Zasto kutija osetljiva na klik mora dolaziti iz izmerenih sirina

Primamljivo je zamisliti lociranje linka pretrazivanjem stranice za njegovim vidljivim tekstom i crtanjem pravougaonika oko onoga sto se pronade. To ne radi, a razlog je fundamentalan za nacin na koji se poravnati tekst cuva. Stilizovane staze se iscrtavaju sa ugradenim podskupovima fontova. Podskup fonta ponovo numerise glifove koje zadrzava, tako da tok sadrzaja stranice sadrzi heksadecimalne CID kodove, a ne originalne kodove karaktera. Bajtovi na stranici nisu slova koja covek cita i ne mogu se pretrazivati kao tekst. Pretraga za natpisom sidra ne pronalazi nista, jer taj natpis ne postoji kao doslovni tekst nigde u toku

Jedini pouzdan oslonac za pravougaonik je geometrija koju je prolaz rasporeda vec proizveo. Pomak i izmerena sirina svake staze su izracunati tokom protoka linije, pre nego sto je bilo koji glif ponovo numerisan, i oni opisuju gde ce se tekst fizicki pojaviti. HotPDF stoga uzima pravougaonik linka direktno iz postavljene kutije staze, a ne iz bilo kakve pretrage teksta. Posto je merenje koristilo font renderovanja, kutija je ispravna bez obzira na podskupove. Geometrija prezivljava kodiranje; tekst ne. To je ceo argument za pozicioniranje na osnovu izmerene sirine, i to je razlog zasto program za poravnanje koji pokusava naknadno da ubaci linkove pretrazivanjem teksta proizvodi aktivne zone koje se pomeraju ili nestaju

Pokretanje poravnanja iz vaseg koda

Za PDF koji vec sadrzi XFA paket, pristupna tacka je FlattenLoadedXFA. Ucitajte dokument, pozovite metodu i sacuvajte rezultat. Parametar Editable odlucuje sta se desava sa poljima obrasca: prosledite True da biste ih zadrzali kao AcroForm vidzete koji se mogu popuniti, ili False da biste svaki vidzet oznacili samo za citanje, tako da izlaz bude zamrznuti zapis. Blokovi za crtanje obogacenog teksta, sa svojim stilizovanim stazama i link anotacijama, proizvode se u oba slucaja. Funkcija vraca broj vidzeta koje je emitovala

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;

Uvek procitajte XFAFlattenWarnings nakon poziva. Lista se prazni na pocetku svakog poravnanja i akumulira liniju za svaki element koji mehanizam odbije da renderuje: nepodrzanu vrstu polja, sliku za crtanje koja se ne moze dekodirati, exData blok bez upotrebljivih opsega. Nista od toga ne izaziva izuzetak, tako da je prazna lista upozorenja dokaz da je sve mapirano, a lista koja nije prazna govori vam tacno koje originale treba da pregledate. Kada drzite sirovi XFA kao XDP bajtove, a ne kao ucitani PDF, srodna metoda ApplyXFAAsAcroForm uzima te bajtove direktno i deli istu putanju koda i isto ponasanje upozorenja. Komplementarna metoda AddXFAPacket ide u drugom smeru, ugradujuci XFA paket u dokument koji gradite

Potvrda rezultata u citacu

Otvorite poravnatu datoteku u Acrobat-u ili bilo kom trenutnom citacu i proverite dve stvari. Prvo, da je obogaceni tekst renderovan sa netaknutim stilom: podebljane staze su podebljane, obojene staze nose svoju boju, a opsezi leze u ispravnom redosledu na liniji umesto da se preklapaju ili izlaze iz kutije. Drugo, da su hiperlinkovi aktivni. Predite misem preko sidra i statusna traka bi trebalo da prikaze ciljnu adresu; kliknite na nju i URI akcija bi trebalo da je otvori. Koristite inspektor anotacija u citacu da potvrdite da je svaka od njih prava /Link anotacija ciji pravougaonik /Rect grli tekst sidra, lezeci preko sadrzaja koji su sada obicni nacrtani glifovi, a ne XFA renderovan iz obrasca. Ta kombinacija, stilizovani staticni tekst plus stvarne Link anotacije na ispravnim pravougaonicima, jeste ono sto cini da poravnati dokument nadezivi XFA mehanizme koji mu vise nisu potrebni

Poravnanje samih polja, tekstualnih kutija, polja za potvrdu i lista izbora koji okruzuju ovaj obogaceni tekst, pokriveno je u nasem vodicu o poravnanju XFA obrazaca u AcroForm vidzete. Za siru pricu o rucnoj izgradnji i postavljanju Link anotacija, izvan onih koje stvara putanja poravnanja, pogledajte rad sa PDF anotacijama u HotPDF-u. Oba se oslanjaju na isti model anotacija i obrazaca koji se isporucuje sa HotPDF Component za Delphi i C++Builder