ไปป์ไลน์ HTML
Spec: CSS Cascade 5, §6.1 CSS Cascade 5 §6.1 Spec: CSS Display 3, §2 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 ผ่านส่วนขยายเสริม — เป็นจุดต่อที่ออกแบบไว้อย่างตั้งใจและแยกออกจากเครือข่าย ไม่ใช่เส้นทางเริ่มต้น
วิธีที่ NextPDF จัดการกับเรื่องนี้
หัวข้อที่มีชื่อว่า “วิธีที่ NextPDF จัดการกับเรื่องนี้”การแปลงเป็นลำดับสเตจ โดยแต่ละสเตจใช้เอาต์พุตแบบมีชนิดจากสเตจก่อนหน้า
- Tokenize HTML becomes an ordered token list — no retained DOM tree.
- Resolve CSS Parse styles; the cascade and applicators compute typed values.
- Style state A push/pop style stack carries computed values per nesting level.
- Layout Block, inline, table, and float geometry computed; no paint here.
- Paint Borders, backgrounds, text, and decorations emit PDF operators.
- Paged media Page-break and @page rules applied as the cursor crosses page bounds.
กฎทางสถาปัตยกรรมสองข้อทำให้สิ่งนี้เป็นมากกว่าลำดับงาน
ชั้นต่างๆ มีสัญญาของตน ข้อความ 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 CSS Cascade 5 §6.1 และการวางตำแหน่งในโฟลว์ เป็นไปตามกฎของ box และ formatting-context ตาม Spec: CSS Display 3, §2 CSS Display 3 §2 สิ่งที่สำคัญไม่แพ้กันคือ ขอบเขต: feature query มีอยู่ก็เพราะไม่ใช่ทุกตัวประมวลผล รองรับทุกฟีเจอร์ตาม Spec: CSS Conditional 5, §2 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 จุดต่อสำหรับการมอบหมายงานให้เบราว์เซอร์เป็นส่วนขยายเสริม ซึ่งกล่าวถึงที่นี่เฉพาะในระดับความสามารถเท่านั้น:
| Edition | Availability |
|---|---|
| Core | Core ให้เอนจิน HTML/CSS แบบ in-process (writeHtml) |
| Pro | เส้นทางการมอบหมายงานให้เบราว์เซอร์เป็นส่วนขยายแบบ add-on ที่ไม่ขึ้นกับระดับ edition |
| Enterprise | เส้นทางการมอบหมายงานให้เบราว์เซอร์เป็นส่วนขยายแบบ add-on ที่ไม่ขึ้นกับระดับ edition |
เอกสารที่เกี่ยวข้อง
หัวข้อที่มีชื่อว่า “เอกสารที่เกี่ยวข้อง”- โมเดลของไปป์ไลน์ — บทบาทของเส้นทางเนื้อหา HTML ในโฟลว์ของเอกสารโดยรวม
- เมื่อใดไม่ควรใช้ NextPDF — ขอบเขตที่ตรงไปตรงมา รวมถึงกรณีที่เส้นทางเบราว์เซอร์หรือเครื่องมืออื่นเหมาะสม
- คู่มือการตัดสินใจเรื่องการผสานรวม — การเลือกระหว่างเอนจินแบบ in-process กับตัวเรนเดอร์สำหรับกรณีใช้งานของคุณ
อภิธานศัพท์
หัวข้อที่มีชื่อว่า “อภิธานศัพท์”- การเรนเดอร์แบบ 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