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

ฟอนต์แบบกำหนดเอง: สัญญาส่วนขยายสำหรับ FontRegistry

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

Terminal window
composer require nextpdf/core:^3

font registry เป็น singleton ที่มีอายุนานกว่าอินสแตนซ์ Document แต่ละตัว โดยจัดเก็บเฉพาะข้อมูล PHP ล้วน ไม่มี resource handle หรือออบเจ็กต์ส่วนขยาย จึงแชร์ข้ามคำขอต่างๆในเวิร์กเกอร์ที่ทำงานต่อเนื่องยาวนานได้

ใช้วิธีลงทะเบียนได้หนึ่งในสามแบบต่อไปนี้:

  • จากไฟล์ register() แยกวิเคราะห์ไฟล์ .ttf, .otf, .ttc, หรือ .pfb แล้วคืนค่าเมทาดาตา สำหรับ TrueType Collection ให้ส่งดัชนี sub-font
  • จากไดเรกทอรี addFontDirectory() เพิ่มพาธการค้นหาที่เอนจินจะสแกนเมื่อแปลงตระกูลฟอนต์จากชื่อ
  • จากข้อมูลไบนารี registerFromBinary() แยกวิเคราะห์ไบต์ TrueType หรือ OpenType แบบดิบ ใช้วิธีนี้สำหรับบริดจ์ @font-face เมื่อฟอนต์มาจาก Uniform Resource Identifier (URI) แบบ data: หรือจากแหล่งระยะไกล

เพื่อลดความหน่วงของคำขอแรก ให้เรียก warmup() ตอนเริ่มต้นเวิร์กเกอร์เพื่อแยกวิเคราะห์ฟอนต์ชุดหนึ่งล่วงหน้า จากนั้นเรียก lock() หลังจาก lock() เมธอดแก้ไขทุกตัวจะโยน LogicException ได้แก่ register(), addFontDirectory(), warmup(), registerBase14(), และ registerFromBinary() เมธอดค้นหายังคงใช้งานได้: get(), has(), all(), และ getSearchDirectories() การล็อกนี้ช่วยปกป้องเวิร์กเกอร์ในโปรดักชันโดยรับประกันว่าไม่มีคำขอใดเปลี่ยนแปลงชุดฟอนต์ที่ใช้ร่วมกันได้

ในกรณีส่วนใหญ่ คุณไม่จำเป็นต้องอิมพลีเมนต์ FontRegistryInterface เอนจินจัดเตรียมการอิมพลีเมนต์ไว้ และคุณเพียงเรียกใช้เท่านั้น ให้อิมพลีเมนต์เองเฉพาะเมื่อต้องการกลยุทธ์การแปลงฟอนต์แบบกำหนดเอง เช่น กลยุทธ์ที่รองรับด้วยที่เก็บแบบกำหนดที่อยู่ตามเนื้อหา ไม่ว่ากรณีใด สัญญายังคงเป็นขอบเขต

NextPDF\Contracts\FontRegistryInterface (เสถียร ตั้งแต่ 1.7.0):

เมธอดคืนค่าวัตถุประสงค์
register(string $fontFile, string $alias, int $fontIndex)FontInfoแยกวิเคราะห์และลงทะเบียนไฟล์ฟอนต์ โยนข้อยกเว้นเมื่อรีจิสทรีถูกล็อกหรือแยกวิเคราะห์ไฟล์ไม่ได้
registerFromBinary(string $fontData, string $alias)FontInfoลงทะเบียนฟอนต์จากไบต์ TrueType หรือ OpenType แบบดิบ
registerBase14(string $key, FontInfo $font)voidลงทะเบียนฟอนต์มาตรฐาน Base 14 ที่สร้างไว้ล่วงหน้า
addFontDirectory(string $directory)voidเพิ่มไดเรกทอรีสำหรับค้นหาฟอนต์
warmup(array $fontFiles)voidแยกวิเคราะห์ชุดฟอนต์ล่วงหน้าตอนเริ่มต้นเวิร์กเกอร์
lock()voidตรึงรีจิสทรีเพื่อป้องกันการแก้ไขเพิ่มเติม
isLocked()boolรายงานว่ารีจิสทรีถูกล็อกหรือไม่
get(string $family, string $style)FontInfo | nullค้นหาฟอนต์ตามตระกูลและสไตล์
has(string $key)boolตรวจสอบว่ามีคีย์การลงทะเบียนอยู่หรือไม่
all()array<string, FontInfo>คืนค่าฟอนต์ทั้งหมดที่ลงทะเบียนไว้
getSearchDirectories()list<string>คืนค่าไดเรกทอรีสำหรับค้นหาตามลำดับ
memoryUsage()MemoryReportรายงานการใช้หน่วยความจำของรีจิสทรีในปัจจุบัน
<?php
declare(strict_types=1);
use NextPDF\Contracts\FontRegistryInterface;
/** @var FontRegistryInterface $fonts */
$info = $fonts->register('/srv/fonts/Inter-Regular.ttf', 'Inter');
if (!$fonts->has('inter')) {
throw new RuntimeException('Inter failed to register');
}

