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

ตัวแก้ไข CSS: ลำดับการแสดงผลและความจำเพาะ

คลาส CssResolver จับคู่ selector กับสตรีมของโทเค็น จัดลำดับกฎที่จับคู่ได้ตามเลเยอร์การแสดงผล ความจำเพาะ และลำดับในเอกสาร จากนั้นจึงนำ !important มาใช้ในรอบที่สอง

Terminal window
composer require nextpdf/core:^3

CssResolver คือคอมโพเนนต์ของ Layer 1 (ตาม ADR-010) คอมโพเนนต์นี้ถือครองกฎ Cascading Style Sheets (CSS) ที่แจงแล้ว และตัดสินว่าการประกาศใดมีผลกับแต่ละเอลิเมนต์ คลาสนี้แยกออกมาจาก HtmlParser เพื่อให้โครงสร้างชัดเจน และเป็นส่วนภายใน ไม่ใช่ application programming interface (API) สาธารณะ

ตัวแก้ไขไม่จำเป็นต้องใช้ทรีของเอกสาร การจับคู่ selector อ่านสตรีมโทเค็นแบบแบนและใช้แมปดัชนีที่ HtmlChildScanner สร้างขึ้นใน pipeline Stage 3 ได้แก่จำนวนเอลิเมนต์ลูก จำนวนแท็กชนิดเดียวกัน และสถานะว่างเปล่า แมปเหล่านี้รองรับ pseudo-class เชิงโครงสร้าง ส่วน selector เชิงสัมพันธ์ :has() ใช้การพรีสแกนแบบมีขอบเขตตามที่ ข้อจำกัดของการสตรีม อธิบายไว้

การแก้ไขลำดับการแสดงผลทำงานเป็นสองรอบภายใน CssResolver::resolveMatchingProperties() รอบที่ 1 นำการประกาศแบบปกติมาใช้ตามลำดับการแสดงผล โดยเรียงตามน้ำหนักของเลเยอร์การแสดงผลก่อน ตามด้วยความจำเพาะ แล้วจึงเป็นลำดับในเอกสาร รอบที่ 2 นำการประกาศ !important มาใช้ตามลำดับความจำเพาะ การประกาศ !important จะแทนที่การประกาศแบบปกติใด ๆ โดยไม่คำนึงถึงความจำเพาะ การแบ่งเป็นสองรอบนี้คือกลยุทธ์ในการนำไปใช้งาน และให้ชุดคุณสมบัติที่แก้ไขแล้วสำหรับเลเยอร์เค้าโครงนำไปใช้

ลำดับการแสดงผลที่ตัวแก้ไขนำไปใช้สอดคล้องกับข้อกำหนด CSS Cascading and Inheritance ของ World Wide Web Consortium (W3C) การประกาศจะถูกจัดลำดับตามแหล่งที่มาและความสำคัญก่อน จากนั้นจึงตามความจำเพาะของ selector เมื่อความจำเพาะเท่ากัน การประกาศลำดับสุดท้ายในเอกสารจะชนะ (CSS Cascade 5 §6.4 ดูที่ความสอดคล้อง) ความคิดเห็นในซอร์สโค้ดของ CssResolver อ้างถึงข้อกำหนดเดียวกัน จึงใช้เป็นวิธีตรวจสอบพฤติกรรมนี้ได้อีกทางหนึ่ง ควบคู่ไปกับข้อกำหนดและอภิธานศัพท์

ความจำเพาะถูกคำนวณเป็นชุดสามค่า (A, B, C) จากจำนวนส่วนประกอบ ID, class และ type แล้วเปรียบเทียบชุดสามค่านั้นทีละส่วนประกอบ (Selectors Level 4 §16) NextPDF คำนวณความจำเพาะของกฎที่จับคู่ได้แต่ละข้อก่อนจัดลำดับการแสดงผล

