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

การสร้างผลลัพธ์ PDF/A, PDF/X และ PDF/UA ใน Delphi: คู่มือ HotPDF

PDF/A, PDF/X และ PDF/UA เป็นมาตรฐานสามแบบที่แตกต่างกัน แก้ปัญหาสามอย่างที่ต่างกัน: การเก็บถาวรระยะยาว การแลกเปลี่ยนงานพิมพ์ และการเข้าถึงได้ มันไม่ใช่ช่องกาเครื่องหมายสามช่องบนแบบฟอร์ม compliance เดียวกัน และข้อผิดพลาดที่พบบ่อยที่สุดคือการปฏิบัติกับมันราวกับว่ามันเป็นแบบนั้น ไฟล์หนึ่งอาจสมบูรณ์แบบตาม PDF/A แต่ใช้ไม่ได้เลยสำหรับโรงพิมพ์ ต้นแบบสำหรับพิมพ์ที่สมบูรณ์แบบอาจอ่านไม่ได้เลยสำหรับ screen reader ที่แย่ไปกว่านั้นคือ ทั้งสามมาตรฐานล้วนเป็นข้อจำกัดต่อโครงสร้างภายในของไฟล์ ไม่ใช่ต่อหน้าตาที่มันแสดงผล เอกสารที่เปิดได้ราบรื่นใน viewer ทุกตัวที่คุณมีอาจยังคงผ่านการตรวจสอบไม่ได้ในความพยายามครั้งแรก และมักจะเป็นแบบนั้นจริง ๆ ด้วย

HotPDF ซึ่งเป็น native VCL PDF library ของ losLab ปฏิบัติต่อความสอดคล้องตามมาตรฐานเสมือนเป็นสิ่งที่คุณประกาศไว้ก่อนที่หน้าแรกจะมีอยู่ด้วยซ้ำ คุณตั้งค่า compliance property แนบโครงสร้างที่มาตรฐานกำหนดไว้ และไลบรารีจะปฏิเสธการตั้งค่าที่ขัดแย้งกับโปรไฟล์ตอนบันทึก นี่เป็นโมเดลที่ดีกว่าการสร้างไฟล์ขึ้นมาก่อนแล้วหวังว่า post-processor จะช่วยแก้ย้อนหลังได้ เพราะสิ่งที่มาตรฐานเหล่านี้กำหนดส่วนใหญ่ไม่สามารถเพิ่มเข้าไปทีหลังได้

มาตรฐาน ISO สามแบบ สามคำสัญญาที่ต่างกัน

PDF/A (ISO 19005) ว่าด้วยเรื่องเวลา มันสัญญาว่าไฟล์จะยังคงแสดงผลได้เหมือนเดิมทุกประการในอีกหลายสิบปีข้างหน้า จึงเรียกร้องความสมบูรณ์ในตัวเองอย่างเต็มที่: ฝังฟอนต์ทุกตัว กำหนดความหมายของสีทุกสีให้ไม่ขึ้นกับอุปกรณ์ผ่าน OutputIntent มี XMP metadata ครบถ้วน และห้ามสิ่งใดก็ตามที่พฤติกรรมขึ้นอยู่กับสภาพแวดล้อม การเข้ารหัสและ JavaScript ถูกห้ามใช้ เพราะไม่มีใครรับประกันได้ว่าตัวถอดรหัสหรือ script engine จะยังคงมีอยู่ในปี 2050

PDF/X (ISO 15930) ว่าด้วยเรื่องสีบนกระดาษ มันมีอยู่เพื่อให้นักออกแบบส่งไฟล์ให้โรงพิมพ์ได้โดยไม่ต้องพูดคุยรายละเอียดกัน ซึ่งหมายถึงสภาวะการพิมพ์ที่กำหนดคุณลักษณะไว้แล้ว คีย์ /Trapped ที่บังคับต้องมี เรขาคณิตของ trim และ bleed ที่กำหนดไว้ชัดเจน และในแบบ X-1a จะต้องไม่มี live transparency ให้ RIP ต้องเดาเอาเอง PDF/UA (ISO 14289) ว่าด้วยเรื่องว่าใครอ่านผลลัพธ์ได้บ้าง เทคโนโลยีช่วยเหลือต้องการ tag tree ที่สมบูรณ์ ลำดับการอ่านที่สมเหตุสมผล ภาษาของเอกสารที่ประกาศไว้ชัดเจน และข้อความทางเลือกสำหรับทุกสิ่งที่ไม่ใช่ข้อความ

