XFA, архитектурата на XML форми (XML Forms Architecture), е остаряла (deprecated). ISO 32000-1 я носи в §12.7 с бележката, че е премахната от PDF 2.0, а модерните четци спират своите XFA машини една по една. Нищо от това обаче не е изпразнило архивите. Държавни входящи форми, застрахователни заявления и банкови извлечения са създавани как XFA в продължение на почти две десетилетия и тези файлове все още пристигат в пощенските кутии и конвейерите за документи днес. Когато четецът, който ги е рендерирал, спре да го прави, формата се превръща в празна страница с плейсхолдър "моля, отворете в друг четец". Трайното решение е да изгладите (flatten) XFA в статично PDF съдържание, което всеки четец може да изрисува.
Трудната част на това изглаждане не са самите полета. Текстовите полета и квадратчетата за отметка се съпоставят с графичните компоненти на AcroForm доста чисто. Трудната част е форматираният текст (rich text), който XFA съхранява в елемент за рисуване в блок <exData contentType="text/html">. Този блок е подмножество на HTML с вградени стилове и често с котви (anchors/връзки). Пренасянето му върху страницата означава възпроизвеждане както на стилизирания текст, така и на активните хипервръзки, а хипервръзките са мястото, където повечето реализации тихо се отказват.
Как всъщност изглежда форматираният текст в XFA
Тялото на exData е малка част от XHTML. Параграфът е <p>; стилизираният диапазон от знаци е <span> със собствен вграден CSS за дебелина, наклон, цвят и размер; а хипервръзката е <a href="...">, обвиваща своя видим текст. Един ред може да съдържа няколко спана (spans) подред, всеки с различен стил, като един от тях може да бъде котва (връзка). Стилизирането не е декорация, която може да бъде отхвърлена. Клауза, рендерирана в получервен червен цвят, защото е правно предупреждение, трябва да остане получерна и червена след изглаждането, иначе изгладеният документ представя невярно оригинала.
Така че машината за изглаждане не може да третира блока като един низ. Тя трябва да обходи вградената структура, да разреши ефективния стил на всяко изпълнение (run), като насложи вградения CSS на спана върху базовия шрифт на елемента за рисуване, и да разположи изпълненията едно след друго по продължение на реда. HotPDF моделира всеки от тези разположени фрагменти като вътрешен запис TXFARichRun. Записът носи текста на изпълнението, неговия разрешен стил, измерената му кутия и, за котва, Href, към който тя сочи.
Разполагане на изпълненията от ляво на дясно
Позиционирането е мястото, където форматираният текст спира да бъде проблем на парсирането и става проблем на набор на текст (typesetting). Изпълненията споделят един ред, така че всяко изпълнение започва там, където е завършило предишното. Няма маркиране, което да записва тези позиции; те трябва да бъдат измерени. Вътрешната подпрограма на машината LayoutRichText измерва всяко изпълнение със същите метрики на шрифта, които по-късно ще го нарисуват, след което задава хоризонталното отместване на изпълнението на текущата сума от ширините на всички предходни изпълнения. Изпълнение едно започва в началото на кутията за рисуване, изпълнение две започва при ширината на изпълнение едно, изпълнение три при комбинираната ширина на първите две и т.н. по продължение на реда.
Ето защо подравняването на шрифта за измерване е толкова важно. Проходът по оформление (layout pass) измерва напредъка на ширината (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 annotation), описана в 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;
Защо зоната на попадане (hit box) трябва да идва от измерени ширини
Изкушаващо е да си представим откриването на връзката чрез търсене на страницата за нейния видим текст и чертане на правоъгълника около намереното. Това обаче не работи и причината е фундаментална за начина, по който се съхранява изгладеният текст. Стилизираните изпълнения се рисуват с вградени подмножества от шрифтове (subset fonts). Шрифтовото подмножество преномерира глифовете, които запазва, така че потокът от съдържание на страницата съдържа шестнадесетични CID кодове, а не оригиналните кодове на знаците. Байтовете на страницата не са буквите, които чете човек, и не могат да се търсят как текст. Търсенето на надписа на котвата не намира нищо, защото този надпис не съществува как буквален текст никъде в потока.
Единствената надеждна котва за правоъгълника е геометрията, която проходът по оформление вече е произвел. Отместването и измерената ширина на всяко изпълнение са изчислени по време на изтичането на реда, преди който и да е глиф да бъде преномериран, и те описват къде физически ще се появи текстът. Поради това HotPDF взема правоъгълника на връзката директно от разположената кутия на изпълнението, а не от търсене на текст. Тъй като измерването е използвало рендиращия шрифт, кутията е правилна независимо от подмножествата. Геометрията оцелява при кодирането; текстът - не. Това е целият аргумент за позициониране по измерена ширина и затова инструмент за изглаждане, който се опитва да монтира връзки по-късно чрез текстово търсене, произвежда зони на попадане, които се отклоняват или изчезват.
Управление на изглаждането от вашия код
За PDF, който вече съдържа XFA пакет, входната точка е FlattenLoadedXFA. Заредете документа, извикайте метода и запишете резултата. Параметърът Editable решава какво се случва с полевата на формата: предайте True, за да ги запазите като попълваеми графични компоненти (widgets) на 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.