fullbleed 2.1.0__tar.gz → 2.2.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (188) hide show
  1. fullbleed-2.2.2/Cargo.lock +37 -0
  2. {fullbleed-2.1.0 → fullbleed-2.2.2}/Cargo.toml +4 -2
  3. {fullbleed-2.1.0 → fullbleed-2.2.2}/PKG-INFO +48 -12
  4. {fullbleed-2.1.0 → fullbleed-2.2.2}/README.md +47 -11
  5. {fullbleed-2.1.0 → fullbleed-2.2.2}/crates/fullbleed_audit_contract/Cargo.lock +1 -1
  6. {fullbleed-2.1.0 → fullbleed-2.2.2}/crates/fullbleed_audit_contract/Cargo.toml +1 -1
  7. {fullbleed-2.1.0 → fullbleed-2.2.2}/crates/fullbleed_audit_contract/src/lib.rs +52 -15
  8. {fullbleed-2.1.0 → fullbleed-2.2.2}/pyproject.toml +1 -1
  9. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed/_abi.py +19 -0
  10. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed/ui/core.py +21 -0
  11. {fullbleed-2.1.0 → fullbleed-2.2.2}/src/assets.rs +7 -2
  12. fullbleed-2.2.2/src/canvas.rs +1852 -0
  13. fullbleed-2.2.2/src/doc_context.rs +89 -0
  14. {fullbleed-2.1.0 → fullbleed-2.2.2}/src/doc_template.rs +395 -81
  15. fullbleed-2.2.2/src/flowable.rs +44293 -0
  16. {fullbleed-2.1.0 → fullbleed-2.2.2}/src/font.rs +731 -76
  17. {fullbleed-2.1.0 → fullbleed-2.2.2}/src/font_subset.rs +1 -1
  18. {fullbleed-2.1.0 → fullbleed-2.2.2}/src/frame.rs +89 -14
  19. {fullbleed-2.1.0 → fullbleed-2.2.2}/src/html.rs +6033 -622
  20. {fullbleed-2.1.0 → fullbleed-2.2.2}/src/image_native.rs +388 -1
  21. {fullbleed-2.1.0 → fullbleed-2.2.2}/src/jit.rs +56 -0
  22. {fullbleed-2.1.0 → fullbleed-2.2.2}/src/lib.rs +4503 -213
  23. {fullbleed-2.1.0 → fullbleed-2.2.2}/src/native_shape.rs +52 -7
  24. {fullbleed-2.1.0 → fullbleed-2.2.2}/src/page_template.rs +58 -1
  25. {fullbleed-2.1.0 → fullbleed-2.2.2}/src/pdf.rs +2833 -144
  26. {fullbleed-2.1.0 → fullbleed-2.2.2}/src/pdf_raster.rs +1 -0
  27. {fullbleed-2.1.0 → fullbleed-2.2.2}/src/python/abi_exports.rs +33 -0
  28. {fullbleed-2.1.0 → fullbleed-2.2.2}/src/python.rs +2 -1
  29. {fullbleed-2.1.0 → fullbleed-2.2.2}/src/raster.rs +2025 -137
  30. {fullbleed-2.1.0 → fullbleed-2.2.2}/src/raster_native.rs +148 -12
  31. {fullbleed-2.1.0 → fullbleed-2.2.2}/src/sfnt.rs +43 -0
  32. {fullbleed-2.1.0 → fullbleed-2.2.2}/src/spill.rs +703 -21
  33. {fullbleed-2.1.0 → fullbleed-2.2.2}/src/style.rs +9888 -653
  34. {fullbleed-2.1.0 → fullbleed-2.2.2}/src/svg.rs +159 -35
  35. {fullbleed-2.1.0 → fullbleed-2.2.2}/src/text_shape.rs +121 -11
  36. {fullbleed-2.1.0 → fullbleed-2.2.2}/src/types.rs +101 -0
  37. fullbleed-2.1.0/Cargo.lock +0 -14
  38. fullbleed-2.1.0/src/canvas.rs +0 -892
  39. fullbleed-2.1.0/src/doc_context.rs +0 -14
  40. fullbleed-2.1.0/src/flowable.rs +0 -23941
  41. {fullbleed-2.1.0 → fullbleed-2.2.2}/COPYRIGHT +0 -0
  42. {fullbleed-2.1.0 → fullbleed-2.2.2}/FONT_LICENSE_AUDIT.json +0 -0
  43. {fullbleed-2.1.0 → fullbleed-2.2.2}/FONT_LICENSE_AUDIT.md +0 -0
  44. {fullbleed-2.1.0 → fullbleed-2.2.2}/LICENSE +0 -0
  45. {fullbleed-2.1.0 → fullbleed-2.2.2}/LICENSING.md +0 -0
  46. {fullbleed-2.1.0 → fullbleed-2.2.2}/THIRD_PARTY_LICENSES.md +0 -0
  47. {fullbleed-2.1.0 → fullbleed-2.2.2}/build.rs +0 -0
  48. {fullbleed-2.1.0 → fullbleed-2.2.2}/build_backend/fullbleed_build_backend.py +0 -0
  49. {fullbleed-2.1.0 → fullbleed-2.2.2}/crates/fullbleed_audit_contract/LICENSE +0 -0
  50. {fullbleed-2.1.0 → fullbleed-2.2.2}/crates/fullbleed_audit_contract/README.md +0 -0
  51. {fullbleed-2.1.0 → fullbleed-2.2.2}/crates/fullbleed_audit_contract/specs/fullbleed.audit_registry.v1.yaml +0 -0
  52. {fullbleed-2.1.0 → fullbleed-2.2.2}/crates/fullbleed_audit_contract/specs/section508_html_registry.v1.yaml +0 -0
  53. {fullbleed-2.1.0 → fullbleed-2.2.2}/crates/fullbleed_audit_contract/specs/wcag20aa_registry.v1.yaml +0 -0
  54. {fullbleed-2.1.0 → fullbleed-2.2.2}/crates/fullbleed_audit_contract/src/json.rs +0 -0
  55. {fullbleed-2.1.0 → fullbleed-2.2.2}/crates/fullbleed_audit_contract/src/sha256.rs +0 -0
  56. {fullbleed-2.1.0 → fullbleed-2.2.2}/docs/specs/fullbleed.a11y.verify.v1.schema.json +0 -0
  57. {fullbleed-2.1.0 → fullbleed-2.2.2}/docs/specs/fullbleed.audit_registry.v1.yaml +0 -0
  58. {fullbleed-2.1.0 → fullbleed-2.2.2}/docs/specs/fullbleed.pmr.v1.schema.json +0 -0
  59. {fullbleed-2.1.0 → fullbleed-2.2.2}/docs/specs/section508_html_registry.v1.yaml +0 -0
  60. {fullbleed-2.1.0 → fullbleed-2.2.2}/docs/specs/wcag20aa_registry.v1.yaml +0 -0
  61. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed/__init__.py +0 -0
  62. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed/__main__.py +0 -0
  63. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed/accessibility/__init__.py +0 -0
  64. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed/accessibility/engine.py +0 -0
  65. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed/accessibility/runtime.py +0 -0
  66. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed/accessibility/types.py +0 -0
  67. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed/audit_prototype.py +0 -0
  68. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed/audit_section508.py +0 -0
  69. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed/audit_wcag.py +0 -0
  70. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed/ui/__init__.py +0 -0
  71. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed/ui/accessibility.py +0 -0
  72. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed/ui/cav/__init__.py +0 -0
  73. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed/ui/cav/_core.py +0 -0
  74. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed/ui/cav/agency_letter.py +0 -0
  75. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed/ui/cav/court_motion_form.py +0 -0
  76. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed/ui/cav/declaration_form.py +0 -0
  77. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed/ui/cav/instruction_sheet.py +0 -0
  78. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed/ui/cav/investment_portfolio_report.py +0 -0
  79. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed/ui/cav/marriage_record.py +0 -0
  80. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed/ui/cav/profiles.py +0 -0
  81. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed/ui/cav/recorded_plat.py +0 -0
  82. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed/ui/cav/redaction_request_form.py +0 -0
  83. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed/ui/cav/warranty_deed.py +0 -0
  84. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed/ui/primitives.py +0 -0
  85. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed/ui/style.py +0 -0
  86. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_assets/__init__.py +0 -0
  87. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_assets/bootstrap.min.css +0 -0
  88. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_assets/fonts/Inter-Variable.ttf +0 -0
  89. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_assets/fonts/NotoSans-Regular.ttf +0 -0
  90. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_assets/fonts/NotoSansMath-Regular.ttf +0 -0
  91. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_assets/fonts/NotoSansSymbols-Regular.ttf +0 -0
  92. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_assets/fonts/NotoSansSymbols2-Regular.ttf +0 -0
  93. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_assets/icons/bootstrap-icons.svg +0 -0
  94. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_cli/__init__.py +0 -0
  95. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_cli/__main__.py +0 -0
  96. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_cli/assets.py +0 -0
  97. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_cli/cache.py +0 -0
  98. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_cli/cli.py +0 -0
  99. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_cli/finalize.py +0 -0
  100. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_cli/scaffold.py +0 -0
  101. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_cli/scaffold_templates/__init__.py +0 -0
  102. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_cli/scaffold_templates/init/.gitignore +0 -0
  103. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_cli/scaffold_templates/init/COMPLIANCE.md +0 -0
  104. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_cli/scaffold_templates/init/SCAFFOLDING.md +0 -0
  105. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_cli/scaffold_templates/init/components/__init__.py +0 -0
  106. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_cli/scaffold_templates/init/components/body.py +0 -0
  107. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_cli/scaffold_templates/init/components/fb_ui.py +0 -0
  108. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_cli/scaffold_templates/init/components/footer.py +0 -0
  109. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_cli/scaffold_templates/init/components/header.py +0 -0
  110. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_cli/scaffold_templates/init/components/primitives.py +0 -0
  111. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_cli/scaffold_templates/init/components/styles/body.css +0 -0
  112. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_cli/scaffold_templates/init/components/styles/footer.css +0 -0
  113. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_cli/scaffold_templates/init/components/styles/header.css +0 -0
  114. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_cli/scaffold_templates/init/components/styles/primitives.css +0 -0
  115. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_cli/scaffold_templates/init/report.py +0 -0
  116. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_cli/scaffold_templates/init/styles/report.css +0 -0
  117. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_cli/scaffold_templates/init/styles/tokens.css +0 -0
  118. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_cli/scaffold_templates/new/accessible/README.md +0 -0
  119. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_cli/scaffold_templates/new/accessible/output/.gitignore +0 -0
  120. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_cli/scaffold_templates/new/accessible/output/.gitkeep +0 -0
  121. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_cli/scaffold_templates/new/accessible/report.py +0 -0
  122. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_cli/scaffold_templates/new/accessible/styles/report.css +0 -0
  123. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_cli/scaffold_templates/new/accessible/vendor/fonts/Inter-Variable.ttf +0 -0
  124. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_cli/scaffold_templates/new/invoice/output/.gitignore +0 -0
  125. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_cli/scaffold_templates/new/invoice/output/.gitkeep +0 -0
  126. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_cli/scaffold_templates/new/invoice/report.py +0 -0
  127. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_cli/scaffold_templates/new/invoice/templates/invoice.css +0 -0
  128. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_cli/scaffold_templates/new/invoice/templates/invoice.html +0 -0
  129. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_cli/scaffold_templates/new/reference/COMPLIANCE.md +0 -0
  130. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_cli/scaffold_templates/new/reference/README.md +0 -0
  131. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_cli/scaffold_templates/new/reference/SCAFFOLDING.md +0 -0
  132. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_cli/scaffold_templates/new/reference/assets/brand-mark.svg +0 -0
  133. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_cli/scaffold_templates/new/reference/assets/reference-pattern.svg +0 -0
  134. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_cli/scaffold_templates/new/reference/components/__init__.py +0 -0
  135. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_cli/scaffold_templates/new/reference/components/appendix.py +0 -0
  136. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_cli/scaffold_templates/new/reference/components/architecture.py +0 -0
  137. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_cli/scaffold_templates/new/reference/components/cover.py +0 -0
  138. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_cli/scaffold_templates/new/reference/components/data_pages.py +0 -0
  139. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_cli/scaffold_templates/new/reference/components/fb_ui.py +0 -0
  140. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_cli/scaffold_templates/new/reference/components/primitives.py +0 -0
  141. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_cli/scaffold_templates/new/reference/components/styles/appendix.css +0 -0
  142. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_cli/scaffold_templates/new/reference/components/styles/architecture.css +0 -0
  143. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_cli/scaffold_templates/new/reference/components/styles/cover.css +0 -0
  144. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_cli/scaffold_templates/new/reference/components/styles/data.css +0 -0
  145. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_cli/scaffold_templates/new/reference/components/styles/primitives.css +0 -0
  146. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_cli/scaffold_templates/new/reference/components/styles/visuals.css +0 -0
  147. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_cli/scaffold_templates/new/reference/components/visuals.py +0 -0
  148. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_cli/scaffold_templates/new/reference/data/reference.json +0 -0
  149. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_cli/scaffold_templates/new/reference/output/.gitignore +0 -0
  150. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_cli/scaffold_templates/new/reference/report.py +0 -0
  151. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_cli/scaffold_templates/new/reference/styles/report.css +0 -0
  152. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_cli/scaffold_templates/new/reference/styles/tokens.css +0 -0
  153. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_cli/scaffold_templates/new/statement/output/.gitignore +0 -0
  154. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_cli/scaffold_templates/new/statement/output/.gitkeep +0 -0
  155. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_cli/scaffold_templates/new/statement/report.py +0 -0
  156. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_cli/scaffold_templates/new/statement/templates/statement.css +0 -0
  157. {fullbleed-2.1.0 → fullbleed-2.2.2}/python/fullbleed_cli/scaffold_templates/new/statement/templates/statement.html +0 -0
  158. {fullbleed-2.1.0 → fullbleed-2.2.2}/src/base64.rs +0 -0
  159. {fullbleed-2.1.0 → fullbleed-2.2.2}/src/css_native.rs +0 -0
  160. {fullbleed-2.1.0 → fullbleed-2.2.2}/src/css_queries.rs +0 -0
  161. {fullbleed-2.1.0 → fullbleed-2.2.2}/src/debug.rs +0 -0
  162. {fullbleed-2.1.0 → fullbleed-2.2.2}/src/error.rs +0 -0
  163. {fullbleed-2.1.0 → fullbleed-2.2.2}/src/finalize.rs +0 -0
  164. {fullbleed-2.1.0 → fullbleed-2.2.2}/src/flate_native.rs +0 -0
  165. {fullbleed-2.1.0 → fullbleed-2.2.2}/src/glyph_report.rs +0 -0
  166. {fullbleed-2.1.0 → fullbleed-2.2.2}/src/html_dom.rs +0 -0
  167. {fullbleed-2.1.0 → fullbleed-2.2.2}/src/html_entities.rs +0 -0
  168. {fullbleed-2.1.0 → fullbleed-2.2.2}/src/html_entities_0.rs +0 -0
  169. {fullbleed-2.1.0 → fullbleed-2.2.2}/src/html_entities_1.rs +0 -0
  170. {fullbleed-2.1.0 → fullbleed-2.2.2}/src/html_entities_2.rs +0 -0
  171. {fullbleed-2.1.0 → fullbleed-2.2.2}/src/html_entities_3.rs +0 -0
  172. {fullbleed-2.1.0 → fullbleed-2.2.2}/src/html_entities_4.rs +0 -0
  173. {fullbleed-2.1.0 → fullbleed-2.2.2}/src/html_entities_5.rs +0 -0
  174. {fullbleed-2.1.0 → fullbleed-2.2.2}/src/jpeg_native.rs +0 -0
  175. {fullbleed-2.1.0 → fullbleed-2.2.2}/src/math.rs +0 -0
  176. {fullbleed-2.1.0 → fullbleed-2.2.2}/src/metrics.rs +0 -0
  177. {fullbleed-2.1.0 → fullbleed-2.2.2}/src/page_data.rs +0 -0
  178. {fullbleed-2.1.0 → fullbleed-2.2.2}/src/parallel.rs +0 -0
  179. {fullbleed-2.1.0 → fullbleed-2.2.2}/src/pdf_encodings.rs +0 -0
  180. {fullbleed-2.1.0 → fullbleed-2.2.2}/src/pdf_native.rs +0 -0
  181. {fullbleed-2.1.0 → fullbleed-2.2.2}/src/pdfinspect.rs +0 -0
  182. {fullbleed-2.1.0 → fullbleed-2.2.2}/src/perf.rs +0 -0
  183. {fullbleed-2.1.0 → fullbleed-2.2.2}/src/plan.rs +0 -0
  184. {fullbleed-2.1.0 → fullbleed-2.2.2}/src/python_abi.rs +0 -0
  185. {fullbleed-2.1.0 → fullbleed-2.2.2}/src/sfnt_cff.rs +0 -0
  186. {fullbleed-2.1.0 → fullbleed-2.2.2}/src/sfnt_outline.rs +0 -0
  187. {fullbleed-2.1.0 → fullbleed-2.2.2}/src/unicode_data.rs +0 -0
  188. {fullbleed-2.1.0 → fullbleed-2.2.2}/src/xml.rs +0 -0