เพราะทั้งสามมาตรฐานดึงไปในทิศทางที่ต่างกัน ให้เลือกมาตรฐานที่ใช้บังคับตามช่องทาง output แต่ละช่อง แทนที่จะไล่ตามหาไฟล์เดียวที่ตอบโจทย์ทั้งหมด ต้นแบบสำหรับพิมพ์แบบ CMYK ล้วน ๆ เป็นสิ่งที่ผิดอย่างชัดเจนที่จะส่งให้ผู้ใช้ screen reader ที่ไม่เคยเห็นสีเลย และการล็อกพฤติกรรมแบบไดนามิกของโปรไฟล์การเก็บถาวรก็ขัดแย้งกับอะไรก็ตามที่โต้ตอบได้ ให้สร้างแยกตามช่องทางจากข้อมูลต้นทางเดียวกัน แล้วคุณจะหลีกเลี่ยงความขัดแย้งทั้งหมดนี้ได้

HotPDF สร้างไฟล์ PDF/A, PDF/X และ PDF/UA แยกตามช่องทางเอาต์พุต จากเอกสารต้นฉบับเดียวใน Delphi โดยมาตรฐาน ISO แต่ละฉบับคงคำสัญญาเชิงโครงสร้างที่ต่างกัน
PDF/A, PDF/X และ PDF/UA ดึงทางคนละทิศ HotPDF จึงสร้างไฟล์หนึ่งไฟล์ต่อช่องทางผลลัพธ์จากซอร์สเดียวกัน

PDF/A: OutputIntent คือส่วนที่ทุกคนมักลืม

หากไฟล์ PDF/A ผ่านการตรวจสอบไม่ได้ OutputIntent คือสิ่งแรกที่ควรตรวจดู มันเป็นโครงสร้างที่ตัวสร้างไฟล์มักข้ามไปบ่อยที่สุด เพราะไม่มีอะไรที่มองเห็นได้ขึ้นอยู่กับมันเลย ISO 19005 กำหนดให้ต้องมีหนึ่งตัว: ICC profile ที่ฝังไว้ ซึ่งกำหนดชัดเจนว่าสีของอุปกรณ์ในเอกสารหมายถึงอะไรจริง ๆ HotPDF ทำให้ profile นั้นเป็นอินพุตที่ต้องระบุอย่างชัดเจน แทนที่จะเป็นสิ่งที่คิดขึ้นทีหลัง:

var
  Pdf: THotPDF;
  ICC: TFileStream;
begin
  Pdf := THotPDF.Create(nil);
  try
    Pdf.FileName := 'invoice-archival.pdf';
    Pdf.PDFACompliance := 'B';            // ระดับ B: ความเที่ยงตรงด้านภาพ
    Pdf.Lang := 'en-US';
    Pdf.StandardFontEmulation := False;   // ฝังฟอนต์จริง ไม่จำลองแบบ Base-14
    ICC := TFileStream.Create('sRGB.icc', fmOpenRead);
    try
      Pdf.AddPDFAOutputIntent('sRGB IEC61966-2.1', '', ICC, 3, 'DeviceRGB');
    finally
      ICC.Free;
    end;
    Pdf.BeginDoc;
    Pdf.CurrentPage.SetFont('Arial', [], 11);
    Pdf.CurrentPage.TextOut(50, 760, 0, 'Archival invoice body');
    Pdf.EndDoc;
  finally
    Pdf.Free;
  end;
end;

