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

Pipeline kết xuất HTML

Khi bạn gọi writeHtml(), nó chạy một lượt tiến duy nhất trên HyperText Markup Language (HTML): token hóa đầu vào, phân giải @page cùng các kiểu, dàn nội dung và vẽ các toán tử Portable Document Format (PDF). Nó không giữ lại cây phần tử giữa các giai đoạn.

Terminal window
composer require nextpdf/core:^3

Pipeline kết xuất HTML chuyển HTML+CSS, tức HTML kết hợp với Cascading Style Sheets (CSS), thành các toán tử content-stream của PDF trong một lượt tiến duy nhất. Nó không dựng cây tài liệu để lưu giữ. Các giai đoạn dưới đây phản ánh HtmlParser::parse() trên main.

Giai đoạn 1 — Làm sạch và chuẩn hóa. HtmlParser::parse() từ chối đầu vào vượt quá 10 MB, loại bỏ ký tự điều khiển và chuẩn hóa ký tự xuống dòng: cả CRLF lẫn CR đơn đều trở thành LF, khớp với cách nguồn HTML được chuẩn hóa ký tự xuống dòng. Sau đó, nó đặt lại mọi trường thực thể, nên trạng thái từ lần gọi trước không thể bị mang sang.

Giai đoạn 2 — Trích xuất @page và các khối kiểu. Trình phân tích trước tiên trích xuất các khối <style>, rồi áp dụng các quy tắc @page đã phát hiện để cấu hình lại hình học trang. Việc này diễn ra trước khi xử lý bất kỳ token nào, vì kích thước trang ảnh hưởng đến mọi quyết định bố cục về sau.

Giai đoạn 3 — Token hóa. HtmlTokenizer::cleanHtml() chuẩn hóa khoảng trắng nhưng vẫn giữ nguyên nội dung <pre>. Sau đó, tokenize() tạo ra một list<HtmlToken> phẳng. Đây là danh sách token, không phải đồ thị node. Pipeline loại bỏ ngay các token văn bản chỉ chứa khoảng trắng. HtmlChildScanner::scan() dựng các bản đồ chỉ mục (số lượng phần tử con, số lượng thẻ, tính rỗng) trên danh sách phẳng, nên các bộ chọn cấu trúc không cần đến cây.

Giai đoạn 4 — Tiền quét :has() tùy chọn. Khi bạn bật tính năng thử nghiệm css.has, CssResolver::resolveHasSelectors() chạy một lượt tiền quét có giới hạn trên danh sách token để phân giải bộ chọn quan hệ. Bước có giới hạn và đã được ghi chép này là ngoại lệ đối với quy tắc một lượt.

Giai đoạn 5 — Xử lý token (kiểu, bố cục, vẽ). HtmlParser::processTokens() duyệt qua danh sách token một lần. Với mỗi phần tử, nó phân giải dòng kế thừa kiểu (các applicator của Layer 1 ghi HtmlStyleState), tính hình học (bố cục Layer 3) và phát ra các toán tử PDF (vẽ Layer 4). Việc kế thừa kiểu dùng một ngăn xếp HtmlStyleState theo cơ chế đẩy-và-lấy. Con trỏ (x, y, lề, độ dời luồng) di chuyển giữa các handler thông qua các snapshot HtmlBlockCursor.

Giai đoạn 6 — Trả về kết quả. parse() trả về một HtmlRenderResult bất biến, gồm luồng nội dung đã phát ra, vị trí con trỏ kết thúc và các khóa phông chữ đã dùng. Phía gọi (writeHtml()) chuyển con trỏ trở lại khung tọa độ của trang.

Để tìm hiểu cách phân tách bốn lớp bên trong Giai đoạn 5, hãy xem trang các giao kèo lớp. Để biết thuộc tính không lưu giữ cây và các giới hạn của nó, hãy xem trang các ràng buộc streaming.

Ký hiệuVị tríGiai đoạn
Document::writeHtml(string $html): staticsrc/Core/Concerns/HasTextOutput.phpĐiểm vào công khai
HtmlParser::parse(string $html): HtmlRenderResultsrc/Html/HtmlParser.phpĐiều phối tất cả các giai đoạn
HtmlTokenizer::cleanHtml() / tokenize()src/Html/HtmlTokenizer.phpGiai đoạn 3
HtmlChildScanner::scan()src/Html/HtmlChildScanner.phpBản đồ chỉ mục của Giai đoạn 3
CssResolver::resolveHasSelectors()src/Html/CssResolver.phpGiai đoạn 4 (có cổng kiểm soát)
HtmlRenderResult (stream, endX, endY, usedFontKeys)src/Html/HtmlRenderResult.phpGiai đoạn 6

