Feature Parity Matrix
Auto-generated from tools/parity/catalog.toml plus inline spec: markers in tests. Run uv run python tools/parity/generate.py to regenerate.
Summary: 129/137 behaviors implemented · 100/137 tested · WeasyPrint comparison hand-curated in catalog.
Legend
| Column |
Meaning |
Spec § |
Sub-section within the spec, if applicable |
WeasyPrint |
✅ full · 🟡 partial · ❌ none |
pdfun |
✅ implemented · ❌ not implemented |
Tested |
✅ (N) tests referencing this behavior · ⚠️ implemented but untested · — not applicable |
HTML — Block-level elements
| Behavior |
Spec § |
WeasyPrint |
pdfun |
Tested |
| Headings (h1–h6) with scaled default sizes |
— |
✅ |
✅ |
⚠️ untested |
| Paragraph (p) default margins |
— |
✅ |
✅ |
✅ (1) tests/test_html.py::paragraph_renders |
| Generic block container (div) |
— |
✅ |
✅ |
✅ (1) tests/test_html.py::div_renders |
| Block quote (blockquote) with indent |
— |
✅ |
✅ |
✅ (1) tests/test_html.py::blockquote_renders |
| Preformatted text (pre) whitespace preservation |
— |
✅ |
✅ |
✅ (1) tests/test_html.py::pre_preserves_spaces |
| Horizontal rule (hr) |
— |
✅ |
✅ |
✅ (1) tests/test_html.py::hr_renders |
| Semantic elements (article/section/nav/header/footer/aside/main) |
— |
✅ |
✅ |
✅ (1) tests/test_html.py::article_renders |
| Figure / figcaption |
— |
✅ |
✅ |
✅ (1) tests/test_html.py::figure_with_figcaption_renders_both |
| Details / summary (expanded rendering) |
— |
🟡 |
✅ |
✅ (1) tests/test_html.py::summary_renders_as_bold_block |
HTML — Inline elements
| Behavior |
Spec § |
WeasyPrint |
pdfun |
Tested |
| Bold (b, strong) |
— |
✅ |
✅ |
⚠️ untested |
| Italic (i, em) |
— |
✅ |
✅ |
⚠️ untested |
| Inline span |
— |
✅ |
✅ |
✅ (1) tests/test_html.py::span_extracts_text |
| Line break (br) |
— |
✅ |
✅ |
✅ (1) tests/test_html.py::br_splits_text |
| Inline code (code, kbd, samp) |
— |
✅ |
✅ |
⚠️ untested |
| Superscript / subscript (sup, sub) |
— |
✅ |
✅ |
⚠️ untested |
| Links (a) with external PDF annotations |
— |
✅ |
✅ |
✅ (1) tests/test_html.py::anchor_tag_preserves_text |
HTML — Lists
| Behavior |
Spec § |
WeasyPrint |
pdfun |
Tested |
| Unordered list (ul, li) |
— |
✅ |
✅ |
✅ (3) tests/test_html.py::ul_renders_item_text, tests/test_html.py::ul_translucent_background_paints_behind_items, tests/test_html.py::ul_padding_left_shifts_bullet_rightward |
| Ordered list (ol, li) |
— |
✅ |
✅ |
✅ (1) tests/test_html.py::ol_has_numbered_markers |
| Nested lists |
— |
✅ |
✅ |
✅ (1) tests/test_html.py::nested_ul |
| Definition list (dl, dt, dd) |
— |
✅ |
✅ |
✅ (1) tests/test_html.py::dl_renders_term_and_definition |
HTML — Embedded content
| Behavior |
Spec § |
WeasyPrint |
pdfun |
Tested |
| PNG image embedding (img) |
— |
✅ |
✅ |
✅ (1) tests/test_html.py::png_img_produces_xobject |
| JPEG image embedding (img) |
— |
✅ |
✅ |
✅ (1) tests/test_html.py::jpeg_img_produces_dctdecode_xobject |
| SVG rendering |
— |
✅ |
❌ |
— |
HTML — Tables
| Behavior |
Spec § |
WeasyPrint |
pdfun |
Tested |
| Table with tr/td/th |
— |
✅ |
✅ |
⚠️ untested |
| Table row groups (thead, tbody, tfoot) |
— |
✅ |
✅ |
⚠️ untested |
| Table caption |
— |
✅ |
✅ |
⚠️ untested |
| Behavior |
Spec § |
WeasyPrint |
pdfun |
Tested |
| input elements |
— |
🟡 |
✅ |
✅ (1) tests/test_html.py::text_input_renders_value_in_bordered_box |
| select elements |
— |
🟡 |
✅ |
✅ (1) tests/test_html.py::select_renders_first_option_in_bordered_box |
| textarea elements |
— |
🟡 |
✅ |
✅ (1) tests/test_html.py::textarea_renders_inner_text_in_bordered_box |
| button elements |
— |
🟡 |
✅ |
✅ (1) tests/test_html.py::button_renders_inner_text_with_default_box |
CSS 2.1 §5 — Selectors
| Behavior |
Spec § |
WeasyPrint |
pdfun |
Tested |
| Type selectors (p, h1) |
5.3 |
✅ |
✅ |
✅ (1) tests/test_html.py::style_type_selector_color |
| Class selectors (.foo) |
5.8.3 |
✅ |
✅ |
✅ (1) tests/test_html.py::style_class_selector |
| ID selectors (#foo) |
5.9 |
✅ |
✅ |
✅ (1) tests/test_html.py::style_id_selector |
| Universal selector (*) |
5.3 |
✅ |
✅ |
✅ (1) tests/test_html.py::universal_selector_matches_all_elements |
| Compound selectors (p.note#main) |
5.2 |
✅ |
✅ |
✅ (1) tests/test_html.py::style_compound_selector |
| Descendant combinator (div p) |
5.5 |
✅ |
✅ |
✅ (1) tests/test_html.py::style_descendant_selector |
| Child combinator (div > p) |
5.6 |
✅ |
✅ |
✅ (1) tests/test_html.py::style_child_selector |
| Adjacent sibling combinator (h1 + p) |
5.7 |
✅ |
✅ |
✅ (1) tests/test_html.py::adjacent_sibling_matches_immediate_p |
| General sibling combinator (h1 ~ p) |
5.7 |
✅ |
✅ |
✅ (1) tests/test_html.py::general_sibling_matches_all_following_p |
| Selector lists (h1, h2, h3) |
5.2.1 |
✅ |
✅ |
✅ (1) tests/test_html.py::style_multiple_selectors |
| Attribute selectors ([type="text"]) |
5.8 |
✅ |
✅ |
✅ (1) tests/test_html.py::attr_prefix_match |
| :first-child pseudo-class |
5.11.3 |
✅ |
✅ |
✅ (1) tests/test_html.py::first_child_matches_first_p |
| :nth-child() pseudo-class |
5.11.3 |
✅ |
✅ |
✅ (1) tests/test_html.py::nth_child_even_hits_even_positions |
| :not() pseudo-class |
5.11.4 |
✅ |
✅ |
✅ (1) tests/test_html.py::not_pseudo_excludes_class |
| ::before pseudo-element |
5.12.3 |
✅ |
✅ |
✅ (1) tests/test_html.py::before_prepends_generated_content |
| ::after pseudo-element |
5.12.3 |
✅ |
✅ |
✅ (1) tests/test_html.py::after_appends_generated_content |
| ::first-line pseudo-element |
5.12.1 |
❌ |
❌ |
— |
CSS 2.1 §6 — Cascade and inheritance
| Behavior |
Spec § |
WeasyPrint |
pdfun |
Tested |
| Specificity ordering |
6.4.3 |
✅ |
✅ |
✅ (1) tests/test_html.py::style_id_beats_class |
| Cascade: UA defaults < |