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

ไปป์ไลน์ HTML

Spec: CSS Cascade 5, §6.1 Spec: CSS Display 3, §2 Evidence: Code-backed

NextPDF เรนเดอร์ HTML และ CSS เป็น PDF ภายในโปรเซส PHP ของคุณ: ไม่มีเบราว์เซอร์ ไม่มีซับโปรเซสตามค่าเริ่มต้น หน้านี้อธิบายสเตจแบบเป็นชั้นที่การแปลงต้องผ่าน สิ่งที่เอนจิน CSS ครอบคลุมจริง และกรณีที่การมอบหมายงานให้ตัวเรนเดอร์ของเบราว์เซอร์จริงเป็นทางเลือกที่ตรงไปตรงมา

“HTML to PDF” ฟังดูเหมือนเป็นการดำเนินการเดียว แต่ในความเป็นจริงประกอบด้วย cascade, box model, รอบการจัดเลย์เอาต์ และรอบการเพนต์ แต่ละส่วนเป็นปัญหาที่มีข้อกำหนดชัดเจนและมีโหมดความล้มเหลวเฉพาะตัว เอนจินที่รวมส่วนเหล่านี้เป็นกระบวนการเดียวจะเปราะบาง การเปลี่ยนแปลง cascade อาจทำให้กล่องเคลื่อนที่ และวิธีเดียวที่จะรู้ได้คือต้องเรนเดอร์แล้วดูผล

โมเดลแบบ in-process มีข้อได้เปรียบจริง: ไม่ต้องติดตั้งเบราว์เซอร์ ไม่ต้องจัดการแซนด์บ็อกซ์ และไม่มีขอบเขตโปรเซสที่ต้องส่งข้อมูลข้ามไป แต่ข้อได้เปรียบนี้จะคุ้มค่าก็ต่อเมื่อการแปลงถูกแยกส่วนอย่างชัดเจนเพียงพอที่จะทดสอบแต่ละประเด็นได้อย่างอิสระ สถาปัตยกรรมคือสิ่งที่ทำให้ “render HTML in PHP” น่าเชื่อถือ ไม่ใช่แค่สิ่งที่ทำได้

  • การแปลง HTML/CSS ทำงานแบบ in-process ผ่าน writeHtml() ผลลัพธ์เป็นเนื้อหา PDF แบบเนทีฟ ไม่ใช่ภาพของหน้ากระดาษ
  • เป็นแบบ single-pass และ streaming ตัวแยกโทเค็นสร้างรายการโทเค็น ตัวแจงใช้รายการนั้นจากซ้ายไปขวา และไม่มีการเก็บ DOM tree ทั้งหมดไว้ (ADR-001) ขีดจำกัดแบบตายตัวควบคุมจำนวนเอลิเมนต์และความลึกของการซ้อน
  • เอนจินถูกจัดเป็น ชั้นที่ชัดเจน: การแจง CSS และ applicator, สถานะสไตล์, เลย์เอาต์และการจัดรูปแบบ, การเพนต์ และ paged media — พร้อมกฎเข้มงวดว่าชั้นใดทำอะไรได้บ้าง (ADR-010)
  • เอนจิน CSS ครอบคลุม cascade, box model และเลย์เอาต์ทั่วไป (block, inline, ตาราง, float และอื่นๆ) — ครอบคลุมมาก แต่เป็นส่วนย่อยที่กำหนดไว้อย่างชัดเจนของสิ่งที่เบราว์เซอร์สมัยใหม่ทำได้
  • เมื่อคุณต้องการความเที่ยงตรงระดับเบราว์เซอร์อย่างแม่นยำสำหรับ CSS สมัยใหม่ที่กำหนดได้อย่างอิสระ NextPDF สามารถ มอบหมายงานให้ตัวเรนเดอร์ของเบราว์เซอร์แบบ headless ผ่านส่วนขยายเสริม — เป็นจุดต่อที่ออกแบบไว้อย่างตั้งใจและแยกออกจากเครือข่าย ไม่ใช่เส้นทางเริ่มต้น

การแปลงเป็นลำดับสเตจ โดยแต่ละสเตจใช้เอาต์พุตแบบมีชนิดจากสเตจก่อนหน้า

  1. Tokenize HTML becomes an ordered token list — no retained DOM tree.
  2. Resolve CSS Parse styles; the cascade and applicators compute typed values.
  3. Style state A push/pop style stack carries computed values per nesting level.
  4. Layout Block, inline, table, and float geometry computed; no paint here.
  5. Paint Borders, backgrounds, text, and decorations emit PDF operators.
  6. Paged media Page-break and @page rules applied as the cursor crosses page bounds.
