XFA หรือ XML Forms Architecture ถูกยกเลิกการใช้งานแล้ว มาตรฐาน ISO 32000-1 ระบุไว้ในหัวข้อ §12.7 พร้อมบันทึกว่ามันจะถูกลบออกจาก PDF 2.0 และโปรแกรมอ่านสมัยใหม่กำลังทยอยนำเอ็นจิน XFA ออกทีละราย แต่นั่นไม่ได้ทำให้เอกสารเก่าๆ ในคลังจัดเก็บหมดไป ฟอร์มการรับข้อมูลของรัฐบาล ใบสมัครประกันภัย และสเตทเมนต์ของธนาคาร ถูกเขียนขึ้นในรูปแบบ XFA เป็นเวลาร่วมสองทศวรรษ และไฟล์เหล่านั้นยังคงส่งเข้ามาในกล่องจดหมายและระบบประมวลผลเอกสารในปัจจุบัน เมื่อโปรแกรมอ่านที่เคยแสดงผลไฟล์เหล่านั้นหยุดทำงาน ฟอร์มจะกลายเป็นหน้าว่างที่มีข้อความระบุว่า "โปรดเปิดในโปรแกรมอ่านอื่น" วิธีแก้ไขที่คงทนคือการแปลงโครงสร้าง XFA ให้เป็นเนื้อหา PDF แบบคงที่ซึ่งโปรแกรมอ่านทุกตัวสามารถวาดได้
ส่วนที่ยากของการแปลงนั่นไม่ใช่เรื่องฟิลด์ กล่องข้อความและกล่องเครื่องหมายสามารถจับคู่เข้ากับวิดเจ็ต AcroForm ได้อย่างสะอาดเพียงพอ ส่วนที่ยากคือข้อความเข้ารหัส (rich text) ที่ XFA จัดเก็บไว้ภายในองค์ประกอบการวาด ในบล็อก <exData contentType="text/html"> บล็อกนั้นคือเซตย่อยของ HTML ที่มีสไตล์แบบอินไลน์และมักมีจุดยึด (anchors) การใส่สิ่งนี้ลงบนหน้ากระดาษหมายถึงการสร้างข้อความที่มีสไตล์และไฮเปอร์ลิงก์ที่ใช้งานได้ขึ้นมาใหม่ และไฮเปอร์ลิงก์คือจุดที่การติดตั้งระบบส่วนใหญ่มักยอมแพ้อย่างเงียบๆ
หน้าตาของข้อความเข้ารหัสแบบ XFA ที่แท้จริง
เนื้อหา exData คือส่วนย่อยขนาดเล็กของ XHTML ย่อหน้าคือ <p> ช่วงของอักขระที่มีสไตล์คือ <span> ที่มีสไตล์ CSS แบบอินไลน์ของตัวเองเพื่อระบุน้ำหนัก ลักษณะ สี และขนาดของอักษร และไฮเปอร์ลิงก์คือ <a href="..."> ที่ครอบคลุมข้อความที่แสดงผล เส้นบรรทัดเดียวสามารถมีส่วนย่อยหลายตัวในแถวเดียวกัน โดยแต่ละส่วนมีสไตล์ที่แตกต่างกัน และหนึ่งในนั้นอาจเป็นจุดยึด สไตล์ไม่ใช่การตกแต่งที่สามารถตัดออกได้ ข้อความกฎหมายที่แสดงเป็นสีแดงหนาเนื่องจากเป็นคำเตือนทางกฎหมายจะต้องยังคงเป็นสีแดงหนาหลังจากแบนราบ มิฉะนั้นเอกสารที่แบนราบจะไม่ตรงกับเอกสารต้นฉบับ
ดังนั้นเอ็นจินการแปลงจึงไม่สามารถจัดการบล็อกนั้นเป็นสตริงเดียวได้ มันจะต้องตรวจสอบโครงสร้างอินไลน์ คลี่คลายสไตล์ที่มีผลจริงของแต่ละช่วงการรันโดยการซ้อนทับสไตล์ CSS แบบอินไลน์ของส่วนย่อยลงบนฟอนต์พื้นฐานขององค์ประกอบการวาด และจัดเรียงการรันทีละตัวตามแนวนอนข้ามบรรทัด HotPDF จำลองข้อมูลที่จัดวางแต่ละส่วนเป็นเรกคอร์ด TXFARichRun ภายใน เรกคอร์ดนี้จะเก็บข้อความของการรัน สไตล์ที่คลี่คลายแล้ว กล่องที่วัดขนาดแล้ว และสำหรับจุดยึดจะเก็บค่า Href ที่ชี้ไป
การจัดวางการรันจากซ้ายไปขวา
การกำหนดตำแหน่งคือจุดที่ข้อความเข้ารหัสเปลี่ยนจากปัญหาการวิเคราะห์ไวยากรณ์ไปเป็นปัญหาการจัดเรียงพิมพ์ การรันแชร์บรรทัดเดียวกัน ดังนั้นแต่ละการรันจะเริ่มในจุดที่การรันก่อนหน้าสิ้นสุดลง ไม่มีมาร์กอัปใดที่บันทึกตำแหน่งเหล่านั้นไว้ ค่าตำแหน่งจึงต้องได้รับการวัดขนาด รูทีน LayoutRichText ภายในของเอ็นจินจะวัดขนาดการรันแต่ละตัวด้วยเมทริกซ์ฟอนต์เดียวกันกับที่จะใช้วาดภาพในภายหลัง จากนั้นตั้งค่าออฟเซ็ตแนวนอนของการรันเป็นผลรวมสะสมของความกว้างของการรันก่อนหน้าทั้งหมด การรันแรกจะเริ่มที่จุดเริ่มต้นของกล่องวาดภาพ การรันที่สองจะเริ่มที่ระดับความกว้างของการรันแรก การรันที่สามจะเริ่มที่ผลรวมความกว้างของสองการรันแรก และทำงานสอดคล้องข้ามบรรทัดไปเรื่อยๆ
นี่คือสาเหตุที่การวัดแนวการวางฟอนต์มีความสำคัญมาก ขั้นตอนการจัดเลย์เอาต์จะวัดระยะขยับไปข้างหน้า ส่วนขั้นตอนการเรนเดอร์แยกต่างหากจะทำหน้าที่วาดสัญลักษณ์ หากสองขั้นตอนการทำงานนี้เห็นไม่ตรงกันเกี่ยวกับฟอนต์ กล่องที่จัดเลย์เอาต์คำนวณไว้จะไม่พอดีภายใต้สัญลักษณ์ที่ตัวแสดงผลวาดภาพออกไป 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
ไฮเปอร์ลิงก์ใน PDF ที่เสร็จสมบูรณ์ไม่ใช่ส่วนหนึ่งของเนื้อหาหน้ากระดาษ มันเป็นวัตถุแยกต่างหากซึ่งเรียกว่า คำอธิบายลิงก์ (Link annotation) ซึ่งมีอธิบายไว้ใน ISO 32000-1 §12.5.6.5 คำอธิบายประกอบจะมี /Rect ที่ระบุสี่เหลี่ยมผืนผ้าที่คลิกได้บนหน้ากระดาษ และการดำเนินการที่จะทำงานเมื่อมีการคลิกสี่เหลี่ยมผืนผ้านั้น สำหรับลิงก์ภายนอก การดำเนินการจะเป็นแบบ URI: /S /URI พร้อมระบุที่อยู่เป้าหมายเป็นสตริง /URI ข้อความที่มองเห็นได้ด้านล่างคือเนื้อหาหน้ากระดาษปกติ ส่วนคำอธิบายประกอบคือพื้นที่คลิกที่มองไม่เห็นซึ่งวางทับอยู่ด้านบน
เส้นทางการแปลงจะดำเนินตามโมเดลนี้อย่างสมบูรณ์ เมื่อการรันมีค่า Href อันดับแรก HotPDF จะวาดข้อความที่มีสไตล์ จากนั้นสร้างคำอธิบายลิงก์ทับบนกล่องของการรันนั้น จุดเริ่มต้นสาธารณะสำหรับคำอธิบายประกอบนั้นคือเมธอดของหน้า 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;
ทำไมกล่องคลิกจึงต้องคำนวณจากความกว้างที่วัดได้
อาจชวนให้คิดว่าการหาตำแหน่งลิงก์ทำได้โดยการค้นหาข้อความที่มองเห็นได้บนหน้ากระดาษแล้ววาดรูปสี่เหลี่ยมผืนผ้ารอบสิ่งที่พบ แต่วิธีนั้นใช้งานไม่ได้ และเหตุผลนั้นเป็นพื้นฐานสำคัญเกี่ยวกับวิธีการจัดเก็บข้อความที่แปลงแล้ว การรันที่มีสไตล์จะถูกวาดด้วยฟอนต์ชุดย่อยที่ฝังไว้ (embedded 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;
การยืนยันผลลัพธ์ในโปรแกรมอ่าน
เปิดไฟล์ที่แปลงแล้วใน Acrobat หรือโปรแกรมอ่านปัจจุบันอื่นๆ และตรวจสอบสองสิ่งนี้ ประการแรก ข้อความเข้ารหัสควรแสดงผลพร้อมด้วยสไตล์ที่ครบถ้วน: การรันแบบตัวหนาจะแสดงเป็นตัวหนา การรันแบบมีสีจะมีสีสันกำกับ และส่วนเนื้อหาจะต้องวางเรียงตามลำดับที่ถูกต้องบนบรรทัดแทนที่จะซ้อนทับกันหรือตกขอบกล่อง ประการที่สอง ไฮเปอร์ลิงก์ต้องใช้งานได้จริง ชี้เคอร์เซอร์เมาส์ไปที่จุดยึดและแถบสถานะควรแสดงที่อยู่ปลายทาง คลิกที่ลิงก์และการดำเนินการ URI ควรเปิดที่อยู่นั้นขึ้นมา ใช้เครื่องมือตรวจสอบคำอธิบายประกอบของโปรแกรมอ่านเพื่อยืนยันว่าแต่ละตัวเป็นคำอธิบายประกอบ /Link จริงที่ /Rect โอบรอบข้อความจุดยึดอย่างพอดี วางทับอยู่บนเนื้อหาซึ่งปัจจุบันเป็นเพียงสัญลักษณ์อักษรปกติแทนที่จะเป็น XFA ที่แสดงผลโดยฟอร์ม การรวมกันนี้ระหว่างข้อความแบบคงที่ที่มีสไตล์กับคำอธิบายลิงก์จริงบนรูปสี่เหลี่ยมผืนผ้าที่ถูกต้อง คือสิ่งที่ช่วยให้เอกสารที่แปลงแล้วมีอายุการใช้งานที่ยาวนานกว่าเอ็นจิน XFA ที่มันไม่จำเป็นต้องใช้งานอีกต่อไป
การแปลงฟิลด์เอง กล่องข้อความ กล่องเครื่องหมาย และรายการตัวเลือกที่อยู่รอบข้อความเข้ารหัสนี้มีอธิบายไว้ในคู่มือการแปลงฟอร์ม XFA เป็นวิดเจ็ต AcroForm ของเรา สำหรับเรื่องราวในภาพกว้างของการสร้างและจัดวางคำอธิบายลิงก์ด้วยตนเอง นอกเหนือจากตัวที่เส้นทางการแปลงสร้างให้ โปรดดูการทำงานกับคำอธิบายประกอบ PDF ใน HotPDF ทั้งสองส่วนนี้พัฒนาขึ้นบนโมเดลคำอธิบายประกอบและฟอร์มเดียวกันที่จัดส่งมากับ HotPDF Component สำหรับ Delphi และ C++Builder