บทความเทคนิค

การติดแท็กโครงสร้างอัตโนมัติเพื่อ PDF ที่เข้าถึงได้ใน Delphi

PDF Library for Delphi สามารถติดแท็กเอกสารไปพร้อมกับการวาด เปิด SetAutoTagMode แล้วคำสั่ง DrawText ธรรมดาจะกลายเป็นย่อหน้า ข้อความที่วาดติดต่อจาก RegisterHeading จะกลายเป็นหัวเรื่องระดับนั้น ส่วนหัวและส่วนท้ายแบบ running จะกลายเป็น artifact ที่ตัวอ่านข้ามไป รูปภาพจะกลายเป็น figure และ DrawTableRows จะพาตารางพร้อมแถวและเซลล์เข้าไปอยู่ใน structure tree

ทางเลือกอื่น — และจนกระทั่งเมื่อไม่นานมานี้เป็นทางเลือกเดียว — คือการหุ้มทุกคำสั่งวาดด้วย BeginTag และ EndTag ด้วยมือ วิธีนี้ใช้ได้ผล และสำหรับเอกสารที่มีโครงสร้างผิดธรรมดามันยังเป็นเครื่องมือที่ถูกต้อง แต่สำหรับรายงาน ใบแจ้งหนี้ หรือเอกสารสรุปทั่วไป มันหมายความว่าความเข้าถึงของเอาต์พุตขึ้นอยู่กับว่าไม่มีใครลืมคู่แท็กเด็ดขาดเดียว ทั่วทุกเส้นทางโค้ดที่วาดอะไรก็ตาม

บิตโหมดครอบคลุมอะไรบ้าง

SetAutoTagMode รับ bit mask แล้วคืนค่าโหมดที่เคยใช้อยู่ก่อน AUTOTAG_TEXT (1) ติดแท็กข้อความเป็นย่อหน้า หรือเป็นหัวเรื่องเมื่อถึงคิว AUTOTAG_FURNITURE (2) ทำเครื่องหมายส่วนหัวและส่วนท้ายแบบ running รวมถึงเลขหน้าเป็น artifact AUTOTAG_FIGURE (4) เปลี่ยนรูปที่วาดเป็น figure หรือเป็น artifact เมื่อประกาศไว้ว่าเป็นรูปประดับ AUTOTAG_TABLE (8) พาตารางที่วาดเข้าไปใน structure tree AUTOTAG_DEFAULT คือ 15 คือทั้งสี่อย่างรวมกัน

การเปิดโหมดยังทำเครื่องหมายเอกสารว่าเป็น tagged อีกด้วย และขั้นตอนนี้สำคัญมากกว่าที่เสียงมันฟังดู ตัวอ่านจะถือว่าเอกสารเป็น untagged เว้นแต่ catalog จะบอกเป็นอย่างอื่น (ISO 32000-1 §14.7.1) ดังนั้นไฟล์ที่พก structure tree ครบถ้วนแต่ไม่มีการประกาศ /MarkInfo จะถูกเทคโนโลยีช่วยเหลือประกาศว่าไม่มีโครงสร้างเลย ทั้งที่ต้นไม้นั้นอยู่ตรงนั้น เพียงแต่ไม่มีอะไรอ่านมัน

แผนภาพ bit mask ของ SetAutoTagMode ที่จับคู่การเรียกวาดภาพ Delphi ธรรมดาเข้าสู่โครงสร้างต้นไม้ PDF แบบมีแท็ก โดยชี้ชัดว่า flag Marked เป็นข้อบังคับ
บิตที่เปิดใช้งานแต่ละบิตของ SetAutoTagMode จะส่งคำสั่งวาดปกติเข้าสู่องค์ประกอบที่ตรงกันในแผนผังโครงสร้าง
var
  Lib: TPDFlib;
begin
  Lib := TPDFlib.Create;
  try
    Lib.SetOrigin(1);
    Lib.SetAutoTagMode(AUTOTAG_DEFAULT);   // text + furniture + figures + tables
    Lib.AddStandardFont(4);
    Lib.SetTextSize(18);
    Lib.RegisterHeading(1, 'Annual service report');
    Lib.DrawText(72, 96, 'Annual service report');   // becomes H1
    Lib.SetTextSize(11);
    Lib.DrawText(72, 130, 'Every unit installed before 2024 was inspected.');
    Lib.SaveToFile('report.pdf');
  finally
    Lib.Free;
  end;
end;

หัวเรื่องรู้ได้อย่างไรว่าข้อความใดเป็นของมัน

