XFA, XML архитектурата за формуляри (XML Forms Architecture), е отхвърлена. ISO 32000-1 я носи в §12.7 с бележката, че е премахната от PDF 2.0, а съвременните четци се отказват от своите XFA двигатели един по един. Нищо от това не е изпразнило архивите. Държавни формуляри за прием, застрахователни заявления и банкови извлечения са били създавани като XFA през по-голямата част от две десетилетия и тези файлове все още пристигат във входящите кутии и конвейерите за документи днес. Когато четецът, който ги е рендирал, спре да го прави, формулярът се превръща в празна страница с контейнер „моля, отворете в друг четец“. Трайното решение е да сплескате (flatten) XFA в статично PDF съдържание, което всеки четец може да нарисува
Трудната част от това сплескване не са полетата. Текстовите полета и полетата за отметка се картографират върху уиджети на AcroForm достатъчно чисто. Трудната част е богатият текст, който XFA съхранява вътре в елемент за чертане, в блок <exData contentType="text/html">. Този блок е подмножество на HTML с вградено (inline) стилизиране и често с котви (anchors). Поставянето му на страницата означава възпроизвеждане както на стилизирания текст, така и на активните хипервръзки, а хипервръзките са мястото, където повечето реализации тихо се отказват
Как всъщност изглежда богатият текст на XFA
Тялото на exData е малък отрязък от XHTML. Параграф е <p>; стилизиран обхват от знаци е <span> със собствен вграден CSS за плътност, наклон, цвят и размер; а хипервръзка е <a href="...">, обвиваща своя видим текст. Един ред може да съдържа няколко обхвата подред, всеки с различно стилизиране, и един от тях може да бъде котва. Стилизирането не е декорация, която може да бъде пропусната. Клауза, рендирана в получер червен цвят, защото е правно предупреждение, трябва да остане получерна и червена след сплескването, в противен случай сплесканият документ представя погрешно оригинала
Така че двигателят за сплескване не може да третира блока като един низ. Той трябва да обходи вградената структура, да разреши ефективния стил на всяка поредица чрез наслояване на вградения CSS на обхвата върху базовия шрифт на елемента за чертане и да разположи поредиците една след друга по линията. HotPDF моделира всеки от тези разположени фрагменти като вътрешен запис TXFARichRun. Записът носи текста на поредицата, нейния разрешен стил, нейната измерена кутия и, за котва, Href, към който сочи
Разполагане на поредиците отляво надясно
Позиционирането е мястото, където богатият текст престава да бъде проблем на анализиране (parsing) и става проблем на набиране. Поредиците споделят един ред, така че всяка поредица започва там, където е завършила предишната. Няма маркер, който записва тези позиции; те трябва да бъдат измерени. Вътрешната рутина LayoutRichText на двигателя измерва всяка поредица със същите метрики на шрифта, които по-късно ще я нарисуват, след което задава хоризонталното отместване на поредицата към текущата сума от всички предходни ширини на поредици. Поредица едно започва от началото на кутията за чертане, поредица две започва при ширината на поредица едно, поредица три при комбинираната ширина на първите две и така нататък по линията
Ето защо подравняването на шрифта за измерване има толкова голямо значение. Проходът за оформление измерва авансите (advances); отделен проход за рендиране рисува глифове. Ако тези два прохода не са съгласни относно шрифта, кутиите, които оформлението е изчислило, няма да седят под глифовете, които рендерерът рисува. HotPDF ги поддържа в синхрон чрез картографиране на разрешения стил на всяка поредица към спецификация на шрифт, чрез вътрешния помощник RunStyleToFontSpec, която съвпада със собствените настройки по подразбиране на рендерера за Arial при 10 пункта. Измереният аванс и начертаният текст след това съвпадат и изчислената кутия на поредицата действително покрива знаците, които читателят вижда
// 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;
От котва към PDF Link анотация
Една хипервръзка в завършен PDF не е част от съдържанието на страницата. Тя е отделен обект, анотация Link, описана в ISO 32000-1 §12.5.6.5. Анотацията има /Rect, който дефинира правоъгълника, върху който може да се щракне на страницата, и действие, което се задейства при щракване върху правоъгълника. За външна връзка действието е URI действие: /S /URI с целевия адрес като негов /URI низ. Видимият текст отдолу е обикновено съдържание на страницата; анотацията е невидимата гореща зона, поставена върху нея
Пътят за сплескване следва точно този модел. Когато дадена поредица носи Href, HotPDF първо начертава стилизирания текст, след което изгражда анотация Link върху кутията на поредицата. Публичната входна точка за тази анотация е методът на страницата AddURILink, който създава обекта /Type /Annot /Subtype /Link с /URI действие и връща речника на анотацията. Нейният правоъгълник е измерената кутия на поредицата, преведена от локалните координати на елемента за чертане в координати на страницата. Резултатът е връзка, която попада точно върху текста на котвата и никъде другаде
// 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;
Защо кутията за попадение трябва да идва от измерени ширини
Изкушаващо е да си представим локализирането на връзката чрез търсене в страницата за нейния видим текст и изчертаване на правоъгълника около това, което бъде намерено. Това не работи и причината е фундаментална за това как се съхранява сплесканият текст. Стилизираните поредици са нарисувани с вградени подмножества шрифтове (subset fonts). Един подшрифт преномерира глифовете, които запазва, така че потокът от съдържание на страницата съдържа шестнадесетични CID кодове, а не оригиналните кодове на знаци. Байтовете на страницата не са буквите, които човек чете, и те не могат да се търсят като текст. Търсенето на надписа на котвата не намира нищо, тъй като този надпис не съществува като буквален текст никъде в потока
Единствената надеждна котва за правоъгълника е геометрията, която проходът за оформление вече е произвел. Отместването и измерената ширина на всяка поредица са били изчислени по време на изливането на реда, преди всеки глиф да бъде преномериран, и те описват къде физически ще се появи текстът. Следователно HotPDF взема правоъгълника на връзката направо от разположената кутия на поредицата, а не от някакво търсене на текст. Тъй като измерването използва шрифта за рендиране, кутията е правилна независимо от подмножеството. Геометрията оцелява след кодирането; текстът – не. Това е целият аргумент за позициониране по измерена ширина и това е причината инструмент за сплескване, който се опитва да преоборудва връзки чрез търсене на текст, да произвежда зони за попадение, които се изместват или изчезват
Управление на сплескването от вашия код
За PDF, който вече съдържа XFA пакет, входната точка е FlattenLoadedXFA. Заредете документа, извикайте метода и запазете резултата. Параметърът Editable решава какво се случва с полетата на формуляра: подайте True, за да ги запазите като попълваеми уиджети на AcroForm, или False, за да маркирате всеки уиджет като само за четене, така че изходът да е замразен запис. Блоковете за чертане на богат текст, с техните стилизирани поредици и анотации за връзки, се произвеждат и в двата случая. Функцията връща броя на уиджетите, които е излъчила
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;
Винаги четете XFAFlattenWarnings след извикването. Списъкът се изчиства в началото на всяко сплескване и натрупва ред за всеки елемент, който двигателят е отказал да рендира: неподдържан вид поле, изображение за чертане, което не се декодира, блок exData без използваеми обхвати. Нито едно от тях не предизвиква изключение, така че празният списък с предупреждения е вашето доказателство, че всичко е картографирано, а непразният ви казва точно кои оригинали да инспектирате. Когато държите суровия XFA като XDP байтове, а не като зареден PDF, методът-близнак ApplyXFAAsAcroForm взема тези байтове директно и споделя същия път на кода и същото поведение на предупрежденията. Допълващият метод AddXFAPacket върви в другата посока, вграждайки XFA пакет в документ, който изграждате
Потвърждаване на резултата в четец
Отворете сплескания файл в Acrobat или всеки текущ четец и проверете две неща. Първо, богатият текст се рендира с непокътнато стилизиране: получерните поредици са получерни, цветните поредици носят своя цвят, а обхватите седят в правилния ред на реда, вместо да се припокриват или да излизат извън кутията. Второ, хипервръзките са активни. Задръжте курсора на мишката върху котва и лентата на състоянието трябва да покаже целевия адрес; щракнете върху него и URI действието трябва да го отвори. Използвайте инспектора за анотации на четеца, за да потвърдите, че всяка от тях е истинска /Link анотация, чийто /Rect прегръща текста на котвата, седейки върху съдържание, което сега са обикновени изрисувани глифове, а не рендиран от формуляр XFA. Тази комбинация, стилизиран статичен текст плюс реални Link анотации върху правилните правоъгълници, е това, което кара сплескания документ да надживее XFA двигателите, от които вече не се нуждае
Сплескването на самите полета, текстовите полета, полетата за отметка и списъците за избор, които заобикалят този богат текст, е разгледано в нашето ръководство за сплескване на XFA формуляри в AcroForm уиджети. За по-широката история на изграждането и поставянето на Link анотации на ръка, отвъд тези, които генерира пътят за сплескване, вижте работа с PDF анотации в HotPDF. И двете се надграждат върху един и същ модел на анотации и формуляри, който се доставя с HotPDF Component за Delphi и C++Builder