Bỏ qua để đến nội dung

Contracts / Typography

Miền typography định nghĩa registry phông chữ và các contract tiền xử lý văn bản: FontRegistryInterface, TextPreprocessorInterface, cùng các value object bất biến TextPreprocessResultTextSegment. Tất cả đều có trạng thái stable.

Terminal window
composer require nextpdf/core:^3

FontRegistryInterface là kho lưu trữ phông chữ tồn tại trong suốt vòng đời của tiến trình. Nó đăng ký phông chữ TrueType, OpenType, TrueType Collection (TTC) hoặc Printer Font Binary (PFB), rồi trả về metadata FontInfo đã được phân tích cú pháp. Vì registry tồn tại lâu hơn từng tài liệu riêng lẻ, một worker chỉ cần phân tích cú pháp mỗi phông chữ một lần. Bạn có thể nạp sẵn một loạt phông chữ khi khởi động, sau đó khóa registry để lưu lượng production không thể thay đổi nó. Registry đã khóa sẽ ném LogicException khi gọi register(), addFontDirectory() hoặc warmup(); thao tác tra cứu vẫn khả dụng. Registry cũng chấp nhận các byte phông chữ thô thông qua registerFromBinary(). Cầu nối @font-face dùng phương thức này để đăng ký phông chữ lấy từ nguồn từ xa hoặc data URI (uniform resource identifier). Registry chỉ lưu trữ dữ liệu PHP thuần túy, không có resource handle, nên có thể chia sẻ trong một worker pool.

Engine nhúng và tạo tập con cho mọi phông chữ mà nó sử dụng. Một chương trình phông chữ được nhúng sẽ đi kèm bên trong tệp Portable Document Format (PDF), nên tài liệu được kết xuất nhất quán trong mọi trình xem, không phụ thuộc vào các phông chữ hệ thống đã cài đặt — ISO 32000-2 §9. Một tập con phông chữ chỉ chứa các glyph mà tài liệu thực sự tham chiếu. Điều này đặc biệt quan trọng với nội dung tiếng Trung, tiếng Nhật và tiếng Hàn (CJK) hoặc nội dung giàu Unicode khác — ISO 32000-2 §9. Contract của registry phơi bày metadata đã phân tích cú pháp để các giai đoạn tạo tập con và nhúng sử dụng.

TextPreprocessorInterface chặn văn bản trước khi văn bản đi vào bố cục glyph, tạo tập con phông chữ, bản đồ ký tự ToUnicode (CMap) và cây cấu trúc. Vị trí này chính là thuộc tính bảo mật: bộ tiền xử lý có chức năng kiểm duyệt nội dung sẽ loại bỏ nội dung đó trước khi nó có thể chạm tới luồng nội dung, tập con phông chữ hoặc metadata. Contract này mang theo hai bất biến. Bộ tiền xử lý không được đưa vào các ký tự ảnh hưởng đến bố cục, và phải giữ nguyên thứ tự đọc logic; trách nhiệm của nó là thay thế nội dung, không phải bố trí lại. Kết quả là một TextPreprocessResult bất biến với danh sách có thứ tự gồm các giá trị TextSegment. Một phân đoạn hoặc là pass-through, hoặc đã bị kiểm duyệt. Với phân đoạn đã bị kiểm duyệt, văn bản hiển thị phụ thuộc vào chế độ che: rỗng đối với hình chữ nhật hộp đen, dấu hoa thị khớp với độ dài gốc, hoặc một nhãn cố định. originalCharCount trên phân đoạn là một gợi ý đo lường không thể đảo ngược, chỉ dùng để định kích thước hình chữ nhật kiểm duyệt. Tuyệt đối không được dùng giá trị này để tái dựng nội dung gốc.

KiểuLoạiThành viên chínhĐộ ổn địnhTừ phiên bản
FontRegistryInterfaceinterfaceregister(), get(), has(), all(), addFontDirectory(), warmup(), lock(), isLocked(), registerBase14(), registerFromBinary(), memoryUsage()stable1.7.0
TextPreprocessorInterfaceinterfaceprocess(string): TextPreprocessResultstable1.9.0
TextPreprocessResultfinal readonly class$segments, hasRedactions(), getDisplayText()stable1.9.0
TextSegmentfinal readonly class$displayText, $isRedacted, $originalCharCount, $fillColorstable1.9.0

TextPreprocessResultTextSegment đóng băng chữ ký constructor cùng các thuộc tính public của chúng; có thể thêm phương thức mới, nhưng không được thay đổi các thuộc tính hiện có.

examples/04-text-and-fonts.php
<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();
$doc->addPage();
$doc->setFont('helvetica', 'B', 18);
$doc->cell(0, 12, 'Bold heading', newLine: true);
$doc->setFont('helvetica', '', 11);
$doc->multiCell(0, 7, 'Body text rendered with a registered font.');
$doc->save(__DIR__ . '/output/04-text-and-fonts.pdf');

setFont() phân giải họ phông chữ thông qua FontRegistryInterface. Tài liệu độc lập sử dụng registry riêng. Trong một worker, hãy chia sẻ một registry chung; xem trang document.

