ฝังและทำซับเซ็ตฟอนต์ TrueType
โดยสรุป
หัวข้อที่มีชื่อว่า “โดยสรุป”ลงทะเบียนฟอนต์ TrueType เรนเดอร์ข้อความด้วยฟอนต์นั้น แล้วให้ตัวเขียนฝังเฉพาะซับเซ็ตที่จำเป็น สูตรนี้ใช้เส้นทางเนื้อหาเดียวกับ examples/04-text-and-fonts.php โดยเพิ่มฟอนต์ TrueType (.ttf) ที่ลงทะเบียนไว้
การติดตั้ง
หัวข้อที่มีชื่อว่า “การติดตั้ง”composer require nextpdf/core:^3คำสั่งนี้จะติดตั้งแพ็กเกจ nextpdf/core ตัวอย่างนี้ทำงานบน PHP 8.4 และ test fixture LiberationSans-Regular.ttf ที่มาพร้อมกันช่วยให้สูตรนี้ทำงานได้ด้วยตัวเอง
ภาพรวมเชิงแนวคิด
หัวข้อที่มีชื่อว่า “ภาพรวมเชิงแนวคิด”ลงทะเบียนหน้าฟอนต์ด้วย FontRegistry::register($path, $alias) รีจิสทรีจะแจงไฟล์และคืนค่า FontInfo โดยใช้ TrueTypeParser สำหรับไฟล์ .ttf และ .otf หากต้องการใช้หน้าฟอนต์ ให้เลือกชื่อแทน (alias) ด้วย setFont($alias, ...) การเรียกนี้ยังบันทึก codepoint ที่ถูกใช้งานด้วย
การทำซับเซ็ตจะทำงานอัตโนมัติเมื่อเรียก save() ตัวเขียนฟอนต์ PDF จะรวบรวม codepoint ที่ถูกใช้งานและเรียก FontSubsetter::subset() หรือ CffSubsetter สำหรับหน้าฟอนต์ Compact Font Format (CFF) หรือ OpenType เมื่อซับเซ็ตมีขนาดเล็กกว่าโปรแกรมฟอนต์ฉบับเต็ม ตัวเขียนจะฝังซับเซ็ตนั้น พร้อมทั้งเขียน BaseFont และ FontName ใหม่โดยใช้แท็กซับเซ็ตที่เป็นตัวพิมพ์ใหญ่หกตัวและเชื่อมด้วยเครื่องหมายบวก รูปแบบนี้คือ ABCDEF+FontName ซึ่ง ISO 32000-2 กำหนดไว้สำหรับซับเซ็ตฟอนต์ ตัวเขียนจะจัดเก็บโปรแกรมฟอนต์ TrueType ที่ฝังไว้เป็น FontFile2 ในตัวบรรยายฟอนต์ (ISO 32000-2)
คำนำหน้าซับเซ็ตสร้างจากชื่อ PostScript แบบกำหนดผลลัพธ์ได้ ดังนั้นบิลด์แบบกำหนดผลลัพธ์ได้จึงให้แท็กที่คงที่ ด้วยเหตุนี้โปรไฟล์ความสามารถในการทำซ้ำ (reproducibility) ของสูตรนี้จึงเป็น structural โปรไฟล์ structural จะปรับคำนำหน้าซับเซ็ตและ /ID ในส่วนท้าย (trailer) ให้เป็นมาตรฐาน แทนที่จะยืนยันแบบไบต์ต่อไบต์
พื้นผิว API
หัวข้อที่มีชื่อว่า “พื้นผิว API”FontRegistry::register(string $fontFile, string $alias = '', int $fontIndex = 0): FontInfo—NextPDF\Typography\FontRegistrysetFont(string $family, string $style = '', float $size = 12.0): static—NextPDF\Core\Concerns\HasTypographyโดยส่งชื่อแทนที่ลงทะเบียนไว้เป็น$family- การทำซับเซ็ตเป็นการทำงานภายในของตัวเขียน (
NextPDF\Writer\PdfFontWriter->NextPDF\Typography\FontSubsetter) ไม่มีสวิตช์สาธารณะสำหรับเปิดหรือปิด ตัวเขียนจะทำซับเซ็ตเสมอเมื่อทราบ codepoint และซับเซ็ตมีขนาดเล็กกว่า
ตาราง PHPDoc ฉบับเต็มสร้างจากซอร์สโค้ด
ตัวอย่างโค้ด — เริ่มต้นอย่างรวดเร็ว
หัวข้อที่มีชื่อว่า “ตัวอย่างโค้ด — เริ่มต้นอย่างรวดเร็ว”<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;use NextPDF\Typography\FontRegistry;
$registry = new FontRegistry();$registry->register(__DIR__ . '/MyFont-Regular.ttf', alias: 'MyFont');
$doc = Document::createStandalone();$doc->addPage();$doc->setFont('MyFont', '', 14);$doc->cell(0, 10, 'Rendered with an embedded, subset TrueType face.', newLine: true);
$doc->save(__DIR__ . '/out.pdf');Document::createStandalone() จะสร้างรีจิสทรีแยกของตัวเอง หากต้องการใช้รีจิสทรีที่เติมข้อมูลไว้เอง ให้สร้างเอกสารผ่าน DocumentFactory ดังที่แสดงในตัวอย่างสำหรับการใช้งานจริง แฟกทอรีจะทำให้ตัวเขียนอ่านหน้าฟอนต์ที่ลงทะเบียนไว้
ตัวอย่างโค้ด — การใช้งานจริง
หัวข้อที่มีชื่อว่า “ตัวอย่างโค้ด — การใช้งานจริง”ตัวอย่างนี้ทำงานได้ด้วยตัวเองและรันผ่าน harness ได้ โดยจะลงทะเบียน LiberationSans-Regular.ttf ที่มาพร้อมกันและเรนเดอร์ผ่าน DocumentFactory ดังนั้นรีจิสทรีที่เติมข้อมูลไว้จึงเป็นรีจิสทรีที่ใช้งานอยู่ โดยอาศัยการทำซับเซ็ตอัตโนมัติขณะบันทึก
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\DocumentFactory;use NextPDF\Graphics\ImageRegistry;use NextPDF\Typography\FontRegistry;
// A bundled TrueType test fixture keeps this recipe self-contained.// Replace with a font you have the right to embed.$fontPath = __DIR__ . '/../../fonts/test-fixtures/LiberationSans/LiberationSans-Regular.ttf';if (!is_file($fontPath)) { // Fall back to the repository-relative fixture location. $fontPath = dirname(__DIR__, 2) . '/fonts/test-fixtures/LiberationSans/LiberationSans-Regular.ttf';}
$fontRegistry = new FontRegistry();$fontRegistry->register($fontPath, alias: 'LiberationSans');
$imageRegistry = new ImageRegistry(maxCacheBytes: 0);$documentFactory = new DocumentFactory($fontRegistry, $imageRegistry);
$doc = $documentFactory->create();$doc->setTitle('Embedded Subset Font');$doc->addPage();
$doc->setFont('LiberationSans', '', 20);$doc->cell(0, 14, 'Embedded TrueType face', newLine: true);
$doc->setFont('LiberationSans', '', 12);$doc->multiCell(0, 7, 'Only the glyphs used by this document are embedded. ' . 'The writer subsets the font program and rewrites the BaseFont with a ' . 'deterministic six-letter subset prefix, for example ABCDEF+LiberationSans.');
$out = getenv('NEXTPDF_COOKBOOK_OUTPUT');$doc->save($out !== false ? $out : __DIR__ . '/embed-and-subset-fonts.pdf');
echo "Wrote embed-and-subset-fonts.pdf\n";STDOUT ที่คาดไว้:
Wrote embed-and-subset-fonts.pdfเมื่อต้องการยืนยันซับเซ็ต ให้เปิดผลลัพธ์แล้วตรวจสอบดิกชันนารีฟอนต์ โดย BaseFont จะแสดงเป็น <TAG>+LiberationSans และตัวบรรยายฟอนต์มี FontFile2 การรัน qpdf --check จะรายงานว่าไม่พบข้อผิดพลาดเชิงโครงสร้าง
กรณีขอบและข้อควรระวัง
หัวข้อที่มีชื่อว่า “กรณีขอบและข้อควรระวัง”- ความเป็นเจ้าของรีจิสทรี
Document::createStandalone()จะสร้างรีจิสทรีของตัวเอง ฟอนต์ที่ลงทะเบียนไว้ในFontRegistryตัวอื่นจะไม่ปรากฏ ให้ใช้DocumentFactoryเพื่อส่งรีจิสทรีของคุณ ตามตัวอย่างสำหรับการใช้งานจริง - สิทธิ์ในการฝัง การทำซับเซ็ตไม่เปลี่ยนเงื่อนไขสัญญาอนุญาต ให้ฝังเฉพาะฟอนต์ที่ได้รับอนุญาตให้ฝังเท่านั้น ฟอนต์บางตัวตั้งค่าบิตเพื่อจำกัดการฝัง ตัวแจงจะอ่านบิตเหล่านั้น แต่ผู้ผสานรวมยังรับผิดชอบต่อการปฏิบัติตามข้อกำหนด
- เส้นทาง CFF/OpenType หน้าฟอนต์
.otfและ CFF จะทำซับเซ็ตด้วยCffSubsetterไม่ใช่FontSubsetterพฤติกรรมและการเขียนแท็กซับเซ็ตใหม่นั้นเทียบเท่ากัน มีเพียงเส้นทางของโค้ดเท่านั้นที่แตกต่างกัน - ขนาดไม่ลดลง บางครั้งซับเซ็ตไม่ได้เล็กกว่าต้นฉบับ กรณีนี้อาจเกิดขึ้นกับฟอนต์ขนาดเล็กมากหรือเมื่อใช้ glyph ทั้งหมด ในกรณีดังกล่าว ตัวเขียนจะฝังโปรแกรมฟอนต์ต้นฉบับโดยไม่มีแท็กซับเซ็ต นี่เป็นพฤติกรรมที่ถูกต้อง ไม่ใช่ความล้มเหลว
- ฟอนต์ CJK หน้าฟอนต์จีน ญี่ปุ่น และเกาหลี (CJK) ขนาดใหญ่ใช้กลยุทธ์การทำซับเซ็ตแบบเป็นลำดับชั้นตาม ADR-008 โดยใช้กระบวนการย่อยที่แยกเป็นเอกเทศและกลไกสำรองแบบ PHP-native ดู กำหนดข้อความ CJK ด้วยการเข้ารหัสที่รับรู้ cmap สำหรับรายละเอียดเฉพาะของ CJK และสถานะของไปป์ไลน์ปัจจุบัน
ประสิทธิภาพ
หัวข้อที่มีชื่อว่า “ประสิทธิภาพ”การแจงจะอ่านตารางฟอนต์เพียงรอบเดียว และต้นทุนของการทำซับเซ็ตจะเพิ่มตามจำนวน glyph หน้าฟอนต์ละติน (ที่ไม่ใช่ CJK) จะทำซับเซ็ตในกระบวนการเดียวกันและอยู่ในงบประมาณ wall_ms: 1500, peak_mb: 96 หน้าฟอนต์ CJK ขนาดใหญ่จะถูกส่งต่อไปยังกระบวนการย่อยที่แยกเป็นเอกเทศ พร้อมการหมดเวลาตามเวลานาฬิกาจริงสองวินาที และกลไกสำรองแบบ PHP-native (ADR-008) การส่งต่อนี้ทำให้การทำซับเซ็ตที่ช้าหรือล่มไม่สามารถบล็อกผู้เรียกได้
หมายเหตุด้านความปลอดภัย
หัวข้อที่มีชื่อว่า “หมายเหตุด้านความปลอดภัย”ไฟล์ฟอนต์เป็นข้อมูลนำเข้าไบนารีที่ไม่น่าเชื่อถือ ตัวแจงจะปฏิเสธเส้นทางแบบ stream-wrapper และไบต์ null กระบวนการย่อยสำหรับการทำซับเซ็ต CJK ทำงานโดยไม่สืบทอดการเชื่อมต่อฐานข้อมูล ตัวจัดการไฟล์ หรือสถานะของเฟรมเวิร์ก และใช้กลไกสำรองอย่างปลอดภัยเมื่อเกิดการล่มหรือหมดเวลา (ADR-008) ตรวจสอบแหล่งที่มาของฟอนต์ที่รับมาจากผู้ใช้ปลายทาง
ความสอดคล้องตามมาตรฐาน
หัวข้อที่มีชื่อว่า “ความสอดคล้องตามมาตรฐาน”| ข้อความระบุ | ข้อกำหนด | ข้อ | รหัสอ้างอิง (reference_id) |
|---|---|---|---|
| BaseFont/FontName ของซับเซ็ตฟอนต์มีคำนำหน้าซับเซ็ตที่เป็นตัวพิมพ์ใหญ่หกตัวและเชื่อมด้วยเครื่องหมายบวก | ISO 32000-2 | iso32000_2_sec9#x1.x66.p2 | |
| โปรแกรมฟอนต์ TrueType ที่ฝังไว้จะถูกจัดเก็บเป็น FontFile2 ในตัวบรรยายฟอนต์ | ISO 32000-2 | iso32000_2_sec9#x1.x65.p15 |
สูตรนี้แสดงวิธีที่ NextPDF ฝังและทำซับเซ็ตหน้าฟอนต์ TrueType และสร้างคำนำหน้าซับเซ็ตที่สอดคล้องตามมาตรฐาน โดยไม่ได้รับรองการปฏิบัติตามสัญญาอนุญาตของฟอนต์ สิทธิ์ในการฝังเป็นความรับผิดชอบของผู้นำไปผสานรวม
บริบทเชิงพาณิชย์
หัวข้อที่มีชื่อว่า “บริบทเชิงพาณิชย์”ไม่มี