RegisterHeading กำหนดระดับให้ข้อความถัดไปที่วาด แล้วรอข้อความนั้น หากมีรูปวาดแทรกเข้ามาระหว่างทาง รูปนั้นจะกลายเป็น figure และหัวเรื่องยังคงค้างอยู่รอข้อความที่ตามมา พฤติกรรมนี้ตั้งใจไว้แบบนั้น เพราะทางเลือกอื่นที่ให้รูปรับระดับหัวเรื่องไป ได้ผลิตเอกสารที่เส้นขีดตกแต่งใต้ชื่อเรื่องถูกประกาศเป็นชื่อเรื่อง

กฎ "ใช้ไปกับรายการเดียว" เดียวกันนี้ควบคุม figure RegisterFigure ส่งคำบรรยายให้รูปถัดไป ส่วน RegisterDecoration ประกาศรูปถัดไปเป็นเส้นขีด ขอบ หรือพื้นหลังที่ไม่มีความหมาย ทั้งสองถูกใช้ไปกับรูปเดียว ดังนั้นรูปที่มาทีหลังจะไม่สืบทอดคำบรรยายที่ตั้งใจให้รูปก่อนหน้า — ซึ่งเป็นสาเหตุที่ alt text มักไปติดอยู่กับรูปผิดในโค้ดที่ติดแท็กด้วยมือ

คำบรรยายสำคัญกว่าสตริงอื่นใดในเอกสารที่เข้าถึงได้ ผู้อ่านตาบอดได้รับคำบรรยายแทนรูป และนั่นคือทั้งหมดที่พวกเขาได้ "Chart" ไม่ใช่คำบรรยาย "Quarterly revenue by region, with the eastern region highest in Q3" ต่างหากที่ใช่

แผนภาพ PDF Library for Delphi แสดงหัวข้อ PDF ที่ลงทะเบียนไว้ต้องรอผ่านรูปภาพที่แทรกอยู่ ขณะที่ RegisterFigure และ RegisterDecoration ใช้ได้ครั้งละหนึ่งรูปเท่านั้น
RegisterHeading จะรอข้อความถัดไป ไม่ว่าระหว่างนั้นจะมีการวาดอย่างอื่นหรือไม่
Lib.RegisterFigure('Exploded view of the gearbox assembly');
Lib.AddImageFromFile('gearbox.png', 0);      // becomes a tagged Figure

Lib.RegisterDecoration;                       // meaningless rule
Lib.AddImageFromFile('divider.png', 0);       // drawn inside a layout artifact

ตาราง ส่วนหัว และจุดที่การตัดสินใจเรื่องการทำซ้ำอยู่

เมื่อเปิดบิตตาราง DrawTableRows จะพาตารางพร้อมแถวและเซลล์เข้าไปใน structure tree ทำให้ตัวอ่านบอกได้ว่าค่าหนึ่งอยู่ในคอลัมน์ใด แทนที่จะอ่านตารางทั้งหมดออกมาเป็นข้อความรวมที่ไม่เกี่ยวข้องกัน SetTableHeaderRowCount กำหนดจำนวนแถวนำที่เป็นส่วนหัว แถวเหล่านั้นจะถูกเขียนเป็นเซลล์หัวข้อมือพก scope คอลัมน์ ซึ่งเป็นสิ่งที่ทำให้ตัวอ่านประกาศหัวข้อของค่าที่ผู้ใช้อยู่ได้

แถวส่วนหัวที่กำหนดแบบนี้จะอยู่กับที่ การทำซ้ำที่ด้านบนของแต่ละหน้าเป็นการตัดสินใจเรื่องเลย์เอาต์และยังคงเป็นเช่นนั้น DrawTaggedTableRows รับอาร์กิวเมนต์ RepeatHeaderRows ไว้ทำหน้าที่นั้นพอดี การแยกสองสิ่งนี้ออกจากกันช่วยหลีกเลี่ยง structure tree ที่ได้สำเนาส่วนหัวเพิ่มสำหรับทุกการตัดหน้า ซึ่งเป็นผลที่การทำซ้ำอัตโนมัติจะผลิตให้

แผนภาพ PDF Library for Delphi ของเซลล์หัวตารางที่ระบุขอบเขตคอลัมน์ในโครงสร้างต้นไม้ ขณะที่ RepeatHeaderRows ทำให้การทำซ้ำเมื่อขึ้นหน้าใหม่เป็นเพียงเรื่องการจัดวางเท่านั้น
แถวหัวตารางอยู่ในแผนผังโครงสร้างเพียงครั้งเดียว โดยขอบเขตคอลัมน์ถูกผูกไว้กับเซลล์ TH แต่ละเซลล์
var
  TableID: Integer;