ไปป์ไลน์ HTML แบบ in-process: การทำงานเพียงรอบเดียวจากซ้ายไปขวาบนสตรีมโทเค็น โดยมีการแก้ไขค่า CSS, สถานะสไตล์, เลย์เอาต์ และการเพนต์เป็นชั้นที่แยกจากกัน และมีการแบ่ง paged-media ที่นำมาใช้ขณะเคอร์เซอร์เคลื่อนที่ไป

กฎทางสถาปัตยกรรมสองข้อทำให้สิ่งนี้เป็นมากกว่าลำดับงาน

ชั้นต่างๆ มีสัญญาของตน ข้อความ CSS จะถูกอ่านเฉพาะภายในคลาส applicator โค้ดเลย์เอาต์คำนวณเรขาคณิต แต่ไม่ปล่อยตัวดำเนินการเพนต์ โค้ดเพนต์อ่านสแนปช็อตของ computed-style ที่เปลี่ยนแปลงไม่ได้ ไม่ใช่สถานะติดตามเลย์เอาต์แบบเปลี่ยนแปลงได้ โค้ด paged-media สั่งให้เกิดการแบ่ง แต่มอบหมายการตกแต่งหน้าให้ชั้นการเพนต์ ขอบเขตเหล่านี้ถูกบังคับใช้ (ADR-010) ด้วยเหตุนี้พร็อพเพอร์ตี CSS ใหม่จึงเพิ่มเป็น applicator ใหม่ แทนที่จะเป็นการเปลี่ยนแปลงครั้งเดียวที่ส่งผลต่อทั้งตัวแจง ตัวส่งงานเลย์เอาต์ และตัวเพนต์พร้อมกัน

ไม่มี DOM ไปป์ไลน์เป็นแบบ single-pass และ streaming ตามการตัดสินใจด้านสถาปัตยกรรม (ADR-001): มีสถานะสไตล์ไม่เกินหนึ่งสถานะต่อระดับการซ้อน พร้อมเคอร์เซอร์ที่กำลังทำงานอยู่ ไม่ใช่หนึ่งอ็อบเจกต์ต่อหนึ่งเอลิเมนต์ การดำเนินการบางอย่างจำเป็นต้องมองไปข้างหน้าจริงๆ — การกำหนดขนาดคอลัมน์ของตาราง, :has(), :last-child กรณีเหล่านี้จัดการด้วยโครงสร้างดัชนีพรีสแกนที่มีขอบเขตจำกัดบนรายการโทเค็นแบบแบน โดยไม่ต้องเก็บ tree ไว้ จำนวนเอลิเมนต์และความลึกของการซ้อนถูกจำกัดแบบตายตัว ดังนั้นอินพุตที่ผิดปกติจึงล้มเหลวอย่างรวดเร็วแทนที่จะใช้หน่วยความจำจนหมด

เอนจิน CSS แก้ค่าตามความหมายของ CSS จริง ไม่ใช่สิ่งที่เพียงดูคล้ายกัน การประกาศที่ขัดแย้งกันจะถูกลดให้เหลือค่าเดียวต่อหนึ่งพร็อพเพอร์ตีตาม origin, importance, layer, specificity และ order — ซึ่งเป็น cascade ที่แท้จริง เลย์เอาต์เป็นไปตาม box model โดยชนิดของกล่องและ formatting context ที่กล่องสร้างขึ้นจะกำหนดวิธีวางตำแหน่งของกล่องนั้นและกล่องพี่น้องในโฟลว์ ซอร์สโค้ดของเอนจินถูกจัดระเบียบตามประเด็นเหล่านี้โดยตรง (cascade, box/display, flex, float, ตาราง, fragmentation) ด้วยเหตุนี้คุณจึงสามารถวิเคราะห์พฤติกรรมของเอนจินเทียบกับข้อกำหนดได้ แทนที่จะต้องค้นพบจากการทดลอง

