fullbleed 2.3.0__tar.gz → 2.3.1__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 (196) hide show
  1. {fullbleed-2.3.0 → fullbleed-2.3.1}/Cargo.lock +1 -1
  2. {fullbleed-2.3.0 → fullbleed-2.3.1}/Cargo.toml +1 -1
  3. {fullbleed-2.3.0 → fullbleed-2.3.1}/PKG-INFO +10 -4
  4. {fullbleed-2.3.0 → fullbleed-2.3.1}/README.md +9 -3
  5. {fullbleed-2.3.0 → fullbleed-2.3.1}/fullbleed-agent-contract.json +1 -1
  6. {fullbleed-2.3.0 → fullbleed-2.3.1}/llms.txt +1 -1
  7. {fullbleed-2.3.0 → fullbleed-2.3.1}/pyproject.toml +1 -1
  8. {fullbleed-2.3.0 → fullbleed-2.3.1}/src/flowable.rs +12 -0
  9. {fullbleed-2.3.0 → fullbleed-2.3.1}/src/lib.rs +57 -0
  10. {fullbleed-2.3.0 → fullbleed-2.3.1}/src/pdf.rs +150 -25
  11. {fullbleed-2.3.0 → fullbleed-2.3.1}/src/pdf_raster.rs +199 -12
  12. {fullbleed-2.3.0 → fullbleed-2.3.1}/COPYRIGHT +0 -0
  13. {fullbleed-2.3.0 → fullbleed-2.3.1}/FONT_LICENSE_AUDIT.json +0 -0
  14. {fullbleed-2.3.0 → fullbleed-2.3.1}/FONT_LICENSE_AUDIT.md +0 -0
  15. {fullbleed-2.3.0 → fullbleed-2.3.1}/LICENSE +0 -0
  16. {fullbleed-2.3.0 → fullbleed-2.3.1}/LICENSING.md +0 -0
  17. {fullbleed-2.3.0 → fullbleed-2.3.1}/THIRD_PARTY_LICENSES.md +0 -0
  18. {fullbleed-2.3.0 → fullbleed-2.3.1}/build.rs +0 -0
  19. {fullbleed-2.3.0 → fullbleed-2.3.1}/build_backend/fullbleed_build_backend.py +0 -0
  20. {fullbleed-2.3.0 → fullbleed-2.3.1}/crates/fullbleed_audit_contract/Cargo.lock +0 -0
  21. {fullbleed-2.3.0 → fullbleed-2.3.1}/crates/fullbleed_audit_contract/Cargo.toml +0 -0
  22. {fullbleed-2.3.0 → fullbleed-2.3.1}/crates/fullbleed_audit_contract/LICENSE +0 -0
  23. {fullbleed-2.3.0 → fullbleed-2.3.1}/crates/fullbleed_audit_contract/README.md +0 -0
  24. {fullbleed-2.3.0 → fullbleed-2.3.1}/crates/fullbleed_audit_contract/specs/fullbleed.audit_registry.v1.yaml +0 -0
  25. {fullbleed-2.3.0 → fullbleed-2.3.1}/crates/fullbleed_audit_contract/specs/section508_html_registry.v1.yaml +0 -0
  26. {fullbleed-2.3.0 → fullbleed-2.3.1}/crates/fullbleed_audit_contract/specs/wcag20aa_registry.v1.yaml +0 -0
  27. {fullbleed-2.3.0 → fullbleed-2.3.1}/crates/fullbleed_audit_contract/src/json.rs +0 -0
  28. {fullbleed-2.3.0 → fullbleed-2.3.1}/crates/fullbleed_audit_contract/src/lib.rs +0 -0
  29. {fullbleed-2.3.0 → fullbleed-2.3.1}/crates/fullbleed_audit_contract/src/sha256.rs +0 -0
  30. {fullbleed-2.3.0 → fullbleed-2.3.1}/docs/specs/fullbleed.a11y.verify.v1.schema.json +0 -0
  31. {fullbleed-2.3.0 → fullbleed-2.3.1}/docs/specs/fullbleed.audit_registry.v1.yaml +0 -0
  32. {fullbleed-2.3.0 → fullbleed-2.3.1}/docs/specs/fullbleed.pmr.v1.schema.json +0 -0
  33. {fullbleed-2.3.0 → fullbleed-2.3.1}/docs/specs/section508_html_registry.v1.yaml +0 -0
  34. {fullbleed-2.3.0 → fullbleed-2.3.1}/docs/specs/wcag20aa_registry.v1.yaml +0 -0
  35. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed/__init__.py +0 -0
  36. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed/__main__.py +0 -0
  37. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed/_abi.py +0 -0
  38. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed/accessibility/__init__.py +0 -0
  39. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed/accessibility/engine.py +0 -0
  40. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed/accessibility/runtime.py +0 -0
  41. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed/accessibility/types.py +0 -0
  42. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed/audit_prototype.py +0 -0
  43. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed/audit_section508.py +0 -0
  44. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed/audit_wcag.py +0 -0
  45. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed/ui/__init__.py +0 -0
  46. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed/ui/accessibility.py +0 -0
  47. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed/ui/cav/__init__.py +0 -0
  48. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed/ui/cav/_core.py +0 -0
  49. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed/ui/cav/agency_letter.py +0 -0
  50. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed/ui/cav/court_motion_form.py +0 -0
  51. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed/ui/cav/declaration_form.py +0 -0
  52. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed/ui/cav/instruction_sheet.py +0 -0
  53. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed/ui/cav/investment_portfolio_report.py +0 -0
  54. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed/ui/cav/marriage_record.py +0 -0
  55. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed/ui/cav/profiles.py +0 -0
  56. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed/ui/cav/recorded_plat.py +0 -0
  57. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed/ui/cav/redaction_request_form.py +0 -0
  58. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed/ui/cav/warranty_deed.py +0 -0
  59. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed/ui/core.py +0 -0
  60. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed/ui/primitives.py +0 -0
  61. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed/ui/style.py +0 -0
  62. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_assets/__init__.py +0 -0
  63. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_assets/bootstrap.min.css +0 -0
  64. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_assets/fonts/Inter-Variable.ttf +0 -0
  65. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_assets/fonts/NotoSans-Regular.ttf +0 -0
  66. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_assets/fonts/NotoSansMath-Regular.ttf +0 -0
  67. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_assets/fonts/NotoSansSymbols-Regular.ttf +0 -0
  68. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_assets/fonts/NotoSansSymbols2-Regular.ttf +0 -0
  69. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_assets/icons/bootstrap-icons.svg +0 -0
  70. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/__init__.py +0 -0
  71. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/__main__.py +0 -0
  72. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/agent.py +0 -0
  73. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/agent_acceptance.py +0 -0
  74. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/agent_contract.py +0 -0
  75. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/assets.py +0 -0
  76. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/cache.py +0 -0
  77. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/cli.py +0 -0
  78. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/finalize.py +0 -0
  79. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/mcp.py +0 -0
  80. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/scaffold.py +0 -0
  81. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/scaffold_templates/__init__.py +0 -0
  82. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/scaffold_templates/init/.gitignore +0 -0
  83. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/scaffold_templates/init/AGENTS.md +0 -0
  84. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/scaffold_templates/init/COMPLIANCE.md +0 -0
  85. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/scaffold_templates/init/SCAFFOLDING.md +0 -0
  86. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/scaffold_templates/init/components/__init__.py +0 -0
  87. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/scaffold_templates/init/components/body.py +0 -0
  88. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/scaffold_templates/init/components/fb_ui.py +0 -0
  89. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/scaffold_templates/init/components/footer.py +0 -0
  90. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/scaffold_templates/init/components/header.py +0 -0
  91. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/scaffold_templates/init/components/primitives.py +0 -0
  92. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/scaffold_templates/init/components/styles/body.css +0 -0
  93. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/scaffold_templates/init/components/styles/footer.css +0 -0
  94. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/scaffold_templates/init/components/styles/header.css +0 -0
  95. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/scaffold_templates/init/components/styles/primitives.css +0 -0
  96. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/scaffold_templates/init/report.py +0 -0
  97. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/scaffold_templates/init/styles/report.css +0 -0
  98. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/scaffold_templates/init/styles/tokens.css +0 -0
  99. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/scaffold_templates/new/accessible/README.md +0 -0
  100. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/scaffold_templates/new/accessible/output/.gitignore +0 -0
  101. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/scaffold_templates/new/accessible/output/.gitkeep +0 -0
  102. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/scaffold_templates/new/accessible/report.py +0 -0
  103. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/scaffold_templates/new/accessible/styles/report.css +0 -0
  104. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/scaffold_templates/new/accessible/vendor/fonts/Inter-Variable.ttf +0 -0
  105. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/scaffold_templates/new/invoice/output/.gitignore +0 -0
  106. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/scaffold_templates/new/invoice/output/.gitkeep +0 -0
  107. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/scaffold_templates/new/invoice/report.py +0 -0
  108. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/scaffold_templates/new/invoice/templates/invoice.css +0 -0
  109. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/scaffold_templates/new/invoice/templates/invoice.html +0 -0
  110. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/scaffold_templates/new/reference/COMPLIANCE.md +0 -0
  111. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/scaffold_templates/new/reference/README.md +0 -0
  112. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/scaffold_templates/new/reference/SCAFFOLDING.md +0 -0
  113. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/scaffold_templates/new/reference/assets/brand-mark.svg +0 -0
  114. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/scaffold_templates/new/reference/assets/reference-pattern.svg +0 -0
  115. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/scaffold_templates/new/reference/components/__init__.py +0 -0
  116. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/scaffold_templates/new/reference/components/appendix.py +0 -0
  117. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/scaffold_templates/new/reference/components/architecture.py +0 -0
  118. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/scaffold_templates/new/reference/components/cover.py +0 -0
  119. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/scaffold_templates/new/reference/components/data_pages.py +0 -0
  120. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/scaffold_templates/new/reference/components/fb_ui.py +0 -0
  121. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/scaffold_templates/new/reference/components/primitives.py +0 -0
  122. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/scaffold_templates/new/reference/components/styles/appendix.css +0 -0
  123. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/scaffold_templates/new/reference/components/styles/architecture.css +0 -0
  124. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/scaffold_templates/new/reference/components/styles/cover.css +0 -0
  125. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/scaffold_templates/new/reference/components/styles/data.css +0 -0
  126. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/scaffold_templates/new/reference/components/styles/primitives.css +0 -0
  127. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/scaffold_templates/new/reference/components/styles/visuals.css +0 -0
  128. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/scaffold_templates/new/reference/components/visuals.py +0 -0
  129. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/scaffold_templates/new/reference/data/reference.json +0 -0
  130. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/scaffold_templates/new/reference/output/.gitignore +0 -0
  131. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/scaffold_templates/new/reference/report.py +0 -0
  132. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/scaffold_templates/new/reference/styles/report.css +0 -0
  133. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/scaffold_templates/new/reference/styles/tokens.css +0 -0
  134. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/scaffold_templates/new/statement/output/.gitignore +0 -0
  135. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/scaffold_templates/new/statement/output/.gitkeep +0 -0
  136. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/scaffold_templates/new/statement/report.py +0 -0
  137. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/scaffold_templates/new/statement/templates/statement.css +0 -0
  138. {fullbleed-2.3.0 → fullbleed-2.3.1}/python/fullbleed_cli/scaffold_templates/new/statement/templates/statement.html +0 -0
  139. {fullbleed-2.3.0 → fullbleed-2.3.1}/skills/fullbleed/SKILL.md +0 -0
  140. {fullbleed-2.3.0 → fullbleed-2.3.1}/skills/fullbleed/agents/openai.yaml +0 -0
  141. {fullbleed-2.3.0 → fullbleed-2.3.1}/skills/fullbleed/references/compliance.md +0 -0
  142. {fullbleed-2.3.0 → fullbleed-2.3.1}/skills/fullbleed/references/selection.md +0 -0
  143. {fullbleed-2.3.0 → fullbleed-2.3.1}/skills/fullbleed/references/workflows.md +0 -0
  144. {fullbleed-2.3.0 → fullbleed-2.3.1}/src/assets.rs +0 -0
  145. {fullbleed-2.3.0 → fullbleed-2.3.1}/src/base64.rs +0 -0
  146. {fullbleed-2.3.0 → fullbleed-2.3.1}/src/canvas.rs +0 -0
  147. {fullbleed-2.3.0 → fullbleed-2.3.1}/src/css_native.rs +0 -0
  148. {fullbleed-2.3.0 → fullbleed-2.3.1}/src/css_queries.rs +0 -0
  149. {fullbleed-2.3.0 → fullbleed-2.3.1}/src/debug.rs +0 -0
  150. {fullbleed-2.3.0 → fullbleed-2.3.1}/src/doc_context.rs +0 -0
  151. {fullbleed-2.3.0 → fullbleed-2.3.1}/src/doc_template.rs +0 -0
  152. {fullbleed-2.3.0 → fullbleed-2.3.1}/src/error.rs +0 -0
  153. {fullbleed-2.3.0 → fullbleed-2.3.1}/src/finalize.rs +0 -0
  154. {fullbleed-2.3.0 → fullbleed-2.3.1}/src/flate_native.rs +0 -0
  155. {fullbleed-2.3.0 → fullbleed-2.3.1}/src/font.rs +0 -0
  156. {fullbleed-2.3.0 → fullbleed-2.3.1}/src/font_subset.rs +0 -0
  157. {fullbleed-2.3.0 → fullbleed-2.3.1}/src/frame.rs +0 -0
  158. {fullbleed-2.3.0 → fullbleed-2.3.1}/src/glyph_report.rs +0 -0
  159. {fullbleed-2.3.0 → fullbleed-2.3.1}/src/html.rs +0 -0
  160. {fullbleed-2.3.0 → fullbleed-2.3.1}/src/html_dom.rs +0 -0
  161. {fullbleed-2.3.0 → fullbleed-2.3.1}/src/html_entities.rs +0 -0
  162. {fullbleed-2.3.0 → fullbleed-2.3.1}/src/html_entities_0.rs +0 -0
  163. {fullbleed-2.3.0 → fullbleed-2.3.1}/src/html_entities_1.rs +0 -0
  164. {fullbleed-2.3.0 → fullbleed-2.3.1}/src/html_entities_2.rs +0 -0
  165. {fullbleed-2.3.0 → fullbleed-2.3.1}/src/html_entities_3.rs +0 -0
  166. {fullbleed-2.3.0 → fullbleed-2.3.1}/src/html_entities_4.rs +0 -0
  167. {fullbleed-2.3.0 → fullbleed-2.3.1}/src/html_entities_5.rs +0 -0
  168. {fullbleed-2.3.0 → fullbleed-2.3.1}/src/image_native.rs +0 -0
  169. {fullbleed-2.3.0 → fullbleed-2.3.1}/src/jit.rs +0 -0
  170. {fullbleed-2.3.0 → fullbleed-2.3.1}/src/jpeg_native.rs +0 -0
  171. {fullbleed-2.3.0 → fullbleed-2.3.1}/src/math.rs +0 -0
  172. {fullbleed-2.3.0 → fullbleed-2.3.1}/src/metrics.rs +0 -0
  173. {fullbleed-2.3.0 → fullbleed-2.3.1}/src/native_shape.rs +0 -0
  174. {fullbleed-2.3.0 → fullbleed-2.3.1}/src/page_data.rs +0 -0
  175. {fullbleed-2.3.0 → fullbleed-2.3.1}/src/page_template.rs +0 -0
  176. {fullbleed-2.3.0 → fullbleed-2.3.1}/src/parallel.rs +0 -0
  177. {fullbleed-2.3.0 → fullbleed-2.3.1}/src/pdf_encodings.rs +0 -0
  178. {fullbleed-2.3.0 → fullbleed-2.3.1}/src/pdf_native.rs +0 -0
  179. {fullbleed-2.3.0 → fullbleed-2.3.1}/src/pdfinspect.rs +0 -0
  180. {fullbleed-2.3.0 → fullbleed-2.3.1}/src/perf.rs +0 -0
  181. {fullbleed-2.3.0 → fullbleed-2.3.1}/src/plan.rs +0 -0
  182. {fullbleed-2.3.0 → fullbleed-2.3.1}/src/python/abi_exports.rs +0 -0
  183. {fullbleed-2.3.0 → fullbleed-2.3.1}/src/python.rs +0 -0
  184. {fullbleed-2.3.0 → fullbleed-2.3.1}/src/python_abi.rs +0 -0
  185. {fullbleed-2.3.0 → fullbleed-2.3.1}/src/raster.rs +0 -0
  186. {fullbleed-2.3.0 → fullbleed-2.3.1}/src/raster_native.rs +0 -0
  187. {fullbleed-2.3.0 → fullbleed-2.3.1}/src/sfnt.rs +0 -0
  188. {fullbleed-2.3.0 → fullbleed-2.3.1}/src/sfnt_cff.rs +0 -0
  189. {fullbleed-2.3.0 → fullbleed-2.3.1}/src/sfnt_outline.rs +0 -0
  190. {fullbleed-2.3.0 → fullbleed-2.3.1}/src/spill.rs +0 -0
  191. {fullbleed-2.3.0 → fullbleed-2.3.1}/src/style.rs +0 -0
  192. {fullbleed-2.3.0 → fullbleed-2.3.1}/src/svg.rs +0 -0
  193. {fullbleed-2.3.0 → fullbleed-2.3.1}/src/text_shape.rs +0 -0
  194. {fullbleed-2.3.0 → fullbleed-2.3.1}/src/types.rs +0 -0
  195. {fullbleed-2.3.0 → fullbleed-2.3.1}/src/unicode_data.rs +0 -0
  196. {fullbleed-2.3.0 → fullbleed-2.3.1}/src/xml.rs +0 -0
