ข้ามไปยังเนื้อหา

ฝังและทำซับเซ็ตฟอนต์ TrueType

ลงทะเบียนฟอนต์ TrueType เรนเดอร์ข้อความด้วยฟอนต์นั้น แล้วให้ตัวเขียนฝังเฉพาะซับเซ็ตที่จำเป็น สูตรนี้ใช้เส้นทางเนื้อหาเดียวกับ examples/04-text-and-fonts.php โดยเพิ่มฟอนต์ TrueType (.ttf) ที่ลงทะเบียนไว้

Terminal window
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) ให้เป็นมาตรฐาน แทนที่จะยืนยันแบบไบต์ต่อไบต์

  • FontRegistry::register(string $fontFile, string $alias = '', int $fontIndex = 0): FontInfoNextPDF\Typography\FontRegistry
  • setFont(string $family, string $style = '', float $size = 12.0): staticNextPDF\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-2iso32000_2_sec9#x1.x66.p2
โปรแกรมฟอนต์ TrueType ที่ฝังไว้จะถูกจัดเก็บเป็น FontFile2 ในตัวบรรยายฟอนต์ISO 32000-2iso32000_2_sec9#x1.x65.p15

สูตรนี้แสดงวิธีที่ NextPDF ฝังและทำซับเซ็ตหน้าฟอนต์ TrueType และสร้างคำนำหน้าซับเซ็ตที่สอดคล้องตามมาตรฐาน โดยไม่ได้รับรองการปฏิบัติตามสัญญาอนุญาตของฟอนต์ สิทธิ์ในการฝังเป็นความรับผิดชอบของผู้นำไปผสานรวม

ไม่มี