examples/contracts/typography-production.php
<?php
declare(strict_types=1);
require_once __DIR__ . '/../../vendor/autoload.php';
use NextPDF\Contracts\FontRegistryInterface;
use NextPDF\Contracts\TextPreprocessorInterface;
use NextPDF\Exception\NextPdfException;
use Psr\Log\LoggerInterface;
final readonly class FontWarmupService
{
public function __construct(
private FontRegistryInterface $fonts,
private TextPreprocessorInterface $preprocessor,
private LoggerInterface $logger,
) {}
/**
* Warm a font set at boot, then lock the registry.
*
* @param list<string> $fontFiles Absolute paths to font files.
*/
public function boot(array $fontFiles): void
{
try {
$this->fonts->warmup($fontFiles);
$this->fonts->lock();
} catch (NextPdfException $e) {
$this->logger->error('Font warmup failed', ['error' => $e->getMessage()]);
throw $e;
}
}
public function redact(string $text): string
{
$result = $this->preprocessor->process($text);
return $result->hasRedactions()
? $result->getDisplayText()
: $text;
}
}

warmup() rồi đến lock() là trình tự khởi động của worker. Sau lock(), mọi thao tác thay đổi sẽ ném ngoại lệ. Tra cứu vẫn tiếp tục phục vụ lưu lượng.

  • Registry đã khóa sẽ từ chối mọi phương thức thay đổi. Hãy nạp sẵn và khóa registry khi khởi động; đừng bao giờ gọi register() trong khi xử lý yêu cầu.
  • registerFromBinary() ghi các byte phông chữ vào một tệp tạm thời trước khi phân tích cú pháp. Dữ liệu phông chữ không đáng tin cậy là bề mặt tấn công đối với bộ phân tích cú pháp — hãy kiểm soát dữ liệu đó qua ExternalResourcePolicyInterface (xem trang security-policy).
  • Một TextPreprocessor không được thêm ngắt dòng, ký tự xuống dòng (carriage return) hay tab. Những ký tự đó làm thay đổi bố cục và phá vỡ bất biến đầu tiên của contract.
  • TextSegment::$originalCharCount chỉ là gợi ý về chiều rộng. Dùng nó để suy ra nội dung gốc sẽ vô hiệu hóa việc kiểm duyệt và vi phạm bất biến thứ ba của contract.
  • TextPreprocessResult::getDisplayText() trả về chuỗi rỗng cho các phân đoạn hộp đen theo thiết kế. Đừng coi phân đoạn rỗng là lỗi tiền xử lý.

Việc phân tích cú pháp phông chữ chiếm phần lớn chi phí ở lần sử dụng đầu tiên; registry dàn chi phí đó xuống còn một lần cho mỗi tiến trình. Sau khi nạp sẵn, get()has() là các thao tác tra cứu map O(1). memoryUsage() trả về một MemoryReport để worker có thể theo dõi bộ nhớ đệm phông chữ so với ngân sách của nó. Tiền xử lý văn bản có độ phức tạp tuyến tính theo độ dài đầu vào. Danh sách phân đoạn thêm một chi phí phụ có giới hạn, tỷ lệ với số lượng kết quả khớp cần kiểm duyệt. performance_budget 1500 ms thời gian thực và 64 MB đỉnh bao gồm việc nạp sẵn cho một bộ phông chữ điển hình cộng với việc kết xuất tài liệu. Chi phí tạo tập con tỷ lệ với số lượng glyph thực sự được sử dụng, chứ không phải toàn bộ bảng glyph của phông chữ. Vì vậy, việc tạo tập con giúp giảm kích thước đầu ra và chi phí kết xuất cho nội dung CJK.

Miền typography có hai bề mặt liên quan đến bảo mật. Bề mặt thứ nhất là đầu vào phông chữ: registerFromBinary() phân tích cú pháp các byte tùy ý. Dữ liệu phông chữ không đáng tin cậy phải đi qua một ExternalResourcePolicyInterface giới hạn kích thước tệp và số lượng glyph trước khi chạm tới bộ phân tích cú pháp. Bề mặt thứ hai là kiểm duyệt: TextPreprocessorInterface chạy trước bố cục glyph, tạo tập con phông chữ, CMap ToUnicode và cây cấu trúc, nên nội dung đã bị kiểm duyệt không bao giờ đi vào sản phẩm được kết xuất. Kiểm duyệt bằng lớp phủ tại thời điểm vẽ sẽ làm rò rỉ văn bản gốc trong luồng nội dung và tập con. Vị trí của contract ngăn loại khiếm khuyết đó xảy ra. Gợi ý đo lường trên một phân đoạn được cố ý thiết kế để không thể đảo ngược. Hãy coi mọi phông chữ hoặc văn bản được cung cấp từ bên ngoài là không đáng tin cậy.

Tuyên bốTiêu chuẩnĐiều khoảnBằng chứng
Mọi phông chữ mà tài liệu sử dụng đều được nhúng để tài liệu kết xuất mà không phụ thuộc vào phông chữ hệ thống.ISO 32000-2§9
Phông chữ được nhúng được tạo tập con, giới hạn trong các glyph mà tài liệu tham chiếu.ISO 32000-2§9

Cả hai điều khoản đều được diễn giải lại. NextPDF không sao chép văn bản chuẩn tắc. PDF/A-4 bắt buộc nhúng mọi phông chữ. Mức tuân thủ đó được ghi lại trên các trang extraction và accessibility.

  • Contracts: 41 interface công khai (SPI) — tổng quan về service provider interface và các mức độ ổn định.
  • Contracts / Document — vai trò của registry trong vòng đời tài liệu.
  • Contracts / Security PolicyExternalResourcePolicyInterface kiểm soát các byte phông chữ không đáng tin cậy.
  • Typography — mô-đun định hình văn bản và bố cục.
  • Font — phân tích cú pháp, tạo tập con và nhúng phông chữ.
  • Text — đầu ra văn bản tiêu thụ kết quả của bộ tiền xử lý.