@@ -0,0 +1,37 @@
1
+ # This file is automatically @generated by Cargo.
2
+ # It is not intended for manual editing.
3
+ version = 4
4
+
5
+ [[package]]
6
+ name = "fullbleed"
7
+ version = "2.2.2"
8
+ dependencies = [
9
+ "fullbleed_audit_contract",
10
+ "subsetter",
11
+ "unicode-bidi",
12
+ ]
13
+
14
+ [[package]]
15
+ name = "fullbleed_audit_contract"
16
+ version = "0.1.3"
17
+
18
+ [[package]]
19
+ name = "rustc-hash"
20
+ version = "2.1.3"
21
+ source = "registry+https://github.com/rust-lang/crates.io-index"
22
+ checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d"
23
+
24
+ [[package]]
25
+ name = "subsetter"
26
+ version = "0.2.6"
27
+ source = "registry+https://github.com/rust-lang/crates.io-index"
28
+ checksum = "38803281d1c23166c5ebcb455439a5d2afe711cc909cf88af72448c297756ad6"
29
+ dependencies = [
30
+ "rustc-hash",
31
+ ]
32
+
33
+ [[package]]
34
+ name = "unicode-bidi"
35
+ version = "0.3.18"
36
+ source = "registry+https://github.com/rust-lang/crates.io-index"
37
+ checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5"
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "fullbleed"
3
- version = "2.1.0"
3
+ version = "2.2.2"
4
4
  edition = "2024"