หน้านี้เป็น Evidence: Code-backed สเตจและกฎต่างๆ เชื่อมโยงกับ repository หลัก:

  • จุดเข้าใช้งานแบบ in-process คือ writeHtml(string $html): static ใน src/Core/Concerns/HasTextOutput.php
  • การออกแบบ single-pass ที่ไม่เก็บ DOM พร้อมขีดจำกัดตายตัวสำหรับเอลิเมนต์และการซ้อนคือ ADR-001 และโค้ด tokenizer/parser/style-stack ใน src/Html/
  • สัญญาของเอนจินแบบเป็นชั้น — CSS parsing/applicators, สถานะสไตล์, เลย์เอาต์, การเพนต์, paged media — คือ ADR-010 ซึ่งสะท้อนอยู่ในโครงสร้าง src/Html/ (ตัวอย่างเช่น Cascade/, Css/, Flex/, Float/, Fragmentation/ และคลาส applicator)
  • จุดต่อสำหรับการมอบหมายงานให้เบราว์เซอร์คือ writeHtmlChrome() ในไฟล์เดียวกัน ซึ่งมีบันทึกไว้ว่าต้องใช้ส่วนขยายตัวเรนเดอร์เสริมพร้อมไบนารี Chrome/Chromium

มาตรฐานต่างๆ ยึดโยงคำกล่าวอ้างเรื่องความครอบคลุมอย่างตรงไปตรงมา cascade ลดการประกาศที่ขัดแย้งกันให้เหลือค่าเดียวต่อหนึ่งพร็อพเพอร์ตี — origin, importance, layer, specificity, order — ตาม Spec: CSS Cascade 5, §6.1 และการวางตำแหน่งในโฟลว์ เป็นไปตามกฎของ box และ formatting-context ตาม Spec: CSS Display 3, §2 สิ่งที่สำคัญไม่แพ้กันคือ ขอบเขต: feature query มีอยู่ก็เพราะไม่ใช่ทุกตัวประมวลผล รองรับทุกฟีเจอร์ตาม Spec: CSS Conditional 5, §2 เอนจิน CSS ของ NextPDF เป็น ส่วนย่อยที่กำหนดไว้อย่างชัดเจนและสอดคล้องกับข้อกำหนด และการระบุเรื่องนี้อย่างตรงไปตรงมาเป็นส่วนหนึ่งของ สัญญา

การเรนเดอร์แบบ in-process เป็นการเรียกเพียงครั้งเดียว เอาต์พุตคือข้อความ PDF ที่เลือกได้ ไม่ใช่หน้าที่ถูกแปลงเป็นแรสเตอร์:

<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();
$doc->setTitle('HTML Basic');
$doc->addPage();
$html = <<<'HTML'
<h1 style="color: #1E3A8A;">HTML Rendering in NextPDF</h1>
<p>NextPDF renders <strong>HTML and CSS</strong> directly into PDF pages,
<em>in-process</em>.</p>
<ul>
<li>Headings, paragraphs, bold and italic</li>
<li>Lists, tables, inline styles</li>
</ul>
HTML;
$doc->writeHtml($html);
$doc->save(__DIR__ . '/html-basic.pdf');

หากเอกสารเดียวกันต้องการ CSS สมัยใหม่ที่กำหนดได้อย่างอิสระพร้อมความเที่ยงตรงระดับเบราว์เซอร์อย่างแม่นยำ การเรียกจะเปลี่ยนเป็น writeHtmlChrome($html) แทน — เอกสารเดียวกัน แต่เส้นทางการเรนเดอร์ต่างกัน และมีการพึ่งพาตัวเรนเดอร์ของเบราว์เซอร์เสริมโดยตั้งใจ

ความเข้าใจผิดที่เกิดซ้ำๆ คือการมองว่าเอนจิน HTML-to-PDF นั้น “โดยพื้นฐานก็คือเบราว์เซอร์” ซึ่งไม่เป็นเช่นนั้น และ NextPDF ก็ไม่ได้กล่าวอ้างเช่นนั้น เบราว์เซอร์เป็นการนำแพลตฟอร์มเว็บทั้งหมดมาใช้งานอย่างกว้างขวางและได้รับการอัปเดตอย่างต่อเนื่อง เอนจินแบบ in-process ของ NextPDF เป็น ส่วนย่อย ที่สอดคล้องกับข้อกำหนดและมุ่งเน้นที่เลย์เอาต์ของเอกสาร แบบจำลองทางความคิดที่ตรงไปตรงมาคือ “เอนจิน CSS สำหรับเอกสารงานพิมพ์ที่มีความสามารถ” ไม่ใช่ “Chrome ใน PHP” เมื่อจำเป็นต้องใช้แพลตฟอร์มเต็มรูปแบบจริงๆ นั่นคือสิ่งที่ writeHtmlChrome() มีไว้สำหรับ เป็นเส้นทางแยกต่างหากที่ต้องเลือกใช้เองและมีภาระในการดำเนินงานของตนเอง ไม่ใช่ทางสำรองแบบเงียบๆ