@@ -4,7 +4,7 @@ version = 4
4
4
 
5
5
  [[package]]
6
6
  name = "fullbleed"
7
- version = "2.3.0"
7
+ version = "2.3.1"
8
8
  dependencies = [
9
9
  "fullbleed_audit_contract",
10
10
  "subsetter",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "fullbleed"
3
- version = "2.3.0"
3
+ version = "2.3.1"
4
4
  edition = "2024"
5
5
  rust-version = "1.85"
6
6
  license = "MIT"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fullbleed
3
- Version: 2.3.0
3
+ Version: 2.3.1
4
4
  Classifier: Development Status :: 5 - Production/Stable
5
5
  Classifier: License :: OSI Approved :: MIT License
6
6
  Classifier: Operating System :: MacOS
@@ -170,6 +170,11 @@ Fixed-geometry slots may remain inside immutable page-space transforms and clips
170
170
  captures that active coordinate-state program once and replays it in each compact record overlay;
171
171
  it does not rerun layout or clone the complete page display list.
172
172
 
173
+ Base-14/WinAnsi slot paint keeps the measured byte-patch linker. Registered embedded fonts use a
174
+ command-level overlay program so each bound value is shaped and included in the font subset while
175
+ the compiled layout, pagination, and static page stream remain shared. The throughput figures above
176
+ describe the Base-14 fast lane; they are not claims for registered-font shaping.
177
+
173
178
  The fixed-copy result remains scoped to identical content; the variable-data result is scoped to
174
179
  paint-only text whose geometry does not reflow. Neither is a claim that arbitrary new HTML or
175
180
  size-changing records render 200x faster. See
@@ -208,7 +213,7 @@ python -m pip install fullbleed
208
213
  From a local wheel:
209
214
 
210
215
  ```bash
211
- python -m pip install C:\path\to\fullbleed-2.3.0-cp310-abi3-win_amd64.whl
216
+ python -m pip install C:\path\to\fullbleed-2.3.1-cp310-abi3-win_amd64.whl
212
217
  ```
213
218
 
214
219
  From a source checkout with Rust installed, no Python build package is needed:
@@ -1079,8 +1084,9 @@ Module exports:
1079
1084
  The compiled lane runs parsing/layout once. `render_pdf_batch` virtualizes identical untagged page
1080
1085
  content. The fixed binding methods accept columnar `dict[str, list[str]]` data and emit distinct
1081
1086
  fixed-geometry text overlays per record while sharing static paint. They require exact slot keys,
1082
- equal non-zero column lengths, page-local WinAnsi-compatible text, and an untagged profile; bound
1083
- values do not trigger reflow.
1087
+ equal non-zero column lengths, page-local text, and an untagged profile. Base-14 text uses the
1088
+ direct byte-patch lane; registered fonts are shaped into a command-level overlay. Bound values do
1089
+ not trigger layout or reflow.
1084
1090
 
1085
1091
  The reflow binding methods use the same exact-column contract but execute the compiled parsed-DOM
1086
1092
  program. `{{slot}}` values are literal text and may reshape, wrap, resize blocks, and change page
@@ -127,6 +127,11 @@ Fixed-geometry slots may remain inside immutable page-space transforms and clips
127
127
  captures that active coordinate-state program once and replays it in each compact record overlay;
128
128
  it does not rerun layout or clone the complete page display list.
129
129
 
130
+ Base-14/WinAnsi slot paint keeps the measured byte-patch linker. Registered embedded fonts use a
131
+ command-level overlay program so each bound value is shaped and included in the font subset while
132
+ the compiled layout, pagination, and static page stream remain shared. The throughput figures above
133
+ describe the Base-14 fast lane; they are not claims for registered-font shaping.
134
+
130
135
  The fixed-copy result remains scoped to identical content; the variable-data result is scoped to
131
136
  paint-only text whose geometry does not reflow. Neither is a claim that arbitrary new HTML or
132
137
  size-changing records render 200x faster. See
@@ -165,7 +170,7 @@ python -m pip install fullbleed
165
170
  From a local wheel:
166
171
 
167
172
  ```bash
168
- python -m pip install C:\path\to\fullbleed-2.3.0-cp310-abi3-win_amd64.whl
173
+ python -m pip install C:\path\to\fullbleed-2.3.1-cp310-abi3-win_amd64.whl
169
174
  ```
170
175
 
171
176
  From a source checkout with Rust installed, no Python build package is needed:
@@ -1036,8 +1041,9 @@ Module exports:
1036
1041
  The compiled lane runs parsing/layout once. `render_pdf_batch` virtualizes identical untagged page
1037
1042
  content. The fixed binding methods accept columnar `dict[str, list[str]]` data and emit distinct
1038
1043
  fixed-geometry text overlays per record while sharing static paint. They require exact slot keys,
1039
- equal non-zero column lengths, page-local WinAnsi-compatible text, and an untagged profile; bound
1040
- values do not trigger reflow.
1044
+ equal non-zero column lengths, page-local text, and an untagged profile. Base-14 text uses the
1045
+ direct byte-patch lane; registered fonts are shaped into a command-level overlay. Bound values do
1046
+ not trigger layout or reflow.
1041
1047
 
1042
1048
  The reflow binding methods use the same exact-column contract but execute the compiled parsed-DOM
1043
1049
  program. `{{slot}}` values are literal text and may reshape, wrap, resize blocks, and change page
@@ -3640,7 +3640,7 @@
3640
3640
  "license": "MIT",
3641
3641
  "name": "Fullbleed PDF Engine",
3642
3642
  "python_package": "fullbleed",
3643
- "version": "2.3.0"
3643
+ "version": "2.3.1"
3644
3644
  },