5
5
  rust-version = "1.85"
6
6
  license = "MIT"
@@ -37,4 +37,6 @@ inherits = "release"
37
37
  debug = true
38
38
 
39
39
  [dependencies]
40
- fullbleed_audit_contract = { version = "0.1.2", path = "crates/fullbleed_audit_contract" }
40
+ fullbleed_audit_contract = { version = "0.1.3", path = "crates/fullbleed_audit_contract" }
41
+ subsetter = { version = "0.2.6", default-features = false }
42
+ unicode-bidi = "0.3.18"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fullbleed
3
- Version: 2.1.0
3
+ Version: 2.2.2
4
4
  Classifier: Development Status :: 5 - Production/Stable
5
5
  Classifier: License :: OSI Approved :: MIT License
6
6
  Classifier: Operating System :: MacOS
@@ -91,7 +91,8 @@ Additional focused references are in `docs/`:
91
91
  - Python-first extension surface for hackability and custom workflows.
92
92
  - Python render calls release the GIL while Rust rendering executes.
93
93
  - Ordered standard-library worker pools for batch rendering and selected internal workloads.
94
- - No third-party Rust crate graph and no required third-party Python runtime or build packages.
94
+ - A deliberately small, license-audited Rust dependency graph and no required third-party Python
95
+ runtime or build packages.
95
96
 