Lấy nguồn từ examples/08-html-basic.php.

<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();
$doc->setTitle('HTML Basic');
$doc->addPage();
$doc->writeHtml('<h1 style="color:#1E3A8A;">HTML Rendering</h1><p>One pass.</p>');
$doc->save(__DIR__ . '/output/08-html-basic.pdf');

Kết xuất một báo cáo đã được định kiểu bằng một khối <style> nhúng. Pipeline trích xuất và áp dụng khối kiểu trước khi xử lý bất kỳ token nào.

<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Core\Document;
use NextPDF\Exception\HtmlParsingException;
function renderInvoice(string $bodyHtml, string $out): void
{
$doc = Document::createStandalone();
$doc->setTitle('Invoice');
$doc->addPage();
$html = '<style>@page { margin: 20mm; } '
. 'h1 { color: #1E3A8A; } '
. 'table { width: 100%; }</style>'
. $bodyHtml;
try {
$doc->writeHtml($html);
} catch (HtmlParsingException $e) {
// Sanitize/cap failures surface here. Do not retry.
throw $e;
}
$doc->save($out);
}

Trường hợp biên & những điểm cần lưu ý

Phần tiêu đề “Trường hợp biên & những điểm cần lưu ý”
  • @page được đọc trước token. Một quy tắc @page đặt sau nội dung vẫn được áp dụng, vì việc trích xuất kiểu diễn ra trước khi token hóa. Hình học trang được cố định trước Giai đoạn 5.
  • Khoảng trắng <pre> được giữ nguyên. cleanHtml() bảo vệ nội dung <pre>; pipeline gộp khoảng trắng ở những chỗ khác.
  • :has() có cổng kiểm soát. Nếu bạn không bật tính năng thử nghiệm css.has, Giai đoạn 4 sẽ không chạy và các bộ chọn :has() sẽ không khớp.
  • Một bộ đệm luồng duy nhất. Pipeline ghi vào một bộ đệm chuỗi duy nhất. Nó không bao giờ di chuyển nội dung đã ghi. Không có bước dàn lại bố cục.
  • Các giới hạn áp dụng giữa lượt. Các giới hạn về số phần tử và độ lồng nhau ném lỗi trong Giai đoạn 5, không phải trước đó. Một tài liệu có thể thất bại giữa chừng.

Pipeline duyệt với độ phức tạp O(số lượng token). Việc định kích thước cột bảng thêm một lượt quét hàng có giới hạn cho từng bảng (Giai đoạn 5, TableParser). Khi được bật, lượt tiền quét :has() thêm một lượt duyệt danh sách token có giới hạn (Giai đoạn 4). Bộ nhớ là O(độ sâu lồng nhau) cho ngăn xếp kiểu, không phải O(số lượng phần tử); xem các ràng buộc streaming. Phép đo chuẩn hiệu năng của pipeline kết xuất HTML dùng một cổng 5% để phòng ngừa suy giảm (công việc đã hợp nhất, PR #564). performance_budget theo từng trang (wall_ms: 1500, peak_mb: 64) là trần vận hành.

Giai đoạn 1 là ranh giới bảo mật đầu tiên: giới hạn đầu vào 10 MB, loại bỏ ký tự điều khiển và chuẩn hóa ký tự xuống dòng đều chạy trước khi token hóa. Trong Giai đoạn 5, DefaultHtmlSecurityPolicy kiểm soát các thẻ, thuộc tính, thuộc tính CSS và lược đồ URL được cho phép. Xem mô hình bảo mật của mô-đun HTML.

Việc chuẩn hóa ký tự xuống dòng tuân theo cách tiêu chuẩn HTML xử lý ký tự xuống dòng: CRLF và CR đơn trở thành LF. Tính tuân thủ CSS theo từng thuộc tính được ghi chép trong ma trận hỗ trợ CSS, còn hành vi dòng kế thừa được ghi chép trên css-resolver. Trang này không trình bày lại mức hỗ trợ theo từng thuộc tính.

Khả năng dành cho doanh nghiệp. Premium mở rộng phạm vi hỗ trợ CSS trên cùng pipeline. Trình tự sáu giai đoạn không thay đổi giữa các phiên bản. Xem ma trận hỗ trợ CSS.