ความเข้าใจผิดข้อที่สอง: การสันนิษฐานว่าเส้นทางเบราว์เซอร์เป็นเพียง “การเรนเดอร์หน้าผ่านเครือข่าย” ตามการออกแบบแล้ว เป็นตรงกันข้าม จุดต่อสำหรับการมอบหมายงานเรนเดอร์จะปิดกั้นการเข้าถึงเครือข่ายของ subresource เสมอ — ไม่มีรูปภาพ ฟอนต์ stylesheet หรือ frame จากระยะไกล — ดังนั้นจึงไม่สามารถกลายเป็นช่องทางสำหรับการร้องขอออกสู่ภายนอกได้ ได้ความเที่ยงตรงระดับพิกเซล ใช่; ช่องทางออกสู่เครือข่ายแบบเปิด ไม่

หน้านี้อธิบายรูปแบบของไปป์ไลน์และทางเลือกระหว่างแบบ in-process กับเบราว์เซอร์ หน้านี้ ไม่ใช่ เมทริกซ์การรองรับ CSS ที่ละเอียดถึงระดับพร็อพเพอร์ตี โมดูล และ selector สิ่งที่เอนจินแบบ in-process ครอบคลุมถูกกำหนดโดยโค้ดและการทดสอบความสอดคล้องของโค้ด ไม่ใช่โดยภาพรวมนี้ ความครอบคลุมนั้นยังพัฒนาเปลี่ยนแปลงอยู่ เส้นทางการมอบหมายงานให้เบราว์เซอร์ต้องใช้ส่วนขยายเสริมและไบนารี Chrome/Chromium การตั้งค่า คุณลักษณะด้านการดำเนินงาน และโครงสร้างภายในของส่วนขยายนั้นอยู่นอกขอบเขตของหน้านี้ และมีบันทึกไว้พร้อมกับแพ็กเกจดังกล่าว “In-process” อธิบายเส้นทาง writeHtml() ที่เป็นค่าเริ่มต้น ไม่ใช่การกล่าวอ้างว่าทุกเส้นทางการเรนเดอร์หลีกเลี่ยงซับโปรเซส คำกล่าวอ้างทางสถาปัตยกรรมมีความถูกต้อง ณ วันที่ตรวจทานหน้านี้ แหล่งข้อมูลที่เชื่อถือได้คือ src/Html/, ADR-001 และ ADR-010 ใน repository หลัก

เอนจิน CSS แบบ in-process เป็นความสามารถของ Core จุดต่อสำหรับการมอบหมายงานให้เบราว์เซอร์เป็นส่วนขยายเสริม ซึ่งกล่าวถึงที่นี่เฉพาะในระดับความสามารถเท่านั้น:

เส้นทางการเรนเดอร์ HTML — edition availability
Edition Availability
Core Core ให้เอนจิน HTML/CSS แบบ in-process (writeHtml)
Pro เส้นทางการมอบหมายงานให้เบราว์เซอร์เป็นส่วนขยายแบบ add-on ที่ไม่ขึ้นกับระดับ edition
Enterprise เส้นทางการมอบหมายงานให้เบราว์เซอร์เป็นส่วนขยายแบบ add-on ที่ไม่ขึ้นกับระดับ edition
  • การเรนเดอร์แบบ in-process — การแปลง HTML/CSS เป็น PDF ภายในโปรเซส PHP โดยไม่มีเบราว์เซอร์หรือซับโปรเซสตามค่าเริ่มต้น (writeHtml())
  • Single-pass / streaming — การใช้สตรีมโทเค็นจากซ้ายไปขวาโดยไม่เก็บ DOM tree ทั้งหมดไว้ (ADR-001)
  • Cascade — กระบวนการของ CSS ที่ลดการประกาศที่ขัดแย้งกันให้เหลือค่าเดียวต่อหนึ่งพร็อพเพอร์ตีตาม origin, importance, layer, specificity และ order
  • Formatting context — สภาพแวดล้อมของเลย์เอาต์ที่กล่องสร้างขึ้นและควบคุมวิธีวางตำแหน่งของเนื้อหาในโฟลว์ของกล่องนั้น
  • สัญญาของชั้นเอนจิน — ชุดกฎที่บังคับใช้ (ADR-010) ซึ่งกำหนดว่าชั้นการแจง สไตล์ เลย์เอาต์ การเพนต์ และ paged-media แต่ละชั้นทำอะไรได้บ้าง
  • จุดต่อสำหรับการมอบหมายงานให้เบราว์เซอร์ — เส้นทาง writeHtmlChrome() แบบเสริมที่เรนเดอร์ผ่านเบราว์เซอร์แบบ headless โดยปิดกั้นการเข้าถึงเครือข่ายของ subresource