96
97
  ## Concurrency Model
97
98
 
@@ -100,11 +101,11 @@ Additional focused references are in `docs/`:
100
101
  - The same bounded worker implementation serves selected internal hotspots such as table layout and JIT paint paths.
101
102
  - Do not assume every single-document render path will fully saturate all cores end-to-end.
102
103
 
103
- ## Performance in 2.1
104
+ ## Performance in 2.2
104
105
 
105
- Fullbleed 2.1 embeds exact TrueType glyph subsets instead of complete source font programs. Across
106
- the five independent benchmark fixtures, PDFs are 95.5-97.3% smaller and ordinary warm rendering
107
- is 2.90x faster by geometric mean than released 2.0.0.
106
+ Fullbleed 2.2 retains exact TrueType glyph subsetting and adds compiled fixed-geometry variable-data
107
+ binding. Across the five independent benchmark fixtures, PDFs are 95.5-97.3% smaller and ordinary
108
+ warm rendering is 2.90x faster by geometric mean than released 2.0.0.
108
109
 
109
110
  The new compile-once API freezes an immutable Q32.32 display document and can link it repeatedly
110
111
  without rerunning HTML parsing, selector matching, layout, pagination, or command planning. For
@@ -113,8 +114,24 @@ source page's content stream across the copied page dictionaries. The measured 2
113
114
  200.7x faster per page by geometric mean than the released 2.0.0 warm renderer; a 1,000-page stress
114
115
  run sustained 304,479-666,622 pages/s.
115
116
 
116
- That result is deliberately scoped to fixed compiled copies. It is not a claim that arbitrary new
117
- HTML or varying records render 200x faster. See
117
+ The 2.2 compiler includes a genuine fixed-geometry variable-data lane. A compiled invoice
118
+ with six bound fields rendered 100,000 distinct records in a five-run median of 283.807 ms to
119
+ memory (**352,352 pages/s**) and 295.999 ms directly to a flushed 88.1 MB PDF
120
+ (**337,839 pages/s**). Including the one-time 4.168 ms compile gives 333,147 direct-file pages/s.
121
+ The harness verified all 100,000 unique invoice IDs in page order, exact page count, resolved
122
+ markers, deterministic bytes, and equal buffer/file SHA-256. Reproduce it with:
123
+
124
+ ```bash
125
+ python tools/benchmark_fullbleed_vdp.py --records 100000 --repeats 5
126
+ ```
127
+
128
+ Fixed-geometry slots may remain inside immutable page-space transforms and clips. The compiler
129
+ captures that active coordinate-state program once and replays it in each compact record overlay;
130
+ it does not rerun layout or clone the complete page display list.
131
+
132
+ The fixed-copy result remains scoped to identical content; the variable-data result is scoped to
133
+ paint-only text whose geometry does not reflow. Neither is a claim that arbitrary new HTML or
134
+ size-changing records render 200x faster. See
118
135
  [`docs/performance-pass-2026-08-04.md`](docs/performance-pass-2026-08-04.md) for measurements and
119
136
  [`docs/performance-architecture.md`](docs/performance-architecture.md) for the packed vector IR,
120
137
  typed-binding, virtual-linker, and shader roadmap.
@@ -130,7 +147,7 @@ python -m pip install fullbleed
130
147
  From a local wheel:
131
148
 
132
149
  ```bash
133
- python -m pip install C:\path\to\fullbleed-2.1.0-cp310-abi3-win_amd64.whl
150
+ python -m pip install C:\path\to\fullbleed-2.2.2-cp310-abi3-win_amd64.whl
134
151
  ```
135
152
 
136
153
  From a source checkout with Rust installed, no Python build package is needed:
@@ -776,6 +793,20 @@ Summary as of May 19, 2026:
776
793
  - Canonical validation artifact: `_css_working/tmp/fixture_full_latest.json`
777
794
  - Canonical validation artifact: `_css_working/css_broad_coverage_sprint_s14.md`
778
795
 