มีรายละเอียดไม่กี่อย่างที่ตัดสินว่าผ่านหรือไม่ผ่านตรงนี้ StandardFontEmulation ต้องปิดไว้เสมอ: ฟอนต์ Base-14 ที่จำลองขึ้นมาจะไม่ถูกฝังไว้ และการฝังฟอนต์เป็นสิ่งที่ต่อรองไม่ได้ภายใต้ ISO 19005 การเข้ารหัสต้องปิดไว้เสมอเช่นกัน ดังนั้นห้ามผสม PDFACompliance กับ ActivateProtection เด็ดขาด ไฟล์เก็บถาวรที่เข้ารหัสไว้เป็นความขัดแย้งที่ validator จะจับได้ทันที จำนวน component ใน AddPDFAOutputIntent ต้องตรงกับ profile ซึ่งเป็น 3 สำหรับ profile แบบ RGB อย่าง sRGB IEC61966-2.1 และ 4 สำหรับ CMYK HotPDF จะติดตามการใช้งาน DeviceRGB และ DeviceCMYK เทียบกับ intent ที่ประกาศไว้ขณะเขียน ดังนั้นการเติมสีแบบ CMYK ที่หลุดเข้ามาในเอกสารที่มี intent เป็น RGB จะกลายเป็นปัญหาที่ถูกรายงานออกมา แทนที่จะเงียบหายไปเฉย ๆ

มีเรื่องหนึ่งที่ควรพูดถึงเกี่ยวกับ ICC profile: ให้ปฏิบัติต่อมันเหมือนกับ artifact การ deploy ที่มีการควบคุมเวอร์ชัน ไม่ใช่ไฟล์ที่ใครสักคนเคยวางไว้บน build server ครั้งเดียว ไบต์ของมันถูกฝังเข้าไปในทุกเอกสารที่คุณสร้าง ดังนั้น profile ที่ถูกตัดทอนหรือเสียหายจะปนเปื้อน batch ทั้งชุดอย่างเงียบ ๆ และคุณจะรู้ตัวก็ต่อเมื่อถึงเวลาตรวจสอบเท่านั้น ให้ส่งมันไปพร้อมกับตัวติดตั้งของคุณ บันทึก checksum ของมันไว้ใน run log และโหลดมันผ่านรูปแบบ TFileStream ที่แสดงไว้ข้างต้น เพื่อให้ไฟล์ที่หายไปล้มเหลวอย่างชัดเจนตอนสร้างเอกสาร แทนที่จะเงียบหายไปจนถึงด่านตรวจ archive

PDF/X สำหรับงานพิมพ์: Trapped, CMYK และ press profile

ต้นแบบสำหรับพิมพ์กลับเรื่องราวของสีตรงข้าม แท่นพิมพ์ต้องการ CMYK ที่กำหนดคุณลักษณะไว้แล้ว และมาตรฐานบังคับให้คุณระบุว่ามีการทำ trapping แล้วหรือไม่ แม้ว่าคำตอบที่ตรงไปตรงมาคือคุณไม่รู้เลยก็ตาม คีย์ /Trapped เป็นสิ่งที่บังคับต้องมีไม่ว่าจะอย่างไร:

Pdf.PDFXCompliance := 'X-1a';
Pdf.Trapped := 'Unknown';        // คีย์บังคับตาม ISO 15930
ICC := TFileStream.Create('FOGRA39.icc', fmOpenRead);
try
  Pdf.AddPDFXOutputIntent('FOGRA39 (ISO 12647-2:2004)', '', ICC, 4, 'DeviceCMYK');
finally
  ICC.Free;
end;
Pdf.BeginDoc;
// วาดด้วยสีที่ปลอดภัยสำหรับ CMYK ไม่มี transparency ไม่มีการเข้ารหัส
Pdf.EndDoc;

ตอนนี้จำนวน component เป็น 4 สำหรับ press profile แบบ CMYK X-1a ยังห้าม live transparency ด้วย ดังนั้นให้ตรวจสอบโค้ดวาดใด ๆ ที่ซ้อนชั้น element แบบโปร่งแสงไว้ อะไรก็ตามที่ viewer ผสมผสานบนหน้าจอคือสิ่งที่ RIP จะปฏิเสธไม่ยอมตีความพอดี เมื่อโรงพิมพ์ของคุณส่งลักษณะเฉพาะแบบอื่นมาให้ ให้สลับไบต์ของ profile และสตริง identifier แต่คงโครงสร้างโดยรอบไว้เหมือนเดิม

PDF/UA: โครงสร้างต้องสร้างขึ้นมาตั้งแต่แรก ไม่ใช่แก้ย้อนหลัง