มีข้อจำกัดสำคัญหนึ่งข้อ กฎการกลับลำดับเลเยอร์ §6.4.3 ใช้กับการประกาศ !important ที่ข้ามเลเยอร์การแสดงผล และซอร์สบันทึกไว้ว่าเป็นงานที่ยังค้างอยู่สำหรับกลุ่มงานเลเยอร์การแสดงผล เมื่อมีการประกาศเลเยอร์การแสดงผลและมี !important ข้ามเลเยอร์ ลำดับที่แก้ไขได้อาจแตกต่างจากพฤติกรรมตามข้อกำหนดฉบับเต็ม เมทริกซ์การรองรับ CSS เป็นแหล่งอ้างอิงสถานะการรองรับของแต่ละฟีเจอร์ และหน้านี้ไม่ได้กล่าวซ้ำถึงการรองรับของแต่ละคุณสมบัติ

สัญลักษณ์ตำแหน่งบทบาท
CssResolver::parseStyleBlock(string $css, bool $nestingEnabled = false): voidsrc/Html/CssResolver.phpแจงบล็อก <style> เป็นกฎ
CssResolver::resolveMatchingProperties(...)src/Html/CssResolver.phpจับคู่ selector และแก้ไขการเรียงลำดับการแสดงผลแบบสองรอบ
CssResolver::resolveHasSelectors(array $tokens): arraysrc/Html/CssResolver.phpการพรีสแกน :has() แบบมีขอบเขต (เปิดใช้แบบมีเงื่อนไข)
CssResolver::resolveFirstLetterProperties(...)src/Html/CssResolver.phpแก้ไขคุณสมบัติของ ::first-letter แบบซูโดเอลิเมนต์
CssResolver::resolvePseudoElementProperties(...)src/Html/CssResolver.phpแก้ไขคุณสมบัติของ ::before / ::after แบบซูโดเอลิเมนต์
CssResolver::getLayerRegistry(): LayerRegistrysrc/Html/CssResolver.phpเลเยอร์การแสดงผลที่ประกาศไว้

คุณไม่ได้เรียกใช้ตัวแก้ไขโดยตรง แต่เขียน CSS และตัวแก้ไขจะทำงานภายใน writeHtml() ในลำดับการแสดงผลด้านล่าง p ถูกแก้ไขเป็นสีแดง เนื่องจากกฎของ class มีความจำเพาะสูงกว่ากฎของ type

<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();
$doc->addPage();
$doc->writeHtml(
'<style>p { color: blue; } .lead { color: red; }</style>'
. '<p class="lead">Higher-specificity class wins.</p>'
);
$doc->save(__DIR__ . '/output/cascade.pdf');

ตัวอย่างนี้แสดงรอบที่สองของ !important การประกาศ type ที่เป็น !important จะแทนที่การประกาศ class ที่เทียบเท่าแบบอินไลน์ แม้ว่า selector ของ class จะมีความจำเพาะสูงกว่าก็ตาม

<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();
$doc->addPage();
$doc->writeHtml(
'<style>p { color: green !important; } .lead { color: red; }</style>'
. '<p class="lead">!important overrides higher specificity.</p>'
);
$doc->save(__DIR__ . '/output/important.pdf');
  • !important ไม่คำนึงถึงความจำเพาะ รอบที่ 2 นำการประกาศ !important มาใช้ตามลำดับความจำเพาะ และการประกาศเหล่านั้นจะแทนที่การประกาศแบบปกติเสมอ
  • เลเยอร์การแสดงผล + !important ที่ข้ามเลเยอร์ ซอร์สบันทึกกฎการกลับลำดับเลเยอร์ §6.4.3 สำหรับการประกาศ important ไว้ว่ายังค้างอยู่ ตรวจสอบพฤติกรรมกับ เมทริกซ์การรองรับ CSS ก่อนนำไปใช้เป็นเงื่อนไขที่ต้องพึ่งพา
  • การไม่ประกาศเลเยอร์คือเส้นทางที่รวดเร็ว เมื่อไม่มี @layer การจัดลำดับจะลดเหลือเพียงพฤติกรรมตามความจำเพาะเท่านั้น และเหมือนกันทุกบิตกับพฤติกรรมก่อนมีเลเยอร์
  • :has() เปิดใช้แบบมีเงื่อนไข การพรีสแกนเชิงสัมพันธ์จะทำงานเฉพาะเมื่อเปิดใช้ฟีเจอร์ทดลอง css.has เท่านั้น
  • การจับคู่ selector อิงตามสตรีม selector เชิงโครงสร้างใช้แมปดัชนี ไม่ใช่การเดินทรี selector ที่ต้องการนำทางในทรีโดยพลการเกินกว่าแมปดัชนีจะรองรับ จะไม่สามารถแก้ไขได้ในโมเดลนี้