796
+ The complete pinned independent Wheel265 gate closed on August 9, 2026 with `1,642 PASS`,
797
+ `0 FAIL`, and `20 REFERENCE-DISPUTED` across 1,662 fixtures. That is strict `100%` parity for
798
+ every adjudicable fixture in this named corpus; disputed references remain visible and are not
799
+ relabeled as implementation passes. Broader web-platform coverage remains bounded by the module
800
+ and known-gap contract in `docs/css-coverage.md`.
801
+
802
+ Flow/reflow hardening validated on August 5, 2026 adds fixed-height block,
803
+ one-column grid, and column-flex continuation slicing; avoids content
804
+ duplication; retains empty styled captions inside avoided figures; preserves
805
+ `display: contents` grid paint phase; and keeps positioned containing blocks
806
+ atomic. The independent review's targeted 18-case regression cluster passes
807
+ `18/18` (13 pixel-exact, maximum above-floor difference `0.97030199%`). This
808
+ targeted diagnostic does not replace the full external-corpus report.
809
+
779
810
  Current validated behavior includes first-class parser -> evaluator -> calculator -> layout/paint coverage across broad static-document CSS domains (values math, layout primitives, pagination/fragmentation baselines, transforms phase-1, gradient/effects subsets including color-first and interleaved-color `filter: drop-shadow(...)` with computed lengths, modern `rgb()` shadow colors with `mm` lengths, duplicate `drop-shadow(...)` color rejection, empty optional filter-function defaults, explicit/currentColor `backdrop-filter: drop-shadow(...)` raster paint, strict `box-shadow` length/color grammar with modern `rgb()` plus `mm` paint coverage, softened blurred inset edge paint, and directional inset offset edge paint, overflow-gated `text-overflow: ellipsis`, physical min/max box constraints, horizontal-tb RTL `direction` inline inset/margin/padding/border mapping, vertical writing-mode `direction: rtl` inline inset remapping, vertical-rl logical sizing/insets/margin/padding/border/min-max constraints, vertical-lr logical sizing/min-max/insets/margin/padding/border remapping, basic vertical text columns with `vertical-rl` leftward and `vertical-lr` rightward line progression, and deterministic diagnostics).
780
811
 
781
812
  Known gap categories are explicitly tracked for the final parity push:
@@ -972,9 +1003,14 @@ Module exports:
972
1003
  | `render_pdf(deterministic_hash=None)` | `bytes` |
973
1004
  | `render_pdf_to_file(path, deterministic_hash=None)` | `int` |
974
1005
  | `render_pdf_batch(copies, deterministic_hash=None)` | `bytes` |
975
-
976
- The compiled lane runs parsing/layout once. Its batch method writes one ordered PDF and virtualizes
977
- identical untagged page content to a shared stream; it is not a dynamic template-binding API.
1006
+ | `render_pdf_bindings(bindings, deterministic_hash=None)` | `bytes` |
1007
+ | `render_pdf_bindings_to_file(bindings, path, deterministic_hash=None)` | `int` |
1008
+
1009
+ The compiled lane runs parsing/layout once. `render_pdf_batch` virtualizes identical untagged page
1010
+ content. The binding methods instead accept columnar `dict[str, list[str]]` data and emit distinct
1011
+ fixed-geometry text overlays per record while sharing static paint. They require exact slot keys,
1012
+ equal non-zero column lengths, page-local WinAnsi-compatible text, and an untagged profile; bound
1013
+ values do not trigger reflow.
978
1014
 
979
1015
  When `deterministic_hash` is set, engine writes PDF SHA-256 to the provided file path.
980
1016
 
@@ -54,7 +54,8 @@ Additional focused references are in `docs/`:
54
54
  - Python-first extension surface for hackability and custom workflows.
55
55
  - Python render calls release the GIL while Rust rendering executes.
56
56
  - Ordered standard-library worker pools for batch rendering and selected internal workloads.
57
- - No third-party Rust crate graph and no required third-party Python runtime or build packages.
57
+ - A deliberately small, license-audited Rust dependency graph and no required third-party Python
58
+ runtime or build packages.
58
59
 
59
60
  ## Concurrency Model
60
61
 
@@ -63,11 +64,11 @@ Additional focused references are in `docs/`:
63
64
  - The same bounded worker implementation serves selected internal hotspots such as table layout and JIT paint paths.
64
65
  - Do not assume every single-document render path will fully saturate all cores end-to-end.
65
66
 
66
- ## Performance in 2.1
67
+ ## Performance in 2.2
67
68
 
68
- Fullbleed 2.1 embeds exact TrueType glyph subsets instead of complete source font programs. Across
69
- the five independent benchmark fixtures, PDFs are 95.5-97.3% smaller and ordinary warm rendering
70
- is 2.90x faster by geometric mean than released 2.0.0.
69
+ Fullbleed 2.2 retains exact TrueType glyph subsetting and adds compiled fixed-geometry variable-data
70
+ binding. Across the five independent benchmark fixtures, PDFs are 95.5-97.3% smaller and ordinary
71
+ warm rendering is 2.90x faster by geometric mean than released 2.0.0.
71
72
 
72
73
  The new compile-once API freezes an immutable Q32.32 display document and can link it repeatedly
73
74
  without rerunning HTML parsing, selector matching, layout, pagination, or command planning. For
@@ -76,8 +77,24 @@ source page's content stream across the copied page dictionaries. The measured 2
76
77
  200.7x faster per page by geometric mean than the released 2.0.0 warm renderer; a 1,000-page stress
77
78
  run sustained 304,479-666,622 pages/s.
78
79
 