3645
3645
  "profiles": {
3646
3646
  "pdf": {
@@ -1,6 +1,6 @@
1
1
  # Fullbleed PDF Engine
2
2
 
3
- > Fullbleed 2.3.0 is a deterministic, self-contained document-generation runtime for Python and Rust. It turns structured data plus static HTML/CSS into validated PDF documents without a browser.
3
+ > Fullbleed 2.3.1 is a deterministic, self-contained document-generation runtime for Python and Rust. It turns structured data plus static HTML/CSS into validated PDF documents without a browser.
4
4
 
5
5
  The installed runtime is authoritative. Do not infer its API from this file or from remembered release knowledge. Start with:
6
6
 
@@ -5,7 +5,7 @@ backend-path = ["build_backend"]
5
5
 
6
6
  [project]
7
7
  name = "fullbleed"
8
- version = "2.3.0"
8
+ version = "2.3.1"
9
9
  description = "Deterministic Python PDF engine for reports, invoices, accessible documents, print-ready output, and compiled VDP"
10
10
  readme = "README.md"
11
11
  requires-python = ">=3.10"
@@ -5600,6 +5600,7 @@ fn draw_registered_text_run(
5600
5600
  y: Pt,
5601
5601
  text: String,
5602
5602
  ) {
5603
+ let contains_binding_slot = !crate::binding_slot_names(&text).is_empty();
5603
5604
  let text = crate::text_shape::encode_shape_options(&text, text_shape_options(style));
5604
5605
  let synthetic_bold = style.font_synthesis_weight
5605
5606
  && !style.font_face_satisfies_weight
@@ -5614,6 +5615,17 @@ fn draw_registered_text_run(
5614
5615
  && registry.requires_synthetic_italic(font_name);
5615
5616
  let strength = (style.font_size * (1.0 / 32.0)).max(Pt::from_f32(0.25));
5616
5617
  let italic_shear = synthetic_italic_shear(style.font_style);
5618
+ // Fixed VDP substitutes slot values after layout. Keep binding-bearing
5619
+ // text as a patchable text command instead of compiling the placeholder's
5620
+ // glyph IDs into the reusable Type 3 synthetic-bold shader.
5621
+ if contains_binding_slot && synthetic_bold {
5622
+ if synthetic_italic {
5623
+ canvas.draw_string_synthetic_bold_italic(x, y, text, strength, italic_shear);
5624
+ } else {
5625
+ canvas.draw_string_synthetic_bold(x, y, text, strength);
5626
+ }
5627
+ return;
5628
+ }
5617
5629
  match (synthetic_bold, synthetic_italic) {
5618
5630
  (true, true) if style.css_pixel_snap_metrics => {
5619
5631
  if draw_registered_synthetic_bold_outline_run(
@@ -14264,6 +14264,63 @@ h1 { font-size: 20pt; }
14264
14264
  ));
14265
14265
  }
14266
14266
 
14267
+ #[test]
14268
+ fn compiled_document_binds_registered_identity_h_fonts_without_literal_slots() {
14269
+ let mut bundle = AssetBundle::default();
14270
+ bundle.add(Asset::new(
14271
+ "VdpCustom".to_string(),
14272
+ AssetKind::Font,
14273
+ std::fs::read(repo_font_path("NotoSans-Regular.ttf")).expect("read custom font"),
14274
+ None,
14275
+ true,
14276
+ ));
14277
+ let engine = FullBleed::builder()
14278
+ .register_bundle(bundle)
14279
+ .build()
14280
+ .expect("engine");
14281
+ let compiled = engine
14282
+ .compile_document(
14283
+ "<main><h1>{{attendee}}</h1><p>{{role}}</p><strong>{{seat}}</strong></main>",
14284
+ "@page { size: 260pt 140pt; margin: 12pt; } body { margin: 0; font-family: VdpCustom; font-weight: 400; } h1 { font-size: 20pt; font-weight: 700; }",
14285
+ )
14286
+ .expect("compile registered-font template");
14287
+ let bindings = std::collections::HashMap::from([
14288
+ (
14289
+ "attendee".to_string(),
14290
+ vec!["ADA RIVERA".to_string(), "GRACE HOPPER".to_string()],
14291
+ ),
14292
+ (
14293
+ "role".to_string(),
14294
+ vec!["RESEARCHER".to_string(), "ENGINEER".to_string()],
14295
+ ),
14296
+ (
14297
+ "seat".to_string(),
14298
+ vec!["A12".to_string(), "B07".to_string()],
14299
+ ),
14300
+ ]);
14301
+ let pdf = compiled
14302
+ .render_bindings_to_buffer(&bindings)
14303
+ .expect("registered-font fixed bindings");
14304
+ let inspection = inspect_pdf_bytes(&pdf).expect("inspect binding output");
14305
+ assert_eq!(inspection.page_count, 2);
14306
+
14307
+ let parsed = crate::pdf_native::Document::load_mem(&pdf).expect("parse binding PDF");
14308
+ let page_numbers = parsed.get_pages().keys().copied().collect::<Vec<_>>();
14309
+ let text = parsed
14310
+ .extract_text_chunks(&page_numbers)
14311
+ .into_iter()
14312
+ .collect::<crate::pdf_native::Result<Vec<_>>>()
14313
+ .expect("extract registered-font bindings")
14314
+ .join("\n");
14315
+ assert!(text.contains("ADA RIVERA"));
14316
+ assert!(text.contains("GRACE HOPPER"));
14317
+ assert!(text.contains("RESEARCHER"));
14318
+ assert!(text.contains("ENGINEER"));
14319
+ assert!(text.contains("A12"));
14320
+ assert!(text.contains("B07"));
14321
+ assert!(!text.contains("{{"));
14322
+ }
14323
+
14267
14324
  #[test]
14268
14325
  fn compiled_reflow_bindings_reuse_dom_and_generate_variable_page_counts() {
14269
14326
  let engine = FullBleed::builder().build().expect("engine");
@@ -438,14 +438,21 @@ struct BindingPageProgram {
438
438
  static_content_id: usize,
439
439
  static_content_len: usize,
440
440
  geometry: PageGeometry,
441
- dynamic_segments: Vec<BindingContentSegment>,
442
- dynamic_capacity: usize,
443
- dynamic_slot_occurrences: usize,
441
+ dynamic: BindingDynamicProgram,
442
+ }
443
+
444
+ enum BindingDynamicProgram {
445
+ Patched {
446
+ segments: Vec<BindingContentSegment>,
447
+ capacity: usize,
448
+ },
449
+ Commands(Page),
444
450
  }
445
451
 
446
452
  struct CompiledBindingPage {
447
453
  static_page: Page,
448
454
  overlay_page: Page,
455
+ dynamic_slot_occurrences: usize,
449
456
  }
450
457
 
451
458
  /// Immutable command partition produced once for a compiled template.
@@ -558,10 +565,102 @@ fn instantiate_binding_content(
558
565
  }
559
566
  }
560
567
 
568
+ fn instantiate_binding_text(
569
+ template: &str,
570
+ slot_lookup: &HashMap<String, usize>,
571
+ columns: &[&[String]],
572
+ row: usize,
573
+ ) -> String {
574
+ let spans = crate::binding_slot_spans(template);
575
+ if spans.is_empty() {
576
+ return template.to_string();
577
+ }
578
+
579
+ let mut output = String::with_capacity(template.len().saturating_add(32));
580
+ let mut cursor = 0usize;
581
+ for (start, end, name) in spans {
582
+ output.push_str(&template[cursor..start]);
583
+ if let Some(slot_index) = slot_lookup.get(name) {
584
+ output.push_str(&columns[*slot_index][row]);
585
+ } else {
586
+ output.push_str(&template[start..end]);
587
+ }
588
+ cursor = end;
589
+ }
590
+ output.push_str(&template[cursor..]);
591
+ output
592
+ }
593
+
594
+ fn instantiate_binding_overlay_page(
595
+ template: &Page,
596
+ slot_lookup: &HashMap<String, usize>,
597
+ columns: &[&[String]],
598
+ row: usize,
599
+ ) -> Page {
600
+ let commands = template
601
+ .commands
602
+ .iter()
603
+ .map(|command| match command {
604
+ Command::DrawString { x, y, text } => Command::DrawString {
605
+ x: *x,
606
+ y: *y,
607
+ text: instantiate_binding_text(text, slot_lookup, columns, row),
608
+ },
609
+ Command::DrawStringTransformed {
610
+ x,
611
+ y,
612
+ text,
613
+ m00,
614
+ m01,
615
+ m10,
616
+ m11,
617
+ } => Command::DrawStringTransformed {
618
+ x: *x,
619
+ y: *y,
620
+ text: instantiate_binding_text(text, slot_lookup, columns, row),
621
+ m00: *m00,
622
+ m01: *m01,
623
+ m10: *m10,
624
+ m11: *m11,
625
+ },
626
+ _ => command.clone(),
627
+ })
628
+ .collect();
629
+ Page { commands }
630
+ }
631
+
632
+ fn binding_overlay_uses_registered_font(page: &Page, registry: Option<&FontRegistry>) -> bool {
633
+ let Some(registry) = registry else {
634
+ return false;
635
+ };
636
+ let mut font_name = "Helvetica".to_string();
637
+ let mut stack = Vec::new();
638
+ for command in &page.commands {
639
+ match command {
640
+ Command::SaveState => stack.push(font_name.clone()),
641
+ Command::RestoreState => {
642
+ if let Some(saved) = stack.pop() {
643
+ font_name = saved;
644
+ }
645
+ }
646
+ Command::SetFontName(value) => font_name.clone_from(value),
647
+ Command::DrawString { text, .. } | Command::DrawStringTransformed { text, .. }
648
+ if !crate::binding_slot_names(text).is_empty()
649
+ && registry.resolve(&font_name).is_some() =>
650
+ {
651
+ return true;
652
+ }
653
+ _ => {}
654
+ }
655
+ }
656
+ false
657
+ }
658
+
561
659
  #[derive(Clone)]
562
660
  struct BindingPaintState {
563
661
  font_name: String,
564
662
  font_size: Pt,
663
+ line_width: Pt,
565
664
  fill: Color,
566
665
  stroke: Color,
567
666
  opacity_fill: f32,
@@ -575,6 +674,7 @@ impl Default for BindingPaintState {
575
674
  Self {
576
675
  font_name: "Helvetica".to_string(),
577
676
  font_size: Pt::from_f32(12.0),
677
+ line_width: Pt::from_f32(1.0),
578
678
  fill: Color::BLACK,
579
679
  stroke: Color::BLACK,
580
680
  opacity_fill: 1.0,
@@ -683,6 +783,7 @@ fn binding_overlay_commands(
683
783
  Command::SetBlendMode { mode } => state.blend_mode = *mode,
684
784
  Command::SetFontName(value) => state.font_name.clone_from(value),
685
785
  Command::SetFontSize(value) => state.font_size = *value,
786
+ Command::SetLineWidth(value) => state.line_width = *value,
686
787
  Command::SetTextRenderingMode(value) => state.text_rendering_mode = *value,
687
788
  Command::DefineForm { commands, .. } | Command::DefineIsolatedForm { commands, .. }
688
789
  if commands_contain_binding_marker(commands) =>
@@ -724,6 +825,7 @@ fn binding_overlay_commands(
724
825
  }
725
826
  overlay.push(Command::SetFontName(state.font_name.clone()));
726
827
  overlay.push(Command::SetFontSize(state.font_size));
828
+ overlay.push(Command::SetLineWidth(state.line_width));
727
829
  if state.text_rendering_mode != 0 {
728
830
  overlay.push(Command::SetTextRenderingMode(state.text_rendering_mode));
729
831
  }
@@ -774,6 +876,7 @@ fn binding_overlay_commands(
774
876
  }
775
877
  overlay.push(Command::SetFontName(state.font_name.clone()));
776
878
  overlay.push(Command::SetFontSize(state.font_size));
879
+ overlay.push(Command::SetLineWidth(state.line_width));
777
880
  if state.text_rendering_mode != 0 {
778
881
  overlay.push(Command::SetTextRenderingMode(state.text_rendering_mode));
779
882
  }
@@ -812,7 +915,11 @@ pub(crate) fn compile_binding_plan(
812
915
  let (overlay_commands, page_slot_counts) =
813
916
  binding_overlay_commands(&page.commands, &slot_lookup)?;
814
917
  let static_commands = binding_static_commands(&page.commands, &slot_lookup);
815
- for (total, count) in total_slot_counts.iter_mut().zip(page_slot_counts) {
918
+ let dynamic_slot_occurrences = page_slot_counts.iter().copied().sum();
919
+ for (total, count) in total_slot_counts
920
+ .iter_mut()
921
+ .zip(page_slot_counts.iter().copied())
922
+ {
816
923
  *total = total.saturating_add(count);
817
924
  }
818
925
  static_command_count = static_command_count.saturating_add(static_commands.len());
@@ -824,6 +931,7 @@ pub(crate) fn compile_binding_plan(
824
931
  overlay_page: Page {
825
932
  commands: overlay_commands,
826
933
  },
934
+ dynamic_slot_occurrences,
827
935
  });
828
936
  }
829
937
 
@@ -1587,47 +1695,64 @@ impl<'a, W: Write> PdfStreamWriter<'a, W> {
1587
1695
  self.write_content_stream_object(static_content_id, "", static_content.as_bytes())?;
1588
1696
  self.page_content_stream_count = self.page_content_stream_count.saturating_add(1);
1589
1697
 
1590
- let overlay_template =
1591
- self.render_page_sized(&page.overlay_page, page_index, geometry)?;
1592
- let (dynamic_segments, dynamic_slot_occurrences, dynamic_static_bytes) =
1593
- compile_binding_content_segments(&overlay_template, &binding_plan.slot_lookup);
1594
- if dynamic_slot_occurrences == 0 {
1595
- return Err(io::Error::new(
1596
- io::ErrorKind::InvalidInput,
1597
- "compiled slot overlay could not be lowered to patchable WinAnsi text",
1598
- ));
1599
- }
1698
+ let dynamic = if binding_overlay_uses_registered_font(&page.overlay_page, self.registry)
1699
+ {
1700
+ BindingDynamicProgram::Commands(page.overlay_page.clone())
1701
+ } else {
1702
+ let overlay_template =
1703
+ self.render_page_sized(&page.overlay_page, page_index, geometry)?;
1704
+ let (segments, occurrences, static_bytes) =
1705
+ compile_binding_content_segments(&overlay_template, &binding_plan.slot_lookup);
1706
+ if occurrences == page.dynamic_slot_occurrences {
1707
+ BindingDynamicProgram::Patched {
1708
+ segments,
1709
+ capacity: static_bytes.saturating_add(128),
1710
+ }
1711
+ } else {
1712
+ BindingDynamicProgram::Commands(page.overlay_page.clone())
1713
+ }
1714
+ };
1600
1715
  programs.push(BindingPageProgram {
1601
1716
  static_content_id,
1602
1717
  static_content_len: static_content.len(),
1603
1718
  geometry,
1604
- dynamic_segments,
1605
- dynamic_capacity: dynamic_static_bytes.saturating_add(128),
1606
- dynamic_slot_occurrences,
1719
+ dynamic,
1607
1720
  });
1608
1721
  }
1609
1722
 
1610
1723
  let max_dynamic_capacity = programs
1611
1724
  .iter()
1612
- .map(|program| program.dynamic_capacity)
1725
+ .filter_map(|program| match &program.dynamic {
1726
+ BindingDynamicProgram::Patched { capacity, .. } => Some(*capacity),
1727
+ BindingDynamicProgram::Commands(_) => None,
1728
+ })
1613
1729
  .max()
1614
1730
  .unwrap_or(0);
1615
1731
  let mut dynamic_content = Vec::with_capacity(max_dynamic_capacity);
1616
1732
  for row in 0..record_count {
1617
1733
  for program in &programs {
1618
- instantiate_binding_content(
1619
- &program.dynamic_segments,
1620
- columns,
1621
- row,
1622
- &mut dynamic_content,
1623
- );
1734
+ match &program.dynamic {
1735
+ BindingDynamicProgram::Patched { segments, .. } => {
1736
+ instantiate_binding_content(segments, columns, row, &mut dynamic_content);
1737
+ }
1738
+ BindingDynamicProgram::Commands(template) => {
1739
+ let overlay = instantiate_binding_overlay_page(
1740
+ template,
1741
+ &binding_plan.slot_lookup,
1742
+ columns,
1743
+ row,
1744
+ );
1745
+ dynamic_content = self
1746
+ .render_page_sized(&overlay, self.page_ids.len(), program.geometry)?
1747
+ .into_bytes();
1748
+ }
1749
+ }
1624
1750
  self.add_bound_page(
1625
1751
  program.static_content_id,
1626
1752
  program.static_content_len,
1627
1753
  &dynamic_content,
1628
1754
  program.geometry,
1629
1755
  )?;
1630
- debug_assert!(program.dynamic_slot_occurrences > 0);
1631
1756
  }
1632
1757
  }
1633
1758
  Ok(())