ในกรณีที่แย่ที่สุด การจับคู่ selector คือ O(rules × elements) ซึ่งถูกจำกัดด้วย เพดานการสตรีม การจัดลำดับการแสดงผลทั้งสองครั้งคือ O(matched rules · log matched rules) ต่อหนึ่งเอลิเมนต์ เส้นทางแบบไม่มีเลเยอร์จะข้ามการแก้ไขเลเยอร์ทั้งหมด performance_budget ต่อหน้า (wall_ms: 1500, peak_mb: 64) กำหนดเพดานการทำงาน เบนช์มาร์กของไปป์ไลน์การเรนเดอร์ HTML ใช้ป้องกันการถดถอย (งานที่ผสานแล้ว PR #564)

ตัวแก้ไขจะเห็นเฉพาะ CSS ที่ DefaultHtmlSecurityPolicy::isCssPropertyAllowed() อนุญาตเท่านั้น allowlist กำหนดเพดานด้านความปลอดภัย และตารางการรองรับในรันไทม์กำหนดเพดานความสามารถแยกต่างหาก คุณสมบัติที่ถูกนโยบายบล็อกจะไม่มีทางเข้าสู่การจัดลำดับการแสดงผล ดู โมเดลความปลอดภัยของโมดูล HTML เพิ่มเติม

พฤติกรรมข้อกำหนดข้อตัวระบุอ้างอิง (reference_id)
การเรียงลำดับการแสดงผล: origin/importance → ความจำเพาะ → ลำดับการปรากฏข้อกำหนด W3C CSS Cascading and Inheritance Level 5§6.4 (css_cascade_5#x1.x7.x1.p21)
ความจำเพาะในรูปชุดสามค่า (A,B,C) จากจำนวน ID/class/typeW3C Selectors Level 4§16 (selectors_4#x1.x16.p2)
การแจงแบบกำหนดผลแน่นอนและการกู้คืนจากข้อผิดพลาดในการแจงW3C CSS Syntax Level 3§4 (css_syntax_3#x1.x4.p2)

เนื้อหาของ W3C อยู่ภายใต้สัญญาอนุญาต CC-BY 4.0 ข้อความข้างต้นเป็นการเรียบเรียงใหม่ และระบุตัวระบุข้อกับตัวระบุชังก์ไว้เพื่อการตรวจสอบ NextPDF ไม่ได้อ้างความสอดคล้องอย่างเต็มรูปแบบกับโมดูลเหล่านี้ ดู เมทริกซ์การรองรับ CSS สำหรับสถานะที่ตรวจสอบแล้วของแต่ละโมดูล

ความสามารถระดับองค์กร Premium ขยายชุดคุณสมบัติที่จับคู่และนำไปใช้ให้กว้างขึ้น อัลกอริทึมการเรียงลำดับการแสดงผลและโมเดล !important แบบสองรอบเหมือนกันในทุกรุ่น ดู เมทริกซ์การรองรับ CSS เพิ่มเติม