79
- That result is deliberately scoped to fixed compiled copies. It is not a claim that arbitrary new
80
- HTML or varying records render 200x faster. See
80
+ The 2.2 compiler includes a genuine fixed-geometry variable-data lane. A compiled invoice
81
+ with six bound fields rendered 100,000 distinct records in a five-run median of 283.807 ms to
82
+ memory (**352,352 pages/s**) and 295.999 ms directly to a flushed 88.1 MB PDF
83
+ (**337,839 pages/s**). Including the one-time 4.168 ms compile gives 333,147 direct-file pages/s.
84
+ The harness verified all 100,000 unique invoice IDs in page order, exact page count, resolved
85
+ markers, deterministic bytes, and equal buffer/file SHA-256. Reproduce it with:
86
+
87
+ ```bash
88
+ python tools/benchmark_fullbleed_vdp.py --records 100000 --repeats 5
89
+ ```
90
+
91
+ Fixed-geometry slots may remain inside immutable page-space transforms and clips. The compiler
92
+ captures that active coordinate-state program once and replays it in each compact record overlay;
93
+ it does not rerun layout or clone the complete page display list.
94
+
95
+ The fixed-copy result remains scoped to identical content; the variable-data result is scoped to
96
+ paint-only text whose geometry does not reflow. Neither is a claim that arbitrary new HTML or
97
+ size-changing records render 200x faster. See
81
98
  [`docs/performance-pass-2026-08-04.md`](docs/performance-pass-2026-08-04.md) for measurements and
82
99
  [`docs/performance-architecture.md`](docs/performance-architecture.md) for the packed vector IR,
83
100
  typed-binding, virtual-linker, and shader roadmap.
@@ -93,7 +110,7 @@ python -m pip install fullbleed
93
110
  From a local wheel:
94
111
 
95
112
  ```bash
96
- python -m pip install C:\path\to\fullbleed-2.1.0-cp310-abi3-win_amd64.whl
113
+ python -m pip install C:\path\to\fullbleed-2.2.2-cp310-abi3-win_amd64.whl
97
114
  ```
98
115
 
99
116
  From a source checkout with Rust installed, no Python build package is needed:
@@ -739,6 +756,20 @@ Summary as of May 19, 2026:
739
756
  - Canonical validation artifact: `_css_working/tmp/fixture_full_latest.json`
740
757
  - Canonical validation artifact: `_css_working/css_broad_coverage_sprint_s14.md`
741
758
 
759
+ The complete pinned independent Wheel265 gate closed on August 9, 2026 with `1,642 PASS`,
760
+ `0 FAIL`, and `20 REFERENCE-DISPUTED` across 1,662 fixtures. That is strict `100%` parity for
761
+ every adjudicable fixture in this named corpus; disputed references remain visible and are not
762
+ relabeled as implementation passes. Broader web-platform coverage remains bounded by the module
763
+ and known-gap contract in `docs/css-coverage.md`.
764
+
765
+ Flow/reflow hardening validated on August 5, 2026 adds fixed-height block,
766
+ one-column grid, and column-flex continuation slicing; avoids content
767
+ duplication; retains empty styled captions inside avoided figures; preserves
768
+ `display: contents` grid paint phase; and keeps positioned containing blocks
769
+ atomic. The independent review's targeted 18-case regression cluster passes
770
+ `18/18` (13 pixel-exact, maximum above-floor difference `0.97030199%`). This
771
+ targeted diagnostic does not replace the full external-corpus report.
772
+
742
773
  Current validated behavior includes first-class parser -> evaluator -> calculator -> layout/paint coverage across broad static-document CSS domains (values math, layout primitives, pagination/fragmentation baselines, transforms phase-1, gradient/effects subsets including color-first and interleaved-color `filter: drop-shadow(...)` with computed lengths, modern `rgb()` shadow colors with `mm` lengths, duplicate `drop-shadow(...)` color rejection, empty optional filter-function defaults, explicit/currentColor `backdrop-filter: drop-shadow(...)` raster paint, strict `box-shadow` length/color grammar with modern `rgb()` plus `mm` paint coverage, softened blurred inset edge paint, and directional inset offset edge paint, overflow-gated `text-overflow: ellipsis`, physical min/max box constraints, horizontal-tb RTL `direction` inline inset/margin/padding/border mapping, vertical writing-mode `direction: rtl` inline inset remapping, vertical-rl logical sizing/insets/margin/padding/border/min-max constraints, vertical-lr logical sizing/min-max/insets/margin/padding/border remapping, basic vertical text columns with `vertical-rl` leftward and `vertical-lr` rightward line progression, and deterministic diagnostics).
743
774
 
744
775
  Known gap categories are explicitly tracked for the final parity push:
@@ -935,9 +966,14 @@ Module exports:
935
966
  | `render_pdf(deterministic_hash=None)` | `bytes` |
936
967
  | `render_pdf_to_file(path, deterministic_hash=None)` | `int` |
937
968
  | `render_pdf_batch(copies, deterministic_hash=None)` | `bytes` |
938
-
939
- The compiled lane runs parsing/layout once. Its batch method writes one ordered PDF and virtualizes
940
- identical untagged page content to a shared stream; it is not a dynamic template-binding API.
969
+ | `render_pdf_bindings(bindings, deterministic_hash=None)` | `bytes` |
970
+ | `render_pdf_bindings_to_file(bindings, path, deterministic_hash=None)` | `int` |
971
+
972
+ The compiled lane runs parsing/layout once. `render_pdf_batch` virtualizes identical untagged page
973
+ content. The binding methods instead accept columnar `dict[str, list[str]]` data and emit distinct
974
+ fixed-geometry text overlays per record while sharing static paint. They require exact slot keys,
975
+ equal non-zero column lengths, page-local WinAnsi-compatible text, and an untagged profile; bound
976
+ values do not trigger reflow.
941
977
 
942
978
  When `deterministic_hash` is set, engine writes PDF SHA-256 to the provided file path.
943
979
 
@@ -4,4 +4,4 @@ version = 4
4
4
 
5
5
  [[package]]
6
6
  name = "fullbleed_audit_contract"
7
- version = "0.1.2"
7
+ version = "0.1.3"
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "fullbleed_audit_contract"
3
- version = "0.1.2"
3
+ version = "0.1.3"
4
4
  edition = "2024"
5
5
  rust-version = "1.85"
6
6
  license = "MIT"