การเข้าถึงได้เป็นมาตรฐานที่ทีมงานมักพยายามติดตั้งเพิ่มเข้าไปทีหลังบ่อยที่สุด และมันลงโทษวิธีการแบบนั้นหนักกว่าสองมาตรฐานที่เหลือ tag tree ต้องสะท้อนลำดับที่เนื้อหาถูกสร้างขึ้นมาในทางตรรกะ ซึ่งเป็นข้อมูลที่คุณไม่มีอีกต่อไปแล้วทันทีที่ไฟล์ถูกเขียนเสร็จ การตั้งค่า PDFUACompliance จะเปิดใช้งาน tagged output และ structure API จะผูกแต่ละคำสั่งวาดเข้ากับบทบาทเชิงความหมายของมันไปพร้อม ๆ กัน:

HotPDF สร้างต้นไม้แท็ก PDF/UA แบบสดขณะการเรียกวาดของ Delphi แต่ละครั้งทำงาน และข้อความที่ปล่อยออกนอก BeginTaggedContent กับ EndTaggedContent จะมองไม่เห็นต่อ screen reader
ต้นไม้แท็กถูกเขียนไปพร้อมกับที่คุณวาด ข้อความที่อยู่นอกคู่แท็กแสดงผลได้ปกติ แต่มองไม่เห็นสำหรับโปรแกรมอ่านหน้าจอ
Pdf.PDFUACompliance := True;     // เปิดใช้งาน tagged PDF โดยอัตโนมัติ
Pdf.Lang := 'en-US';             // ตั้งค่าอย่างชัดเจน; หากว่างจะย้อนกลับไปเป็น 'en'
Pdf.BeginDoc;

Root := Pdf.AddStructureElement(sstDocument, nil);
H1 := Pdf.EmitTaggedHeading(1, Root, 50, 700, 'Quarterly Report');
Para := Pdf.BeginTaggedContent('P', Root);
Pdf.CurrentPage.TextOut(50, 650, 0, 'Revenue grew in all regions.');
Pdf.EndTaggedContent;

Pdf.EndDoc;

ความล้มเหลวที่ควรจับตาดูคือข้อความที่วาดอยู่นอกคู่ BeginTaggedContent/EndTaggedContent ใด ๆ มันจะแสดงผลได้สมบูรณ์แบบแต่มองไม่เห็นเลยสำหรับ screen reader ดังนั้นผู้ทดสอบที่มองเห็นได้จึงไม่มีวันจับมันได้ บั๊กนี้จะถูกส่งมอบออกไปและปรากฏขึ้นก็ต่อเมื่อผู้ใช้เทคโนโลยีช่วยเหลือตัวจริงมาเจอช่องว่างนั้นเข้า เมื่อ template ของคุณมีชื่อบทบาทโครงสร้างแบบกำหนดเอง ให้แม็ปมันเข้ากับชุดมาตรฐานด้วย AddStructRoleMap('MyHead', 'H1') เพื่อให้ผู้อ่านที่ปฏิบัติตามมาตรฐานรู้ว่ามันหมายถึงอะไร ISO 14289 ยังกำหนดให้ต้องประกาศภาษาไว้ด้วย HotPDF จะย้อนกลับไปใช้ 'en' เมื่อ Lang ว่างเปล่า แต่นั่นเป็นเพียงตาข่ายนิรภัย ไม่ใช่เหตุผลที่จะปล่อยให้ภาษาจริงของเอกสารไม่ได้ตั้งค่าไว้

การตรวจสอบ: เชื่อ validator ไม่ใช่ viewer

viewer ที่เปิดไฟล์ของคุณได้ไม่พิสูจน์อะไรเลยเกี่ยวกับความสอดคล้องตามมาตรฐาน ดังนั้นการตรวจสอบจึงควรอยู่ใน release path ด้วยเครื่องมือที่ตรวจโครงสร้าง ไม่ใช่การแสดงผล สำหรับ PDF/A และ PDF/UA veraPDF คือ validator แบบเปิดระดับอ้างอิงที่ดีที่สุด มันรายงานความล้มเหลวตามข้อกำหนด ISO ซึ่ง map กลับไปยังการตั้งค่าข้างต้นได้โดยตรง สำหรับ PDF/X โปรไฟล์ Preflight ของ Adobe Acrobat ยังคงเป็นการตรวจสอบที่ใช้งานได้จริง เพราะความสอดคล้องด้านงานพิมพ์เกี่ยวข้องกับ intent ของสีพอ ๆ กับ syntax

