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

Contracts / Document

Miền tài liệu chứa các contract dùng để tạo đầu ra Portable Document Format (PDF): PdfDocumentInterface cho nội dung, DocumentFactoryInterface để tạo tài liệu an toàn trong worker, các contract registry phông chữ và hình ảnh, cùng ba enum về đầu ra và bố cục. Tất cả đều stable kể từ 1.0.0 hoặc 1.7.0.

Terminal window
composer require nextpdf/core:^3

PdfDocumentInterface là bề mặt giao diện lập trình ứng dụng (API) chính. Nó định nghĩa cách quản lý trang, chọn phông chữ, bố cục văn bản dạng ô và đa ô, kết xuất Hypertext Markup Language (HTML), nhúng hình ảnh và tạo đầu ra cuối cùng. Mỗi phương thức trả về static, nên bạn có thể nối chuỗi các lệnh gọi. Document::createStandalone() trả về một thực thể cụ thể thỏa mãn giao diện đó. Hãy khai báo kiểu theo giao diện này trong các dịch vụ của bạn để phần nội bộ của engine vẫn có thể được thay thế.

Việc tạo tài liệu có hai hướng. Trong một yêu cầu PHP FastCGI Process Manager (PHP-FPM) cổ điển, createStandalone() tạo một tài liệu khép kín với các registry riêng. Các worker chạy lâu, bao gồm RoadRunner, Swoole và Laravel Octane, dùng hướng còn lại. Trong mô hình đó, DocumentFactoryInterface::create() trả về một Document mới, chỉ dùng một lần. Tài liệu đọc từ các registry tồn tại trong suốt vòng đời tiến trình nhưng không bao giờ thay đổi chúng. Factory giữ các singleton FontRegistryInterfaceImageRegistryInterface. Mỗi tài liệu nhận một ngữ cảnh kết xuất và writer riêng. Cách này khoanh vùng lỗi: một tài liệu không thể làm hỏng trạng thái dùng chung mà tài liệu khác đang phụ thuộc.

Các contract registry giúp worker chạy nhanh. FontRegistryInterface phân tích một tệp phông chữ một lần và lưu cache siêu dữ liệu đã phân tích trong suốt vòng đời tiến trình. Bạn có thể khóa nó sau khi khởi động ấm để lưu lượng production không thể thay đổi registry. ImageRegistryInterface lưu cache dữ liệu nhị phân hình ảnh đã giải mã theo một chính sách least-recently-used có giới hạn. Siêu dữ liệu hình ảnh vẫn được giữ lại ngay cả sau khi phần nhị phân bị loại bỏ. Cả hai registry đều cung cấp memoryUsage() để lập kế hoạch dung lượng. ImageRegistryInterface mở rộng ResettableService, contract loại bỏ dữ liệu đã cache mà không hủy siêu dữ liệu cấu trúc. Một worker có thể xóa cache hình ảnh khi bộ nhớ chịu áp lực mà vẫn tiếp tục phục vụ.

Ba enum hoàn thiện miền này. OutputDestination chọn hiển thị nội tuyến, buộc tải xuống, ghi vào hệ thống tệp hoặc trả về chuỗi thô. Orientation chọn hướng dọc hoặc ngang. Alignment chọn văn bản căn trái, căn giữa, căn phải hoặc căn đều. Mỗi enum dùng mã TCPDF cũ làm giá trị enum, nên cầu nối compat-tcpdf ánh xạ gọn gàng. Cam kết tương thích ngược cho các enum này là chỉ bổ sung. Không case nào bị loại bỏ. Case mới có thể xuất hiện trong một bản phát hành minor.

KiểuLoạiThành viên chínhĐộ ổn địnhTừ phiên bản
PdfDocumentInterfaceinterfaceaddPage(), setMargins(), setFont(), cell(), multiCell(), writeHtml(), image(), output(), save()stable1.0.0
DocumentFactoryInterfaceinterfacecreate(?Config): Documentstable1.7.0
ResettableServiceinterfacereset(): voidstable1.7.0
FontRegistryInterfaceinterfaceregister(), get(), warmup(), lock(), isLocked(), registerFromBinary(), memoryUsage()stable1.7.0
ImageRegistryInterfaceinterfaceload(), loadFromString(), getMetadata(), memoryUsage() (mở rộng ResettableService)stable2.0.0
OutputDestinationenum (string)Inline, Download, File, Stringstable1.0.0
Orientationenum (string)Portrait, Landscapestable1.0.0
Alignmentenum (string)Left, Center, Right, Justifystable1.0.0

Trang typography ghi lại đầy đủ FontRegistryInterfaceImageRegistryInterface. Trang này tập trung vào vai trò của chúng trong vòng đời tạo tài liệu.