@@ -298,6 +298,9 @@ fn hash_memoized(cell: &OnceLock<String>, text: &str) -> String {
298
298
  static AUDIT_REGISTRY_HASH: OnceLock<String> = OnceLock::new();
299
299
  static WCAG20AA_REGISTRY_HASH: OnceLock<String> = OnceLock::new();
300
300
  static SECTION508_HTML_REGISTRY_HASH: OnceLock<String> = OnceLock::new();
301
+ static AUDIT_REGISTRY_TEXT: OnceLock<String> = OnceLock::new();
302
+ static WCAG20AA_REGISTRY_TEXT: OnceLock<String> = OnceLock::new();
303
+ static SECTION508_HTML_REGISTRY_TEXT: OnceLock<String> = OnceLock::new();
301
304
  static CONTRACT_FINGERPRINT: OnceLock<String> = OnceLock::new();
302
305
  static AUDIT_REGISTRY_JSON_VALUE: OnceLock<Value> = OnceLock::new();
303
306
  static WCAG20AA_REGISTRY_JSON_VALUE: OnceLock<Value> = OnceLock::new();
@@ -306,23 +309,45 @@ static A11Y_DEFAULT_GATE_LEVELS: OnceLock<BTreeMap<String, String>> = OnceLock::
306
309
  static A11Y_PROFILE_GATE_OVERRIDES: OnceLock<BTreeMap<String, BTreeMap<String, String>>> =
307
310
  OnceLock::new();
308
311
 
312
+ fn canonical_lf(text: &str) -> String {
313
+ text.replace("\r\n", "\n").replace('\r', "\n")
314
+ }
315
+
316
+ fn audit_registry_text() -> &'static str {
317
+ AUDIT_REGISTRY_TEXT
318
+ .get_or_init(|| canonical_lf(AUDIT_REGISTRY_V1_JSON))
319
+ .as_str()
320
+ }
321
+
322
+ fn wcag20aa_registry_text() -> &'static str {
323
+ WCAG20AA_REGISTRY_TEXT
324
+ .get_or_init(|| canonical_lf(WCAG20AA_REGISTRY_V1_JSON))
325
+ .as_str()
326
+ }
327
+
328
+ fn section508_html_registry_text() -> &'static str {
329
+ SECTION508_HTML_REGISTRY_TEXT
330
+ .get_or_init(|| canonical_lf(SECTION508_HTML_REGISTRY_V1_JSON))
331
+ .as_str()
332
+ }
333
+
309
334
  fn audit_registry_value() -> &'static Value {
310
335
  AUDIT_REGISTRY_JSON_VALUE.get_or_init(|| {
311
- json::parse(AUDIT_REGISTRY_V1_JSON)
336
+ json::parse(audit_registry_text())
312
337
  .expect("embedded audit registry must be valid JSON-formatted YAML")
313
338
  })
314
339
  }
315
340
 
316
341
  fn wcag20aa_registry_value() -> &'static Value {
317
342
  WCAG20AA_REGISTRY_JSON_VALUE.get_or_init(|| {
318
- json::parse(WCAG20AA_REGISTRY_V1_JSON)
343
+ json::parse(wcag20aa_registry_text())
319
344
  .expect("embedded wcag20aa registry must be valid JSON-formatted YAML")
320
345
  })
321
346
  }
322
347
 
323
348
  fn section508_html_registry_value() -> &'static Value {
324
349
  SECTION508_HTML_REGISTRY_JSON_VALUE.get_or_init(|| {
325
- json::parse(SECTION508_HTML_REGISTRY_V1_JSON)
350
+ json::parse(section508_html_registry_text())
326
351
  .expect("embedded section508 html registry must be valid JSON-formatted YAML")
327
352
  })
328
353
  }
@@ -350,25 +375,25 @@ fn worst_verdict<'a>(verdicts: impl IntoIterator<Item = &'a str>) -> Option<&'a
350
375
  }
351
376
 
352
377
  pub fn audit_registry_v1_json() -> &'static str {
353
- AUDIT_REGISTRY_V1_JSON
378
+ audit_registry_text()
354
379
  }
355
380
 
356
381
  pub fn wcag20aa_registry_v1_json() -> &'static str {
357
- WCAG20AA_REGISTRY_V1_JSON
382
+ wcag20aa_registry_text()
358
383
  }
359
384
 
360
385
  pub fn audit_registry_v1_hash_sha256() -> String {
361
- hash_memoized(&AUDIT_REGISTRY_HASH, AUDIT_REGISTRY_V1_JSON)
386
+ hash_memoized(&AUDIT_REGISTRY_HASH, audit_registry_text())
362
387
  }
363
388
 
364
389
  pub fn wcag20aa_registry_v1_hash_sha256() -> String {
365
- hash_memoized(&WCAG20AA_REGISTRY_HASH, WCAG20AA_REGISTRY_V1_JSON)
390
+ hash_memoized(&WCAG20AA_REGISTRY_HASH, wcag20aa_registry_text())
366
391
  }
367
392
 
368
393
  pub fn section508_html_registry_v1_hash_sha256() -> String {
369
394
  hash_memoized(
370
395
  &SECTION508_HTML_REGISTRY_HASH,
371
- SECTION508_HTML_REGISTRY_V1_JSON,
396
+ section508_html_registry_text(),
372
397
  )
373
398
  }
374
399
 
@@ -404,15 +429,15 @@ pub fn contract_fingerprint_sha256() -> String {
404
429
 
405
430
  pub fn registry_json(name: &str) -> Option<&'static str> {
406
431
  match name {
407
- AUDIT_REGISTRY_ID => Some(AUDIT_REGISTRY_V1_JSON),
408
- WCAG20AA_REGISTRY_ID => Some(WCAG20AA_REGISTRY_V1_JSON),
409
- SECTION508_HTML_REGISTRY_ID => Some(SECTION508_HTML_REGISTRY_V1_JSON),
432
+ AUDIT_REGISTRY_ID => Some(audit_registry_text()),
433
+ WCAG20AA_REGISTRY_ID => Some(wcag20aa_registry_text()),
434
+ SECTION508_HTML_REGISTRY_ID => Some(section508_html_registry_text()),
410
435
  _ => None,
411
436
  }
412
437
  }
413
438
 
414
439
  pub fn section508_html_registry_v1_json() -> &'static str {
415
- SECTION508_HTML_REGISTRY_V1_JSON
440
+ section508_html_registry_text()
416
441
  }
417
442
 
