XFA, XML Forms Architecture, er avviklet. ISO 32000-1 har den i §12.7 med en merknad om at den er fjernet fra PDF 2.0, og moderne lesere fjerner sine XFA-motorer én etter én. Ingenting av dette har tømt arkivene. Offentlige inntaksskjemaer, forsikringssøknader og kontoutskrifter ble forfattet som XFA i store deler av to tiår, og disse filene ankommer fortsatt i innbokser og dokumentrørledninger i dag. Når leseren som pleide å gjengi dem slutter å gjøre det, blir skjemaet til en blank side med en "vennligst åpne i en annen leser"-plassholder. Den varige løsningen er å flattrykke (flatten) XFA til statisk PDF-innhold som enhver leser kan tegne
Den vanskelige delen av den flattrykkingen er ikke feltene. Tekstbokser og avmerkingsbokser kartlegges til AcroForm-kontroller (widgets) rent nok. Den vanskelige delen er den rike teksten som XFA lagrer inne i et tegnelement, i en <exData contentType="text/html">-blokk. Den blokken er et HTML-undersett med innebygd stiling og, ofte, ankere. Å få det over på siden betyr å reprodusere både den stilete teksten og de levende hyperlenkene, og hyperlenkene er der de fleste implementasjoner i stillhet gir opp
Hvordan XFA rik tekst faktisk ser ut
En exData-kropp er en liten bit av XHTML. Et avsnitt er en <p>; et stilet spenn av tegn er en <span> med sin egen innebygde CSS for tykkelse, holdning, farge og størrelse; og en hyperlenke er en <a href="..."> som omslutter den synlige teksten. En enkelt linje kan inneholde flere spenn på rad, hver med forskjellig stiling, og en av dem kan være et anker. Stilingen er ikke dekorasjon som kan droppes. En klausul gjengitt i fet rød fordi den er en juridisk advarsel, må forbli fet og rød etter flattrykking, ellers feilrepresenterer det flattrykte dokumentet originalen
Så flattrykkingsmotoren kan ikke behandle blokken som én streng. Den må gå gjennom den innebygde strukturen, løse hver kjørings effektive stil ved å legge spennets innebygde CSS over tegnelementets basisskrifttype, og legge kjøringene ut etter hverandre over linjen. HotPDF modellerer hvert av disse utlagte fragmentene som en intern TXFARichRun-post. Posten bærer kjøringens tekst, dens løste stil, dens målte boks, og, for et anker, den Href-en den peker på
Legge ut kjøringene fra venstre til høyre
Posisjonering er der rik tekst slutter å være et parseringsproblem og blir et typografiproblem. Kjøringene deler en linje, så hver kjøring begynner der den forrige sluttet. Det finnes ingen markering som registrerer disse posisjonene; de må måles. Motorens interne LayoutRichText-rutine måler hver kjøring med de samme skriftmetrikkene som senere vil tegne den, og setter deretter kjøringens horisontale forskyvning til den løpende summen av alle tidligere kjøringsbredder. Kjøring én starter ved tegneoksens opprinnelse, kjøring to starter på bredden til kjøring én, kjøring tre på den kombinerte bredden til de to første, og så videre over linjen
Det er grunnen til at skriftjustering ved måling betyr så mye. Layout-gjennomgangen måler fremrykk; en separat gjengivelsesgjennomgang tegner glyfer. Hvis de to gjennomgangene er uenige om skrifttypen, vil ikke boksene layouten beregnet, sitte under glyfene gjengiveren tegner. HotPDF holder dem i takt ved å kartlegge hver kjørings løste stil til en skriftspesifikasjon, gjennom den interne RunStyleToFontSpec-hjelperen, som matcher gjengiverens egne standarder av Arial på 10 punkter. Det målte fremrykket og den tegnede teksten stemmer deretter overens, og en kjørings beregnede boks dekker virkelig tegnene en leser ser
// 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;
Fra en ankerkjøring til en PDF-lenke-annotasjon
En hyperlenke i en ferdig PDF er ikke en del av sideinnholdet. Det er et separat objekt, en lenke-annotasjon (Link annotation), beskrevet i ISO 32000-1 §12.5.6.5. Annotasjonen har en /Rect som definerer det klikkbare rektangelet på siden og en handling som utløses når rektangelet klikkes. For en ekstern lenke er handlingen en URI-handling: /S /URI med måladressen som dens /URI-streng. Den synlige teksten under er vanlig sideinnhold; annotasjonen er den usynlige varme sonen lagt over den
Flattrykkingsbanen følger nøyaktig denne modellen. Når en kjøring bærer en Href, tegner HotPDF først den stilete teksten, og bygger deretter en lenke-annotasjon over kjøringens boks. Det offentlige inngangspunktet for den annotasjonen er sidemetoden AddURILink, som oppretter /Type /Annot /Subtype /Link-objektet med en /URI-handling og returnerer annotasjonsordboken. Rektangelet er kjøringens målte boks, oversatt fra tegnelementets lokale koordinater til sidekoordinater. Resultatet er en lenke som lander presist på ankerteksten og ingen andre steder
// 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;
Hvorfor treffboksen må komme fra målte bredder
Det er fristende å se for seg å finne lenken ved å søke på siden etter dens synlige tekst og tegne rektangelet rundt det som blir funnet. Det fungerer ikke, og årsaken er fundamental for hvordan flattrykt tekst lagres. De stilete kjøringene males med innebygde undersett-skrifttyper. En undersett-skrifttype (subset font) nummererer om glyfene den beholder, så innholdsstrømmen på siden inneholder heksadesimale CID-koder, ikke de opprinnelige tegnkodene. Bytene på siden er ikke bokstavene et menneske leser, og de er ikke søkbare som tekst. Et søk etter ankerets bildetekst finner ingenting, fordi den bildeteksten ikke finnes som bokstavelig tekst noe sted i strømmen
Det eneste pålitelige ankeret for rektangelet er geometrien layout-gjennomgangen allerede produserte. Hver kjørings forskyvning og målte bredde ble beregnet mens linjen fløt, før noen glyf ble nummerert om, og de beskriver hvor teksten fysisk vil vises. HotPDF tar derfor lenkerektangelet rett fra kjøringens nedlagte boks i stedet for fra noe tekstoppslag. Fordi målingen brukte gjengivelses-skrifttypen, er boksen riktig uavhengig av undersett (subsetting). Geometri overlever kodingen; det gjør ikke tekst. Det er hele argumentet for posisjonering med målt bredde, og det er grunnen til at en flattrykker som prøver å ettermontere lenker ved tekstsøk, produserer treffsoner som driver eller forsvinner
Kjøre flattrykkingen fra koden din
For en PDF som allerede inneholder en XFA-pakke, er inngangspunktet FlattenLoadedXFA. Last inn dokumentet, kall metoden og lagre resultatet. Editable-parameteren bestemmer hva som skjer med skjemafeltene: send True for å beholde dem som utfyllbare AcroForm-kontroller (widgets), eller False for å markere hver kontroll som skrivebeskyttet slik at utdataene er en frossen post. De rike tegneblokkene, med sine stilete kjøringer og lenke-annotasjoner, produseres uansett. Funksjonen returnerer antall kontroller den sendte ut
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;
Les alltid XFAFlattenWarnings etter kallet. Listen tømmes i starten av hver flattrykking og samler en linje for hvert element motoren avviste å gjengi: en ikke-støttet felttype, et tegnebilde som ikke ville dekode, en exData-blokk uten brukbare spenn. Ingen av disse utløser et unntak, så en tom advarselsliste er beviset på at alt ble kartlagt, og en ikke-tom forteller deg nøyaktig hvilke originaler du bør inspisere. Når du har rå XFA som XDP-byter i stedet for en innlastet PDF, tar søskenmetoden ApplyXFAAsAcroForm disse bytene direkte og deler den samme kodebanen og samme advarselsatferd. Den komplementære AddXFAPacket-metoden går den andre veien, og bygger inn en XFA-pakke i et dokument du bygger
Bekrefte resultatet i en leser
Åpne den flattrykte filen i Acrobat, eller en hvilken som helst gjeldende leser, og sjekk to ting. For det første at den rike teksten ble gjengitt med stilingen intakt: de fete kjøringene er fete, de fargede kjøringene bærer fargen sin, og spennene sitter i riktig rekkefølge på linjen i stedet for å overlappe eller renne utenfor boksen. For det andre, at hyperlenkene er levende. Hold pekeren over et anker, og statuslinjen skal vise måladressen; klikk på den, og URI-handlingen skal åpne den. Bruk leserens annotasjonsinspektør for å bekrefte at hver er en ekte /Link-annotasjon hvis /Rect omfavner ankerteksten, sittende over innhold som nå er rene malte glyfer i stedet for skjema-gjengitt XFA. Den kombinasjonen, stilet statisk tekst pluss ekte lenke-annotasjoner på de riktige rektanglene, er det som får det flattrykte dokumentet til å overleve XFA-motorene det ikke lenger trenger
Å flattrykke selve feltene, tekstboksene, avmerkingsboksene og valglistene som omgir denne rike teksten, dekkes i vår gjennomgang av flattrykking av XFA-skjemaer til AcroForm-kontroller. For den bredere historien om å bygge og plassere lenke-annotasjoner for hånd, utover de som flattrykkingsbanen genererer, se arbeid med PDF-annotasjoner i HotPDF. Begge bygger på den samme annotasjons- og skjemamodellen som følger med HotPDF Component for Delphi og C++Builder