examples/01-hello-world.php
<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();
$doc->setTitle('Hello World');
$doc->addPage();
$doc->setFont('helvetica', '', 24);
$doc->cell(0, 15, 'Hello, NextPDF!', newLine: true);
$doc->setFont('helvetica', '', 12);
$doc->cell(0, 10, 'This is a minimal PDF generated with NextPDF.', newLine: true);
$doc->save(__DIR__ . '/output/01-hello-world.pdf');
examples/02-pdf-factory.php
<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Core\PdfFactory;
use NextPDF\ValueObjects\{Margin, PageSize};
$factory = PdfFactory::new()
->withPageSize(PageSize::A4())
->withMargins(new Margin(15.0, 15.0, 15.0, 15.0))
->withCompress(true)
->withLang('en');
// The same configured factory creates independent documents.
$doc = $factory->create();
$doc->setTitle('PdfFactory Example');
$doc->setAuthor('NextPDF');
$doc->addPage();
$doc->setFont('helvetica', '', 16);
$doc->cell(0, 12, 'Created via PdfFactory', newLine: true);
$doc2 = $factory->create();
$doc2->addPage();
$doc2->setFont('helvetica', '', 12);
$doc2->cell(0, 10, 'Second document from the same factory.');
$doc->save(__DIR__ . '/output/02-pdf-factory.pdf');

PdfFactory là builder bất biến. Mỗi lệnh gọi with*() trả về một thực thể mới. Bên dưới, nó kết hợp một DocumentFactoryInterface, nên mô hình registry cho worker ở phần tổng quan được áp dụng mà không cần nối dây thêm.

  • createStandalone() tạo các registry riêng. Trong vòng lặp worker, điều đó sẽ phân tích lại mọi phông chữ ở mỗi yêu cầu. Thay vào đó, hãy dùng DocumentFactoryInterface với các registry dùng chung.
  • Một Document được thiết kế để dùng một lần. Việc dùng lại cùng một thực thể cho nhiều tài liệu logic sẽ làm rò rỉ trạng thái. Hãy gọi create() cho mỗi tài liệu và để bộ thu gom rác thu hồi nó.
  • FontRegistryInterface::lock() khiến register(), addFontDirectory(), và warmup() ném ra LogicException. Hãy khóa sau khi khởi động ấm, và đừng bao giờ khóa trong lúc xử lý yêu cầu.
  • OutputDestination::File ghi vào hệ thống tệp của máy chủ và trả về byte thô. save() là đường dẫn tệp tường minh. Đừng trộn lẫn cả hai cho cùng một tài liệu.
  • cell() nhận bool|string cho đối số viền để tương thích với TCPDF. Chuỗi rỗng không giống với false. Hãy truyền đúng giá trị có kiểu mà bạn muốn.

Các registry phông chữ và hình ảnh biến miền tài liệu thành một hệ thống bị giới hạn bởi bộ nhớ, thay vì theo từng yêu cầu. Việc phân tích phông chữ ở yêu cầu đầu tiên chiếm phần lớn thời gian. performance_budget là 1.500 ms wall và 64 MB đỉnh trên ba tài liệu trong ví dụ worker. Gần như toàn bộ ngân sách đó dành cho lần phân tích phông chữ đầu tiên. Sau khi khởi động ấm, phần việc mà contract gây ra trên mỗi tài liệu là O(1): một lần tra registry và một lần cấp phát ngữ cảnh. memoryUsage() trên một trong hai registry trả về một MemoryReport để lập kế hoạch dung lượng trực tiếp. ResettableService::reset() giới hạn bộ nhớ đỉnh khi tải kéo dài.

Các contract tài liệu không có bề mặt mật mã, nhưng có hai rủi ro vận hành cần lưu ý. Thứ nhất, image() nhận một đường dẫn hoặc Uniform Resource Locator (URL). Trong các tình huống đầu vào không đáng tin, hãy ràng buộc việc tải từ xa qua ExternalResourcePolicyInterface (xem trang security-policy) thay vì truyền trực tiếp các URL do người dùng kiểm soát. Thứ hai, writeHtml() là điểm vào cho pipeline HTML. Mã đánh dấu không đáng tin phải đi qua một HtmlSecurityPolicyInterface trước khi kết xuất. Lớp tài liệu tự nó không làm sạch dữ liệu. Đó là trách nhiệm của miền security-policy, và vì đây là một contract, bạn có thể cung cấp một chính sách nghiêm ngặt hơn mà không cần fork.

Các contract tài liệu hiện thực cấu trúc tài liệu PDF 2.0 như được định nghĩa trong ISO 32000-2. Việc xử lý đầu ra, trang và phông chữ tạo ra các đối tượng gián tiếp và một luồng tham chiếu chéo theo ISO 32000-2 §7. Lớp writer phát ra nội dung theo contract của lớp engine, được ghi lại trong bản ghi quyết định kiến trúc (ADR-010). Trang này không khẳng định bất kỳ tuyên bố ở cấp điều khoản nào ngoài việc tuân thủ về cấu trúc. Các trang trích xuất và trợ năng ghi lại việc tuân thủ PDF/A và PDF/UA, đồng thời mang theo các bảng quy chuẩn.