418
443
  pub fn wcag20aa_coverage_from_rule_verdicts<'a, I>(rule_verdicts: I) -> Wcag20AaCoverageSummary
@@ -860,7 +885,7 @@ pub fn metadata() -> AuditContractMetadata {
860
885
  mod tests {
861
886
  use super::*;
862
887
  fn parse_embedded_audit_registry() -> Value {
863
- json::parse(AUDIT_REGISTRY_V1_JSON).expect("embedded audit registry JSON should parse")
888
+ json::parse(audit_registry_text()).expect("embedded audit registry JSON should parse")
864
889
  }
865
890
 
866
891
  #[test]
@@ -871,7 +896,7 @@ mod tests {
871
896
  );
872
897
  assert_eq!(
873
898
  wcag20aa_registry_v1_hash_sha256(),
874
- "3ff936a514baac7b528b2745ec88c9e8d2b4c52c56bd63b39478a897ac52f30c"
899
+ "108d4e5062ede7c5c3893fff78153a58f35749d2dd9df58173fc2dd0dbcd36b6"
875
900
  );
876
901
  assert_eq!(
877
902
  section508_html_registry_v1_hash_sha256(),
@@ -882,10 +907,22 @@ mod tests {
882
907
  assert_eq!(a, b);
883
908
  assert_eq!(
884
909
  a,
885
- "db3f5de6ea0c6cd5b3c1828c58f2023d093cc4a46c9302c2ac3bed47c5277de5"
910
+ "83123c2d5ff2a8b459326a360e3eeac1134fb1eef4c704c433dadfd3e32606d5"
886
911
  );
887
912
  }
888
913
 
914
+ #[test]
915
+ fn registry_payloads_are_canonical_lf() {
916
+ for id in [
917
+ AUDIT_REGISTRY_ID,
918
+ WCAG20AA_REGISTRY_ID,
919
+ SECTION508_HTML_REGISTRY_ID,
920
+ ] {
921
+ let payload = registry_json(id).expect("known registry should have a payload");
922
+ assert!(!payload.contains('\r'), "{id} contains a carriage return");
923
+ }
924
+ }
925
+
889
926
  #[test]
890
927
  fn registry_lookup_returns_known_payloads() {
891
928
  assert!(
@@ -5,7 +5,7 @@ backend-path = ["build_backend"]
5
5
 
6
6
  [project]
7
7
  name = "fullbleed"
8
- version = "2.1.0"
8
+ version = "2.2.2"
9
9
  description = "Deterministic static HTML/CSS-to-PDF renderer built in Rust"
10
10
  readme = "README.md"
11
11
  requires-python = ">=3.10"
@@ -200,6 +200,25 @@ class CompiledDocument(metaclass=_NativeFacadeType):
200
200
  deterministic_hash,
201
201
  )
202
202
 
203
+ def render_pdf_bindings(self, /, bindings, deterministic_hash=None):
204
+ """Render one fixed-geometry page set per row of columnar text bindings."""
205
+ return _call(
206
+ self._handle,
207
+ "CompiledDocument.render_pdf_bindings",
208
+ bindings,
209
+ deterministic_hash,
210
+ )
211
+
212
+ def render_pdf_bindings_to_file(self, /, bindings, path, deterministic_hash=None):
213
+ """Stream a columnar fixed-geometry binding batch directly to ``path``."""
214
+ return _call(
215
+ self._handle,
216
+ "CompiledDocument.render_pdf_bindings_to_file",
217
+ bindings,
218
+ path,
219
+ deterministic_hash,
220
+ )
221
+
203
222
 
204
223
  class PdfEngine(metaclass=_NativeFacadeType):
205
224
  __slots__ = ("_handle",)
@@ -9,6 +9,25 @@ from typing import Any, Callable
9
9
  from .style import style_to_css
10
10
 
11
11
 
12
+ _HTML_VOID_ELEMENTS = frozenset(
13
+ {
14
+ "area",
15
+ "base",
16
+ "br",
17
+ "col",
18
+ "embed",
19
+ "hr",
20
+ "img",
21
+ "input",
22
+ "link",
23
+ "meta",
24
+ "source",
25
+ "track",
26
+ "wbr",
27
+ }
28
+ )
29
+
30
+
12
31
  def _normalize_css_href(value: str | None) -> str | None:
13
32
  if value is None:
14
33
  return None
@@ -256,6 +275,8 @@ def render_node(node: Any) -> str:
256
275
  return ""
257
276
  if isinstance(node, Element):
258
277
  attrs = _render_attrs(node.props)
278
+ if node.tag.casefold() in _HTML_VOID_ELEMENTS:
279
+ return f"<{node.tag}{attrs}>"
259
280
  children_html = "".join(render_node(child) for child in node.children)
260
281
  return f"<{node.tag}{attrs}>{children_html}</{node.tag}>"
261
282
  return escape(str(node))
@@ -115,7 +115,10 @@ pub fn is_supported_font_path(path: &Path) -> bool {
115
115
  let Some(ext) = path.extension().and_then(|v| v.to_str()) else {
116
116
  return false;
117
117
  };
118
- matches!(ext.to_ascii_lowercase().as_str(), "ttf" | "otf")
118
+ matches!(
119
+ ext.to_ascii_lowercase().as_str(),
120
+ "ttf" | "otf" | "ttc" | "otc"
121
+ )
119
122
  }
120
123
 
121
124
  #[cfg_attr(not(feature = "python"), allow(dead_code))]
@@ -512,9 +515,11 @@ mod tests {
512
515
  }
513
516
 
514
517
  #[test]
515
- fn supported_font_path_accepts_ttf_and_otf() {
518
+ fn supported_font_path_accepts_sfnt_fonts_and_collections() {
516
519
  assert!(super::is_supported_font_path(Path::new("demo.ttf")));
517
520
  assert!(super::is_supported_font_path(Path::new("demo.otf")));
521
+ assert!(super::is_supported_font_path(Path::new("demo.ttc")));
522
+ assert!(super::is_supported_font_path(Path::new("demo.otc")));
518
523
  assert!(!super::is_supported_font_path(Path::new("demo.woff2")));
519
524
  }
520
525