ตัวสร้างไฟล์ก็ทำหน้าที่ส่วนของมันเองในเรื่องนี้ ตอนบันทึก HotPDF จะปรับ feature flag ให้สอดคล้องกับเวอร์ชัน PDF ที่ตั้งค่าไว้ โดยลดระดับสิ่งที่เวอร์ชันนั้นแสดงออกมาไม่ได้อย่างเงียบ ๆ เช่น AES-256 ที่ตกลงไปเป็น AES-128 เมื่อต่ำกว่า PDF 1.7 ด่าน compliance ใน EndDoc ไปไกลกว่านั้นและจะ raise ทันทีเมื่อเจอความขัดแย้งที่รุนแรง เช่นการขอ PDFACompliance พร้อมกับการเข้ารหัส ทั้งสองอย่างนี้ไม่ได้แทนที่ validator ภายนอกแต่อย่างใด มันเพียงแค่หยุดไม่ให้การตั้งค่าที่เป็นไปไม่ได้ไปถึง validator ตั้งแต่แรก

ประตูความสอดคล้องใน EndDoc ของ HotPDF จับการตั้งค่าที่เป็นไปไม่ได้ ก่อน veraPDF จะตรวจ PDF/A กับ PDF/UA และ Acrobat Preflight ตรวจ PDF/X ในเส้นทาง release ของ Delphi
HotPDF ปฏิเสธคอนฟิกูเรชันที่ขัดแย้งกันเองที่ EndDoc และตัวตรวจอิสระต่างหากเป็นผู้ตัดสินความเป็นไปตามข้อกำหนดจริง

มีนิสัยหนึ่งที่คุ้มค่าซ้ำแล้วซ้ำเล่า: ควบคุมเวอร์ชันของการตั้งค่า compliance ทั้งชุดให้เป็นหน่วยเดียวกัน รุ่นของ HotPDF, revision ของ template, checksum ของ ICC profile, build ของ validator ที่อนุมัติผ่าน ความสอดคล้องตามมาตรฐานจะเคลื่อนคลาดทันทีที่สิ่งใดสิ่งหนึ่งเปลี่ยนแปลงไปโดยที่สิ่งอื่นไม่เปลี่ยนตาม และการตรวจสอบที่แย่ที่สุดคือกรณีที่ไม่มีใครสร้างขึ้นใหม่ได้ว่าชุดค่าผสมไหนที่ผลิตไฟล์ archive อายุห้าปีขึ้นมา บันทึกการตั้งค่าเดียวต่อหนึ่ง batch จะแก้ปัญหานี้ได้อย่างถาวร

สุดท้าย ให้รันตัว validator กับ output จากการใช้งานจริง ไม่ใช่กับตัวอย่างที่สร้างขึ้นมาด้วยมืออย่างเรียบร้อย ความล้มเหลวที่กัดคุณมักมาจากข้อมูลที่ไม่มีใครคาดคิดไว้: โลโก้ลูกค้าที่ส่งมาเป็น CMYK ในขณะที่ intent ระบุว่า RGB การปรับแต่ง template เล็กน้อยที่แอบให้ฟอนต์ที่ไม่ได้ฝังหลุดเข้ามา code path ใหม่ที่วาดข้อความอยู่นอก tag tree เก็บไฟล์ที่รู้ว่าเสียไว้หนึ่งไฟล์จากทุกเหตุการณ์ในอดีตไว้เป็นอินพุตสำหรับการทดสอบ regression แล้วด่าน compliance จะยังคงซื่อสัตย์อยู่ตลอดเวลา สำหรับฝั่งการ render ของ pipeline เหล่านี้ ดูบทความของเราเรื่อง report output, ฟอนต์ และรูปภาพด้วย HotPDF ส่วนการเชื่อมต่อ validator เข้ากับ build มีบทความคู่กันเรื่องการทำ automation สำหรับ PDF preflight check

property ด้าน compliance, output intent และ tagging API ที่ใช้ในตัวอย่างเหล่านี้มาพร้อมกับHotPDF Delphi Componentสำหรับ Delphi และ C++Builder หน้าผลิตภัณฑ์มีลิงก์ไปยังเอกสารอ้างอิงฉบับเต็มสำหรับทุกฟังก์ชันเรียกที่แสดงในบทความนี้