เมื่อเริ่มต้นเวิร์กเกอร์ ให้วอร์มชุดฟอนต์ ล็อกรีจิสทรี และติดตามการโหลดแต่ละครั้งเพื่อการติดตามสิทธิ์การใช้งาน ตัวอย่างนี้ใช้เฉพาะชนิดข้อมูลสาธารณะเท่านั้น

<?php
declare(strict_types=1);
use NextPDF\Contracts\FontRegistryInterface;
use NextPDF\Event\Content\FontLoadedEvent;
use NextPDF\Event\EventDispatcher;
use NextPDF\Event\ListenerProvider;
use Psr\Log\LoggerInterface;
final class FontWarmup
{
/** @param list<string> $fontFiles */
public function __construct(
private readonly FontRegistryInterface $fonts,
private readonly LoggerInterface $logger,
private readonly array $fontFiles,
) {}
public function boot(): EventDispatcher
{
$listeners = new ListenerProvider();
$listeners->addListener(
FontLoadedEvent::class,
function (FontLoadedEvent $event): void {
$this->logger->info('font.loaded', [
'family' => $event->family,
'style' => $event->style,
'type' => $event->fontType->name,
]);
},
);
if (!$this->fonts->isLocked()) {
$this->fonts->warmup($this->fontFiles);
$this->fonts->lock();
}
return new EventDispatcher($listeners);
}
}
  • รีจิสทรีที่ถูกล็อก หลังจาก lock() การแก้ไขใดๆจะโยน LogicException ตรวจสอบ isLocked() ก่อนวอร์มแบบมีเงื่อนไขในเวิร์กเกอร์ที่ถูกนำกลับมาใช้ใหม่
  • การลงทะเบียนแบบไบนารีไม่ถูกแคชตามคีย์ registerFromBinary() เขียนลงไฟล์ชั่วคราวแล้วแยกวิเคราะห์ไฟล์นั้น ใช้ FontInfo ที่คืนกลับมาเป็นแฮนเดิล
  • ดัชนีของ TrueType Collection (TTC) สำหรับ TrueType Collection อาร์กิวเมนต์ตัวที่สามของ register() เป็นตัวเลือก sub-font ค่าเริ่มต้น 0 เลือกหน้าฟอนต์แรก
  • การแปลงตระกูลฟอนต์ get() คืนค่า null สำหรับคู่ของตระกูลและสไตล์ที่ไม่รู้จัก อย่าสันนิษฐานเด็ดขาดว่าผลลัพธ์จะไม่เป็น null

warmup() ย้ายต้นทุนการแยกวิเคราะห์จากคำขอแรกไปยังขั้นเริ่มต้นเวิร์กเกอร์ เมธอดของรีจิสทรีใช้ข้อมูล PHP ล้วน และการค้นหาเป็นการอ่านแมปในเวลาคงที่ เรียก memoryUsage() เพื่อประเมินขนาดชุดฟอนต์ที่อยู่ในหน่วยความจำของเวิร์กเกอร์เทียบกับงบประมาณหน่วยความจำของคุณ

ฟอนต์ที่ลงทะเบียนแล้วสามารถฝังลงในเนื้อหา Portable Document Format (PDF) ได้ ตรวจสอบความถูกต้องของแหล่งที่มาของฟอนต์ก่อนลงทะเบียน อย่าลงทะเบียนข้อมูลไบนารีที่ผู้โจมตีควบคุมโดยไม่ตรวจสอบขนาดและรูปแบบ ใช้ฮุก FontLoadedEvent เพื่อบังคับใช้การปฏิบัติตามสิทธิ์การใช้งานฟอนต์ และบันทึกว่าเอกสารฝังหน้าฟอนต์ใดบ้าง

ไม่มีข้อกำหนดเชิงบรรทัดฐานด้านการลงนามหรือการจัดเก็บถาวรที่นำมาใช้ การฝังฟอนต์และการทำ subset สอดคล้องกับโมเดลฟอนต์ของ PDF 2.0 ตัวทำ subset ภายในเป็นผู้รับผิดชอบความสอดคล้องนั้น ไม่ใช่สัญญานี้

NextPDF Enterprise เพิ่มการรับรองสิทธิ์การใช้งานฟอนต์และนโยบายการทำ subset ที่ผ่านการตรวจสอบไว้บน FontRegistryInterface ตัวเดียวกัน โค้ดลงทะเบียนของคุณทำงานเหมือนกันในทุกรุ่นเพราะสัญญาเป็นขอบเขต

อภิธานศัพท์ให้นิยามของ font registry, image registry, และ event listener ดูคำนิยามอย่างเป็นทางการได้ในอภิธานศัพท์ที่เผยแพร่