PDFlibPas แก้ปัญหาตัวอักษรที่ฟอนต์ที่เลือกไว้วาดไม่ได้ ด้วยการค้นหาผ่านสายฟอนต์สำรองที่ติดตั้งไว้ทีละคลัสเตอร์ พร้อมคงลำดับการจัดรูปทรงอักษรและทิศทางข้อความสองทิศไว้ครบถ้วน คุณเปิดใช้งานได้ด้วย SetAutomaticFontFallback ขยายสายฟอนต์ด้วย AddFontFallback และมีเพียงฟอนต์สำรองที่ถูกใช้จริงในผลลัพธ์เท่านั้นที่จะถูกฝังลงในไฟล์
ปัญหานี้คือสิ่งที่ระบบสร้างเอกสารทุกระบบต้องเจอในวันที่ชื่อลูกค้ามาในสคริปต์ที่ฟอนต์เทมเพลตไม่เคยรองรับ ความล้มเหลวแบบนี้เงียบเสียจนกลายเป็นเรื่องแพงในภายหลัง
เหตุใดข้อความที่ไม่รองรับจึงหายไปแทนที่จะแจ้งข้อผิดพลาด
เพราะ PDF ไม่มีแนวคิดเรื่องฟอนต์ที่วาดตัวอักษรไม่ได้อยู่แล้ว ฟอนต์แบบธรรมดาจะแมปรหัสไบต์ไปยังชื่อกลิฟผ่านการเข้ารหัส ส่วนฟอนต์แบบผสมจะแมปรหัสผ่าน CMap ไปยังดัชนีกลิฟ เมื่อขอกลิฟที่ฟอนต์นั้นไม่มี ระบบจะได้ดัชนีกลิฟศูนย์ คือ .notdef ซึ่งฟอนต์ส่วนใหญ่วาดออกมาเป็นความว่างเปล่าหรือกล่องเปล่า ไฟล์ยังคงถูกต้องตามโครงสร้าง ตัวดำเนินการข้อความก็ถูกรูปแบบดี และหน้าก็เรนเดอร์ได้ตามปกติ เพียงแต่ตรงตำแหน่งชื่อกลับว่างเปล่า
ไม่มีข้อกำหนดใดใน ISO 32000-1 ที่บังคับให้ผู้สร้างไฟล์ต้องสังเกตเห็นเรื่องนี้ ระบบที่เขียนข้อความโดยไม่ตรวจสอบการรองรับกลิฟ จะได้ PDF ที่ถูกต้องตามข้อกำหนดทางเทคนิคแต่สูญเสียเนื้อหาไปอย่างเงียบ ๆ และความสูญเสียนั้นจะปรากฏบนหน้าจอของลูกค้าในอีกหลายสัปดาห์ต่อมา นี่คือเหตุผลที่ฟีเจอร์สำรองฟอนต์และรายงานกลิฟที่ขาดหายมาด้วยกัน การแก้ไขสิ่งที่แก้ได้เป็นเพียงครึ่งหนึ่งของงาน ส่วนการรายงานสิ่งที่แก้ไม่ได้คืออีกครึ่งหนึ่ง
การสำรองฟอนต์ทำงานทีละคลัสเตอร์ ไม่ใช่ทีละโค้ดพอยต์
ความละเอียดของหน่วยประมวลผลคือรายละเอียดที่แยกการทำงานจริงออกจากสิ่งที่ดูเหมือนจะใช้ได้ ข้อความไม่ใช่ลำดับของตัวอักษรอิสระ พยางค์ในอักษรเทวนาครี อิโมจิที่มีตัวปรับสีผิว หรือตัวอักษรพื้นฐานพร้อมเครื่องหมายประกอบ ล้วนเป็นหนึ่งคลัสเตอร์ที่ต้องเรนเดอร์ด้วยฟอนต์เดียว เพราะการตัดสินใจจัดรูปทรงภายในนั้นขึ้นอยู่กับตารางในฟอนต์นั้นเอง
PDFlibPas จะแก้ปัญหาระดับคลัสเตอร์ ดังนั้นคลัสเตอร์ที่ฟอนต์สำรองรองรับจะถูกวาดทั้งหมดด้วยฟอนต์นั้น การแบ่งกลางคลัสเตอร์แล้ววาดครึ่งหนึ่งด้วยฟอนต์หลักและอีกครึ่งด้วยฟอนต์สำรอง จะได้ผลลัพธ์ที่มีอยู่จริงตามเทคนิคแต่แตกหักให้เห็นชัด ซึ่งอาจแย่กว่าความว่างเปล่าตั้งแต่แรกเสียอีก ลำดับการวิ่งของข้อความก็ถูกรักษาไว้เช่นกัน ดังนั้นการสำรองฟอนต์ภายในข้อความแบบขวาไปซ้ายจะไม่ทำให้ข้อความรอบข้างสลับลำดับ กลไกเดียวกันนี้อยู่เบื้องหลังเลย์เอาต์แนวตั้งที่อธิบายไว้ใน การเขียนแนวตั้งสำหรับภาษาญี่ปุ่นและจีน
var
Lib: TPDFlib;
begin
Lib := TPDFlib.Create;
try
Lib.SetOrigin(1);
Lib.SetAutomaticFontFallback(1);
// ลำดับการค้นหา: ที่เจอก่อนชนะ จึงควรวางฟอนต์ที่ครอบคลุมกว้างสุดไว้ท้ายสุด
Lib.AddFontFallback('Microsoft YaHei'); // ภาษาจีนตัวย่อ
Lib.AddFontFallback('Meiryo'); // ภาษาญี่ปุ่น
Lib.AddFontFallback('Segoe UI Symbol');
Lib.AddFontFallback('Segoe UI Emoji');
Lib.SetMissingGlyphPolicy(PDF_MISSING_GLYPH_REPORT);
Lib.AddTrueTypeFont('Arial', 1); // 1 = ฝังฟอนต์ลงในไฟล์
Lib.SetTextSize(11);
Lib.DrawText(72, 720, 'Invoice for 北京示例科技有限公司');
Lib.DrawText(72, 700, 'Delivery status: on time');
Lib.SaveToFile('invoice.pdf');
finally
Lib.Free;
end;
end;
จัดลำดับสายฟอนต์อย่างตั้งใจ เพราะการแก้ปัญหาจะเลือกฟอนต์แรกที่ครอบคลุมคลัสเตอร์นั้น ถ้าวางฟอนต์แบบ pan-Unicode ที่ครอบคลุมกว้างไว้ก่อน มันจะชนะเกือบทุกกรณี และฟอนต์เฉพาะสคริปต์ที่คุณเลือกมาอย่างพิถีพิถันจะไม่ถูกเรียกใช้เลย ให้วางฟอนต์เฉพาะทางไว้ก่อนแล้วเก็บฟอนต์ครอบจักรวาลไว้ท้ายสุด
รายงานหรือยกเลิก: คุณต้องการความล้มเหลวแบบไหน
SetMissingGlyphPolicy รับค่าได้ทั้ง PDF_MISSING_GLYPH_REPORT ซึ่งเป็นค่าเริ่มต้นที่เข้ากันได้กับเวอร์ชันก่อนหน้า หรือ PDF_MISSING_GLYPH_ABORT ภายใต้นโยบายรายงาน การเขียนข้อความจะดำเนินต่อไป โค้ดพอยต์ที่แก้ไม่ได้จะถูกตัดทิ้งเหมือนเดิมและถูกบันทึกไว้ทุกตัว ภายใต้นโยบายยกเลิก การเขียนข้อความจะถูกปฏิเสธก่อนที่เนื้อหาใด ๆ จะถูกเขียน และ LastErrorCode จะถูกตั้งเป็น 521
เลือกตามวัตถุประสงค์ของเอกสาร ชุดรายงานภายในควรเรนเดอร์ต่อไปและบันทึกช่องว่างไว้ เพราะรายงานที่ไม่สมบูรณ์เล็กน้อยวันนี้ยังดีกว่าไม่มีรายงานเลย ส่วนสัญญาที่มีผลผูกพันทางกฎหมาย ใบแจ้งหนี้ หรือเอกสารใดก็ตามที่มีชื่อระบุอยู่ ควรใช้การยกเลิก เพราะตัวอักษรในชื่อคู่สัญญาที่หายไปอย่างเงียบ ๆ เป็นข้อบกพร่องที่คุณอยากพบในกระบวนการของตัวเองมากกว่าในข้อพิพาท นโยบายยกเลิกจะล้มเหลวก่อนการเขียน จึงไม่มีสตรีมเนื้อหาที่เขียนค้างครึ่งเดียวหลงเหลืออยู่
var
Lib: TPDFlib;
Report: WideString;
begin
Lib := TPDFlib.Create;
try
Lib.SetMissingGlyphPolicy(PDF_MISSING_GLYPH_ABORT);
// ... สร้างเอกสาร ...
if Lib.DrawText(72, 660, CustomerName) <> 1 then
if Lib.LastErrorCode = PDFLIB_ERROR_MISSING_GLYPH then
begin
Report := Lib.GetMissingGlyphReportJSON;
// {"valid":false,"policy":1,"eventCount":1,"events":[
// {"sequence":1,"documentIndex":0,"page":1,"utf16Index":12,
// "codePoint":21271,"unicode":"U+5317","fontName":"Arial",
// "fontType":"TrueType","operation":"DrawText"}]}
EscalateToOperator(Report);
end;
finally
Lib.Free;
end;
end;
รายงานนี้ถูกออกแบบให้อ่านได้ด้วยเครื่องและมีขอบเขตจำกัดโดยตั้งใจ แต่ละเหตุการณ์จะบอกหน้า ดัชนี UTF-16 ภายในสตริง โค้ดพอยต์ทั้งในรูปตัวเลขและรูป U+XXXX ฟอนต์ที่ถูกเลือกใช้ ประเภทของฟอนต์ และการดำเนินการที่พบปัญหา ทำให้ตั๋วซัพพอร์ตสามารถระบุตัวอักษรที่แน่ชัดแทนการอธิบายอาการคร่าว ๆ ตัวติดตามจะเก็บเหตุการณ์ล่าสุดไว้ 256 รายการ ซึ่งเพียงพอสำหรับวินิจฉัยเอกสารและเล็กพอที่จะไม่ทำให้การรันที่ผิดปกติกลายเป็นปัญหาหน่วยความจำ
การวัดขนาดและการวาดต้องสอดคล้องกัน
การวัดความกว้างใช้การตัดสินใจสำรองฟอนต์แบบรับรู้คลัสเตอร์ชุดเดียวกับที่ใช้ตอนวาด ฟังดูเป็นเรื่องที่ควรเป็นอยู่แล้ว แต่นี่คือสิ่งที่ระบบสำรองฟอนต์ที่พัฒนาขึ้นเองส่วนใหญ่ทำผิด คือแก้เฉพาะเส้นทางการวาด แต่ปล่อยให้การวัดยังคงอ้างอิงฟอนต์หลัก ผลคือกล่องข้อความ การจัดชิดขวา และคอลัมน์ตารางทุกจุดถูกคำนวณจากความกว้างที่ไม่ตรงกับสิ่งที่ถูกวาดจริง
เพราะทั้งสองเส้นทางใช้การแก้ปัญหาร่วมกัน สตริงที่วัดขนาดก่อนวาดจะครอบครองความกว้างตามที่วัดไว้จริง รวมถึงส่วนที่ใช้ฟอนต์สำรองด้วย นี่คือสิ่งที่ทำให้เปิดใช้การสำรองฟอนต์แบบทั่วทั้งระบบได้อย่างปลอดภัย แทนที่จะเปิดเฉพาะจุดที่คุณตรวจสอบด้วยมือเท่านั้น
ฝังเฉพาะสิ่งที่ใช้จริงเท่านั้น
ฟอนต์สำรองจะถูกฝังแบบขี้เกียจ คือฟอนต์ในสายที่ไม่เคยแก้ปัญหาคลัสเตอร์ใดเลยจะไม่มีส่วนในผลลัพธ์เลย เอกสารที่มีตัวอักษรจีนหนึ่งตัวและตัวอักษรละตินอีก 5,000 ตัว จะไม่พกฟอนต์ CJK เต็มชุดติดไปด้วย แต่จะพกเฉพาะสิ่งที่ขั้นตอนการทำ subset สร้างขึ้นสำหรับกลิฟตัวนั้นตัวเดียว ซึ่งเป็นพฤติกรรมที่อธิบายไว้ใน การลดขนาดไฟล์และการทำ subset ฟอนต์
ความขี้เกียจแบบนี้ทำให้การตั้งค่าสายฟอนต์กว้าง ๆ มีต้นทุนต่ำ ลงทะเบียนฟอนต์ที่ชุดเอกสารของคุณอาจต้องใช้ในทุกโลแคลที่ให้บริการได้เลย เพราะ PDF แต่ละไฟล์จะเสียต้นทุนเฉพาะสิ่งที่ใช้จริงเท่านั้น สำหรับเอกสารที่คุณไม่ได้เป็นผู้สร้างเอง ซึ่งฟอนต์ที่ขาดหายอยู่ในไฟล์ที่มีอยู่แล้ว เส้นทางการซ่อมแซมจะต่างออกไป และมีอธิบายไว้ใน การฝังฟอนต์ที่ขาดหายลงในไฟล์ PDF ที่มีอยู่
มีข้อควรระวังหนึ่งเรื่องการดีพลอยที่ควรพูดให้ชัด: การสำรองฟอนต์จะแก้ปัญหาจากฟอนต์ที่ติดตั้งอยู่บนเครื่องที่รันโค้ดจริงเท่านั้น เซิร์ฟเวอร์ที่ไม่มีฟอนต์ CJK ติดตั้งไว้จะไม่มีอะไรให้สำรองเลย และรายงานจะบอกคุณตั้งแต่เอกสารแรกก่อนที่จะมีข้อร้องเรียนแรกเสียอีก จัดส่งฟอนต์ที่คุณต้องพึ่งพาไปด้วย และยืนยันเรื่องสิทธิ์การใช้งานสำหรับการฝังฟอนต์เหล่านั้นให้เรียบร้อย
PDFlibPas เป็นไลบรารี PDF สำหรับ Delphi, C++Builder และ Lazarus พร้อมอินเทอร์เฟซ DLL และ ActiveX ที่ตรงกัน ทำให้ API สำรองฟอนต์และรายงานกลิฟที่ขาดหายใช้งานได้จากภาษาอื่นที่ไม่ใช่ Pascal ด้วย เอกสารฉบับเต็มอยู่ที่ หน้าไลบรารี PDF สำหรับ Delphi ของ PDFlibPas