begin
  TableID := Lib.CreateTable(40, 3);
  Lib.SetTableHeaderRowCount(TableID, 1);       // แถว 1 คือแถบส่วนหัว
  Lib.SetTableCellContent(TableID, 1, 1, 'Part');
  Lib.SetTableCellContent(TableID, 1, 2, 'Torque');
  Lib.SetTableCellContent(TableID, 1, 3, 'Unit');
  // ... เติมแถวข้อมูล ...
  // วาดแถว 1..40 ลงในแถบสูง 600pt โดยทำซ้ำแถวส่วนหัวหนึ่งแถวต่อหน้า
  Lib.DrawTaggedTableRows(TableID, 72, 150, 600, 1, 40, 1);
end;

ผสมการติดแท็กอัตโนมัติกับด้วยมือ

การติดแท็กอัตโนมัติจะถอยออกภายในแท็กที่เปิดด้วยมือ ส่วนหนึ่งของเอกสารสามารถบรรยายด้วยโค้ดของคุณและส่วนที่เหลือปล่อยให้ไลบรารี โดยทั้งสองไม่ซ้อนกัน — ซึ่งเป็นการจัดเรียงที่เอกสารจริงส่วนใหญ่ต้องการ หน้าปกและบล็อกลายเซ็นมีโครงสร้างที่มีเพียงคุณเข้าใจ ส่วนเนื้อหาสองร้อยหน้าระหว่างนั้นไม่จำเป็นต้องเป็นเช่นนั้น

กฎความปลอดภัยสองข้อรักษาเอาต์พุตให้สะอาด ไม่มีอะไรถูกติดแท็กภายใน artifact เพราะเนื้อหาที่ทำเครื่องหมายเป็น artifact ต้องไม่พกองค์ประกอบโครงสร้าง และข้อความว่างไม่เปิดองค์ประกอบใด ดังนั้น DrawText ที่หลุดมาพร้อมสตริงว่างจะไม่สร้างองค์ประกอบโครงสร้างที่ตัวอ่านจะประกาศออกมาเป็นความว่างเปล่า ทั้งสองอย่างคือประเภทของข้อบกพร่องที่เอกสารติดแท็กด้วยมือสะสมเงียบ ๆ และตัวตรวจสอบรายงานเป็นกลุ่มเดือนต่อมา

สิ่งที่การติดแท็กอัตโนมัติยังไม่ตัดสินใจให้คุณ

ลำดับการอ่านที่อยู่นอกเหนือลำดับการวาด บทบาทเชิงความหมายที่ไม่ใช่ย่อหน้า หัวเรื่อง figure หรือตาราง และการประกาศภาษา การติดแท็กอัตโนมัติกำหนดโครงสร้างตามลำดับที่เนื้อหาถูกวาด — หากโค้ดเลย์เอาต์ของคุณวาด sidebar ก่อนเนื้อหา นั่นคือลำดับที่ต้นไม้บันทึก สำหรับเอกสารที่ลำดับการมองเห็นกับลำดับการอ่านแตกต่างกันอย่างแท้จริง API ติดแท็กด้วยมือยังคงเป็นเครื่องมือที่ถูกต้อง และคู่มือ tagged PDF และโครงสร้างการเข้าถึง ครอบคลุมบทบาท scope และการผูกส่วนหัวอย่างละเอียด

เมื่อเอกสารเสร็จสมบูรณ์ ให้ตรวจสอบแทนที่จะสมมติเอา บันทึกของ PDF/A และ PDF/UA preflight แสดงวิธีรับคำตัดสินเกี่ยวกับโครงสร้างที่คุณผลิตออกมา และคู่มือ การส่งออกรายงานขับเคลื่อนด้วยข้อมูล ครอบคลุมจุดที่คำสั่งเหล่านี้สอดสนิทในเอนจินรายงานที่สร้างเลย์เอาต์จากข้อมูล

PDF Library for Delphi เป็นไลบรารี PDF แบบ Pascal พื้นเมืองสำหรับ Delphi, C++Builder และ Lazarus โดยไม่มีรันไทม์ PDF ภายนอก เอาต์พุตที่เข้าถึงได้จึงผลิตจากโค้ดชุดเดียวกับที่วาดเอกสาร — ดู หน้าผลิตภัณฑ์ PDF Library for Delphi สำหรับ API และรายการแพลตฟอร์มฉบับสมบูรณ์