pagyra-js 0.0.1

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 (540) hide show
  1. package/.eslintrc.json +30 -0
  2. package/CHANGELOG.md +13 -0
  3. package/README.md +275 -0
  4. package/UA_Styles_Chromium.md +93 -0
  5. package/_ext/woff2_conversion/brotli-decode.d.ts +139 -0
  6. package/_ext/woff2_conversion/brotli-encode.d.ts +129 -0
  7. package/_ext/woff2_conversion/brotli-port.d.ts +12 -0
  8. package/_ext/woff2_conversion/brotli-shared-dictionary.d.ts +25 -0
  9. package/_ext/woff2_conversion/brotli-types.d.ts +15 -0
  10. package/_ext/woff2_conversion/woff2-common.d.ts +37 -0
  11. package/_ext/woff2_conversion/woff2-decode.d.ts +32 -0
  12. package/_ext/woff2_conversion/woff2-encode.d.ts +31 -0
  13. package/_ext/woff2_conversion/woff2-output.d.ts +39 -0
  14. package/_ext/woff2_original_cpp/brotli/brotli.c +1559 -0
  15. package/_ext/woff2_original_cpp/brotli/brotli.md +116 -0
  16. package/_ext/woff2_original_cpp/brotli/decode.h +409 -0
  17. package/_ext/woff2_original_cpp/brotli/encode.h +505 -0
  18. package/_ext/woff2_original_cpp/brotli/port.h +302 -0
  19. package/_ext/woff2_original_cpp/brotli/shared_dictionary.h +100 -0
  20. package/_ext/woff2_original_cpp/brotli/types.h +83 -0
  21. package/_ext/woff2_original_cpp/cmake/FindBrotliDec.cmake +35 -0
  22. package/_ext/woff2_original_cpp/cmake/FindBrotliEnc.cmake +35 -0
  23. package/_ext/woff2_original_cpp/include/woff2/decode.h +36 -0
  24. package/_ext/woff2_original_cpp/include/woff2/encode.h +43 -0
  25. package/_ext/woff2_original_cpp/include/woff2/output.h +86 -0
  26. package/_ext/woff2_original_cpp/src/buffer.h +164 -0
  27. package/_ext/woff2_original_cpp/src/convert_woff2ttf_fuzzer.cc +13 -0
  28. package/_ext/woff2_original_cpp/src/convert_woff2ttf_fuzzer_new_entry.cc +12 -0
  29. package/_ext/woff2_original_cpp/src/file.h +30 -0
  30. package/_ext/woff2_original_cpp/src/font.cc +400 -0
  31. package/_ext/woff2_original_cpp/src/font.h +105 -0
  32. package/_ext/woff2_original_cpp/src/glyph.cc +383 -0
  33. package/_ext/woff2_original_cpp/src/glyph.h +71 -0
  34. package/_ext/woff2_original_cpp/src/normalize.cc +314 -0
  35. package/_ext/woff2_original_cpp/src/normalize.h +39 -0
  36. package/_ext/woff2_original_cpp/src/port.h +66 -0
  37. package/_ext/woff2_original_cpp/src/round.h +27 -0
  38. package/_ext/woff2_original_cpp/src/store_bytes.h +55 -0
  39. package/_ext/woff2_original_cpp/src/table_tags.cc +82 -0
  40. package/_ext/woff2_original_cpp/src/table_tags.h +30 -0
  41. package/_ext/woff2_original_cpp/src/transform.cc +430 -0
  42. package/_ext/woff2_original_cpp/src/transform.h +26 -0
  43. package/_ext/woff2_original_cpp/src/variable_length.cc +129 -0
  44. package/_ext/woff2_original_cpp/src/variable_length.h +30 -0
  45. package/_ext/woff2_original_cpp/src/woff2_common.cc +50 -0
  46. package/_ext/woff2_original_cpp/src/woff2_common.h +64 -0
  47. package/_ext/woff2_original_cpp/src/woff2_compress.cc +43 -0
  48. package/_ext/woff2_original_cpp/src/woff2_dec.cc +1398 -0
  49. package/_ext/woff2_original_cpp/src/woff2_decompress.cc +41 -0
  50. package/_ext/woff2_original_cpp/src/woff2_enc.cc +458 -0
  51. package/_ext/woff2_original_cpp/src/woff2_info.cc +142 -0
  52. package/_ext/woff2_original_cpp/src/woff2_out.cc +63 -0
  53. package/assets/fonts/ttf/arimo/Arimo-Bold.ttf +0 -0
  54. package/assets/fonts/ttf/arimo/Arimo-BoldItalic.ttf +0 -0
  55. package/assets/fonts/ttf/arimo/Arimo-Italic.ttf +0 -0
  56. package/assets/fonts/ttf/arimo/Arimo-Regular.ttf +0 -0
  57. package/assets/fonts/ttf/cinzeldecorative/CinzelDecorative-Black.ttf +0 -0
  58. package/assets/fonts/ttf/cinzeldecorative/CinzelDecorative-Bold.ttf +0 -0
  59. package/assets/fonts/ttf/cinzeldecorative/CinzelDecorative-Regular.ttf +0 -0
  60. package/assets/fonts/ttf/dejavu/DejaVuSans.ttf +0 -0
  61. package/assets/fonts/ttf/firecode/FiraCode-Bold.ttf +0 -0
  62. package/assets/fonts/ttf/firecode/FiraCode-Light.ttf +0 -0
  63. package/assets/fonts/ttf/firecode/FiraCode-Medium.ttf +0 -0
  64. package/assets/fonts/ttf/firecode/FiraCode-Regular.ttf +0 -0
  65. package/assets/fonts/ttf/firecode/FiraCode-SemiBold.ttf +0 -0
  66. package/assets/fonts/ttf/notoemoji/NotoEmoji-Bold.ttf +0 -0
  67. package/assets/fonts/ttf/notoemoji/NotoEmoji-Light.ttf +0 -0
  68. package/assets/fonts/ttf/notoemoji/NotoEmoji-Medium.ttf +0 -0
  69. package/assets/fonts/ttf/notoemoji/NotoEmoji-Regular.ttf +0 -0
  70. package/assets/fonts/ttf/notoemoji/NotoEmoji-SemiBold.ttf +0 -0
  71. package/assets/fonts/ttf/notosans/NotoSans-Regular.ttf +0 -0
  72. package/assets/fonts/ttf/roboto/Roboto-Bold.ttf +0 -0
  73. package/assets/fonts/ttf/roboto/Roboto-BoldItalic.ttf +0 -0
  74. package/assets/fonts/ttf/roboto/Roboto-Italic.ttf +0 -0
  75. package/assets/fonts/ttf/roboto/Roboto-Regular.ttf +0 -0
  76. package/assets/fonts/ttf/stixtwomath/STIXTwoMath-Regular.ttf +0 -0
  77. package/assets/fonts/ttf/tinos/Tinos-Bold.ttf +0 -0
  78. package/assets/fonts/ttf/tinos/Tinos-BoldItalic.ttf +0 -0
  79. package/assets/fonts/ttf/tinos/Tinos-Italic.ttf +0 -0
  80. package/assets/fonts/ttf/tinos/Tinos-Regular.ttf +0 -0
  81. package/assets/fonts/woff/lato/lato-latin-400-italic.woff +0 -0
  82. package/assets/fonts/woff/lato/lato-latin-400-normal.woff +0 -0
  83. package/assets/fonts/woff/lato/lato-latin-700-italic.woff +0 -0
  84. package/assets/fonts/woff/lato/lato-latin-700-normal.woff +0 -0
  85. package/assets/fonts/woff2/caveat/Caveat-Bold.woff2 +0 -0
  86. package/assets/fonts/woff2/caveat/Caveat-Regular.woff2 +0 -0
  87. package/assets/fonts/woff2/lato/lato-latin-400-italic.woff2 +0 -0
  88. package/assets/fonts/woff2/lato/lato-latin-400-normal.woff2 +0 -0
  89. package/assets/fonts/woff2/lato/lato-latin-700-italic.woff2 +0 -0
  90. package/assets/fonts/woff2/lato/lato-latin-700-normal.woff2 +0 -0
  91. package/docs/AGENTS.md +288 -0
  92. package/docs/BACKGROUND-REPEAT-IMPLEMENTATION.md +127 -0
  93. package/docs/BACKGROUND-REPEAT-REFERENCE.md +127 -0
  94. package/docs/BACKGROUND-REPEAT-SPACE-ROUND.md +164 -0
  95. package/docs/css-properties-support.md +256 -0
  96. package/docs/src_modules_table.md +172 -0
  97. package/docs/text-overlap-fix.md +85 -0
  98. package/docs/text-overlap-investigation.md +27 -0
  99. package/eslint.config.js +36 -0
  100. package/glyph_measure.htm +1458 -0
  101. package/package.json +50 -0
  102. package/playground/browser-entry.ts +2 -0
  103. package/playground/exports/background-text-debug.pdf +0 -0
  104. package/playground/public/app.js +875 -0
  105. package/playground/public/assets/1.webp +0 -0
  106. package/playground/public/examples/accents-test.html +24 -0
  107. package/playground/public/examples/advanced-selectors-demo.html +118 -0
  108. package/playground/public/examples/background-advanced-showcase.html +82 -0
  109. package/playground/public/examples/background-clip-text.html +36 -0
  110. package/playground/public/examples/background-origin-showcase.html +137 -0
  111. package/playground/public/examples/background-position-showcase.html +83 -0
  112. package/playground/public/examples/background-repeat-showcase.html +83 -0
  113. package/playground/public/examples/background-repeat-space-round.html +348 -0
  114. package/playground/public/examples/background-size-showcase.html +82 -0
  115. package/playground/public/examples/background-text-debug.html +18 -0
  116. package/playground/public/examples/baseline-test.html +24 -0
  117. package/playground/public/examples/bold-showcase.html +150 -0
  118. package/playground/public/examples/bold-strike-example.html +12 -0
  119. package/playground/public/examples/border-collapse-test.html +23 -0
  120. package/playground/public/examples/centered-shadow-div.html +72 -0
  121. package/playground/public/examples/css-variables.html +50 -0
  122. package/playground/public/examples/debug-accents.html +11 -0
  123. package/playground/public/examples/debug-text-overlap.html +46 -0
  124. package/playground/public/examples/flex-gap-column.html +130 -0
  125. package/playground/public/examples/flex-gap-row.html +137 -0
  126. package/playground/public/examples/flex-padding-test.html +29 -0
  127. package/playground/public/examples/flexbox-text-test.html +193 -0
  128. package/playground/public/examples/fonts-demo.html +126 -0
  129. package/playground/public/examples/footer-example.html +4 -0
  130. package/playground/public/examples/gradient-text.html +54 -0
  131. package/playground/public/examples/grid-gap-demo.html +156 -0
  132. package/playground/public/examples/header-example.html +4 -0
  133. package/playground/public/examples/header-footer-example.html +27 -0
  134. package/playground/public/examples/image-showcase.html +33 -0
  135. package/playground/public/examples/justify-text.html +22 -0
  136. package/playground/public/examples/linear-gradient-example.html +38 -0
  137. package/playground/public/examples/lorem-span.html +14 -0
  138. package/playground/public/examples/margin-block-showcase.html +21 -0
  139. package/playground/public/examples/margin-inline-showcase.html +21 -0
  140. package/playground/public/examples/monthly-summary.html +95 -0
  141. package/playground/public/examples/multi-page-lorem.html +190 -0
  142. package/playground/public/examples/opacity-debug.html +39 -0
  143. package/playground/public/examples/opacity-example.html +70 -0
  144. package/playground/public/examples/png-image-example.html +13 -0
  145. package/playground/public/examples/red-rectangle.html +18 -0
  146. package/playground/public/examples/repro.html +24 -0
  147. package/playground/public/examples/rounded-borders-test.html +24 -0
  148. package/playground/public/examples/simple-list.html +89 -0
  149. package/playground/public/examples/simple-svg.html +37 -0
  150. package/playground/public/examples/simple-table.html +52 -0
  151. package/playground/public/examples/skew-div.html +138 -0
  152. package/playground/public/examples/skew-text.html +21 -0
  153. package/playground/public/examples/starter-report.css +51 -0
  154. package/playground/public/examples/starter-report.html +23 -0
  155. package/playground/public/examples/svg-aspect-ratio-showcase.html +116 -0
  156. package/playground/public/examples/svg-gradients-linear.html +28 -0
  157. package/playground/public/examples/svg-gradients-radial.html +29 -0
  158. package/playground/public/examples/svg-gradients-showcase.html +66 -0
  159. package/playground/public/examples/svg-image-path-test.html +43 -0
  160. package/playground/public/examples/svg-images-clipping.html +27 -0
  161. package/playground/public/examples/svg-path-gallery.html +118 -0
  162. package/playground/public/examples/svg-radial-transform-demo.html +78 -0
  163. package/playground/public/examples/svg-transform-stack.html +103 -0
  164. package/playground/public/examples/svg-transforms-demo.html +127 -0
  165. package/playground/public/examples/table-merge-test.html +34 -0
  166. package/playground/public/examples/text-decoration-showcase.html +138 -0
  167. package/playground/public/examples/text-indent-showcase.html +137 -0
  168. package/playground/public/examples/text-shadow-example.html +29 -0
  169. package/playground/public/examples/very-complex-css.html +293 -0
  170. package/playground/public/examples/webp-example.html +13 -0
  171. package/playground/public/examples/z-index-demo.html +93 -0
  172. package/playground/public/examples.json +240 -0
  173. package/playground/public/images/dice.png +0 -0
  174. package/playground/public/images/duck.jpg +0 -0
  175. package/playground/public/index.html +149 -0
  176. package/playground/public/mode.js +1 -0
  177. package/playground/public/styles.css +382 -0
  178. package/playground/public/tmp-h2-debug.html +33 -0
  179. package/playground/public/tmp-italic-debug.html +32 -0
  180. package/playground/public/vendor/codemirror/codemirror.min.css +1 -0
  181. package/playground/public/vendor/codemirror/codemirror.min.js +1 -0
  182. package/playground/public/vendor/codemirror/css.min.js +1 -0
  183. package/playground/public/vendor/codemirror/darcula.min.css +1 -0
  184. package/playground/public/vendor/codemirror/htmlmixed.min.js +1 -0
  185. package/playground/public/vendor/codemirror/javascript.min.js +1 -0
  186. package/playground/public/vendor/codemirror/xml.min.js +1 -0
  187. package/playground/public/vendor/pagyra-playground-browser.js +165966 -0
  188. package/playground/public/vendor/pagyra-playground-browser.js.map +7 -0
  189. package/playground/server.d.ts +1 -0
  190. package/playground/server.js +68 -0
  191. package/playground/server.ts +128 -0
  192. package/scripts/browser-build.ts +101 -0
  193. package/scripts/build-browser-bundle.ts +52 -0
  194. package/scripts/glyph-comparison/simulate.ts +744 -0
  195. package/scripts/playground-browser-server.ts +57 -0
  196. package/scripts/probe-roboto.ts +6 -0
  197. package/scripts/render-playground-example.ts +121 -0
  198. package/scripts/run-glyph-atlas-tuner-runner.mjs +113 -0
  199. package/scripts/run-glyph-atlas-tuner.ts +141 -0
  200. package/scripts/top-ts-files.ps1 +39 -0
  201. package/scripts/top-ts-files.sh +37 -0
  202. package/scripts/woff2_info.ps1 +132 -0
  203. package/src/browser-entry.ts +14 -0
  204. package/src/compression/adler32.ts +45 -0
  205. package/src/compression/brotli/brotli.ts +463 -0
  206. package/src/compression/brotli/index.ts +15 -0
  207. package/src/compression/brotli/transform.ts +184 -0
  208. package/src/compression/brotli/types.ts +58 -0
  209. package/src/compression/brotli/utils.ts +157 -0
  210. package/src/compression/brotli/vendor/bit_reader.js +124 -0
  211. package/src/compression/brotli/vendor/context.js +250 -0
  212. package/src/compression/brotli/vendor/decode.d.ts +2 -0
  213. package/src/compression/brotli/vendor/decode.js +938 -0
  214. package/src/compression/brotli/vendor/dictionary-data.js +9469 -0
  215. package/src/compression/brotli/vendor/dictionary.js +36 -0
  216. package/src/compression/brotli/vendor/huffman.js +123 -0
  217. package/src/compression/brotli/vendor/package.json +3 -0
  218. package/src/compression/brotli/vendor/prefix.js +60 -0
  219. package/src/compression/brotli/vendor/streams.js +31 -0
  220. package/src/compression/brotli/vendor/transform.js +247 -0
  221. package/src/compression/brotli/vendor-decode.d.ts +4 -0
  222. package/src/compression/brotli/woff2-glyf-transform.ts +623 -0
  223. package/src/compression/decompress.ts +16 -0
  224. package/src/compression/deflate.ts +295 -0
  225. package/src/compression/index.ts +4 -0
  226. package/src/compression/types.ts +26 -0
  227. package/src/compression/utils.ts +107 -0
  228. package/src/core.ts +18 -0
  229. package/src/css/apply-declarations.ts +86 -0
  230. package/src/css/background-types.ts +65 -0
  231. package/src/css/browser-defaults.ts +16 -0
  232. package/src/css/clip-path-types.ts +13 -0
  233. package/src/css/compute-style.ts +494 -0
  234. package/src/css/css-unit-resolver.ts +65 -0
  235. package/src/css/custom-properties.ts +215 -0
  236. package/src/css/enums.ts +127 -0
  237. package/src/css/font-face-parser.ts +233 -0
  238. package/src/css/font-weight.ts +65 -0
  239. package/src/css/inline-style-parser.ts +27 -0
  240. package/src/css/layout-property-resolver.ts +75 -0
  241. package/src/css/length.ts +141 -0
  242. package/src/css/line-height.ts +96 -0
  243. package/src/css/named-colors.ts +150 -0
  244. package/src/css/parsers/background-parser-extended.ts +111 -0
  245. package/src/css/parsers/background-parser.ts +456 -0
  246. package/src/css/parsers/border-block-parser.ts +26 -0
  247. package/src/css/parsers/border-inline-parser.ts +26 -0
  248. package/src/css/parsers/border-parser-extended.ts +256 -0
  249. package/src/css/parsers/border-parser.ts +175 -0
  250. package/src/css/parsers/box-shadow-parser.ts +106 -0
  251. package/src/css/parsers/clip-path-parser.ts +92 -0
  252. package/src/css/parsers/color-parser.ts +14 -0
  253. package/src/css/parsers/dimension-parser.ts +117 -0
  254. package/src/css/parsers/display-flex-parser.ts +59 -0
  255. package/src/css/parsers/flex-parser.ts +144 -0
  256. package/src/css/parsers/font-parser.ts +40 -0
  257. package/src/css/parsers/gradient-parser.ts +366 -0
  258. package/src/css/parsers/grid-parser-extended.ts +55 -0
  259. package/src/css/parsers/grid-parser.ts +218 -0
  260. package/src/css/parsers/length-parser.ts +95 -0
  261. package/src/css/parsers/list-style-parser.ts +39 -0
  262. package/src/css/parsers/margin-block-parser.ts +12 -0
  263. package/src/css/parsers/margin-inline-parser.ts +12 -0
  264. package/src/css/parsers/margin-parser.ts +30 -0
  265. package/src/css/parsers/opacity-parser.ts +32 -0
  266. package/src/css/parsers/overflow-wrap-parser.ts +38 -0
  267. package/src/css/parsers/padding-block-parser.ts +12 -0
  268. package/src/css/parsers/padding-inline-parser.ts +12 -0
  269. package/src/css/parsers/padding-parser.ts +30 -0
  270. package/src/css/parsers/position-parser.ts +75 -0
  271. package/src/css/parsers/register-parsers.ts +302 -0
  272. package/src/css/parsers/registry.ts +18 -0
  273. package/src/css/parsers/text-parser-extended.ts +144 -0
  274. package/src/css/parsers/text-parser.ts +25 -0
  275. package/src/css/parsers/text-shadow-parser.ts +94 -0
  276. package/src/css/properties/box-model.ts +82 -0
  277. package/src/css/properties/flexbox.ts +44 -0
  278. package/src/css/properties/gap.ts +14 -0
  279. package/src/css/properties/grid.ts +94 -0
  280. package/src/css/properties/layout.ts +59 -0
  281. package/src/css/properties/misc.ts +44 -0
  282. package/src/css/properties/typography.ts +71 -0
  283. package/src/css/properties/visual.ts +68 -0
  284. package/src/css/selectors/matcher.ts +219 -0
  285. package/src/css/selectors/parser.ts +163 -0
  286. package/src/css/selectors/simple-key.ts +31 -0
  287. package/src/css/selectors/specificity.ts +41 -0
  288. package/src/css/selectors/types.ts +31 -0
  289. package/src/css/shorthands/border-shorthand.ts +68 -0
  290. package/src/css/shorthands/box-shorthand.ts +33 -0
  291. package/src/css/style-inheritance.ts +50 -0
  292. package/src/css/style.ts +402 -0
  293. package/src/css/ua-defaults/base-defaults.ts +266 -0
  294. package/src/css/ua-defaults/browser-defaults.ts +134 -0
  295. package/src/css/ua-defaults/element-defaults.ts +374 -0
  296. package/src/css/ua-defaults/types.ts +43 -0
  297. package/src/css/unit-conversion.ts +24 -0
  298. package/src/css/utils.ts +108 -0
  299. package/src/css/viewport.ts +17 -0
  300. package/src/debug/audit.ts +20 -0
  301. package/src/debug/ids.ts +13 -0
  302. package/src/debug/log.js +28 -0
  303. package/src/debug/log.ts +52 -0
  304. package/src/debug/tree.ts +57 -0
  305. package/src/dom/node.ts +133 -0
  306. package/src/environment/browser-environment.ts +78 -0
  307. package/src/environment/environment.ts +35 -0
  308. package/src/environment/global.ts +13 -0
  309. package/src/environment/node-environment.browser.ts +28 -0
  310. package/src/environment/node-environment.ts +64 -0
  311. package/src/fonts/detector.ts +28 -0
  312. package/src/fonts/engines/ttf-engine.ts +28 -0
  313. package/src/fonts/engines/woff-engine.ts +38 -0
  314. package/src/fonts/engines/woff2-engine.ts +41 -0
  315. package/src/fonts/extractors/metrics-extractor.ts +362 -0
  316. package/src/fonts/font-registry-resolver.ts +132 -0
  317. package/src/fonts/index.ts +3 -0
  318. package/src/fonts/orchestrator.ts +92 -0
  319. package/src/fonts/parsers/base-parser.ts +23 -0
  320. package/src/fonts/types.ts +85 -0
  321. package/src/fonts/utils/ttf-reconstructor.ts +120 -0
  322. package/src/fonts/woff/decoder.ts +105 -0
  323. package/src/fonts/woff2/buffer.ts +106 -0
  324. package/src/fonts/woff2/decoder.ts +981 -0
  325. package/src/geometry/box.ts +48 -0
  326. package/src/geometry/matrix.ts +59 -0
  327. package/src/html/css/parse-css.ts +85 -0
  328. package/src/html/dom-converter.ts +433 -0
  329. package/src/html/image-converter.ts +200 -0
  330. package/src/html-to-pdf.ts +410 -0
  331. package/src/image/base-decoder.ts +149 -0
  332. package/src/image/image-service.ts +188 -0
  333. package/src/image/jpeg-decoder.ts +73 -0
  334. package/src/image/png-decoder.ts +550 -0
  335. package/src/image/types.ts +20 -0
  336. package/src/image/webp-decoder.ts +242 -0
  337. package/src/image/webp-huffman.ts +218 -0
  338. package/src/image/webp-riff-parser.ts +54 -0
  339. package/src/image/webp-vp8l-decoder.ts +199 -0
  340. package/src/index.ts +35 -0
  341. package/src/layout/context/float-context.ts +62 -0
  342. package/src/layout/context/layout-environment.ts +29 -0
  343. package/src/layout/debug.ts +18 -0
  344. package/src/layout/inline/bounding-box-calculator.ts +132 -0
  345. package/src/layout/inline/font-baseline-calculator.ts +76 -0
  346. package/src/layout/inline/inline-utils.ts +94 -0
  347. package/src/layout/inline/layout.ts +285 -0
  348. package/src/layout/inline/line_breaker.ts +109 -0
  349. package/src/layout/inline/measurement.ts +144 -0
  350. package/src/layout/inline/run-placer.ts +139 -0
  351. package/src/layout/inline/text-alignment.ts +70 -0
  352. package/src/layout/inline/tokenizer.ts +195 -0
  353. package/src/layout/inline/types.ts +76 -0
  354. package/src/layout/pipeline/context-factory.ts +16 -0
  355. package/src/layout/pipeline/default-engine.ts +24 -0
  356. package/src/layout/pipeline/engine.ts +59 -0
  357. package/src/layout/pipeline/layout-tree.ts +13 -0
  358. package/src/layout/pipeline/out-of-flow-manager.ts +73 -0
  359. package/src/layout/pipeline/strategy.ts +12 -0
  360. package/src/layout/pipeline/text-metrics-initializer.ts +13 -0
  361. package/src/layout/strategies/block.ts +236 -0
  362. package/src/layout/strategies/display-none.ts +14 -0
  363. package/src/layout/strategies/fallback.ts +15 -0
  364. package/src/layout/strategies/flex.ts +477 -0
  365. package/src/layout/strategies/fragmentation.ts +17 -0
  366. package/src/layout/strategies/grid.ts +247 -0
  367. package/src/layout/strategies/image.ts +342 -0
  368. package/src/layout/strategies/inline.ts +128 -0
  369. package/src/layout/strategies/table.ts +595 -0
  370. package/src/layout/table/cell_layout.ts +31 -0
  371. package/src/layout/table/diagnostics.ts +19 -0
  372. package/src/layout/text-run.ts +42 -0
  373. package/src/layout/utils/content-measurer.ts +117 -0
  374. package/src/layout/utils/display-utils.ts +24 -0
  375. package/src/layout/utils/floats.ts +98 -0
  376. package/src/layout/utils/gap-calculator.ts +167 -0
  377. package/src/layout/utils/inline-formatter.ts +31 -0
  378. package/src/layout/utils/inline-formatting.ts +9 -0
  379. package/src/layout/utils/margin.ts +140 -0
  380. package/src/layout/utils/node-math.ts +237 -0
  381. package/src/layout/utils/overflow.ts +14 -0
  382. package/src/layout/utils/sizing.ts +12 -0
  383. package/src/layout/utils/text-metrics.ts +361 -0
  384. package/src/logging/debug.ts +58 -0
  385. package/src/pdf/font/base14/widths-courier-bold.ts +159 -0
  386. package/src/pdf/font/base14/widths-courier.ts +159 -0
  387. package/src/pdf/font/base14/widths-helvetica-bold.ts +158 -0
  388. package/src/pdf/font/base14/widths-helvetica.ts +158 -0
  389. package/src/pdf/font/base14/widths-times-bold.ts +158 -0
  390. package/src/pdf/font/base14/widths-times-roman.ts +158 -0
  391. package/src/pdf/font/base14/widths-types.ts +25 -0
  392. package/src/pdf/font/base14-widths.ts +32 -0
  393. package/src/pdf/font/blur.ts +81 -0
  394. package/src/pdf/font/builtin-fonts.browser.ts +262 -0
  395. package/src/pdf/font/builtin-fonts.ts +126 -0
  396. package/src/pdf/font/composite-glyph-parser.ts +242 -0
  397. package/src/pdf/font/embedder.ts +395 -0
  398. package/src/pdf/font/font-config.ts +190 -0
  399. package/src/pdf/font/font-registry.ts +263 -0
  400. package/src/pdf/font/font-subset.ts +258 -0
  401. package/src/pdf/font/glyph-atlas-maxrects.ts +305 -0
  402. package/src/pdf/font/glyph-atlas-tuner.ts +98 -0
  403. package/src/pdf/font/glyph-atlas.ts +226 -0
  404. package/src/pdf/font/glyph-cache.ts +127 -0
  405. package/src/pdf/font/loca-reader.ts +109 -0
  406. package/src/pdf/font/managers/font-resource-manager.ts +73 -0
  407. package/src/pdf/font/managers/subset-resource-manager.ts +164 -0
  408. package/src/pdf/font/rasterizer.ts +270 -0
  409. package/src/pdf/font/resolvers/base-font-mapper.ts +77 -0
  410. package/src/pdf/font/resolvers/family-resolver.ts +33 -0
  411. package/src/pdf/font/resolvers/weight-style-applicator.ts +63 -0
  412. package/src/pdf/font/simple-glyph-parser.ts +289 -0
  413. package/src/pdf/font/to-unicode.ts +109 -0
  414. package/src/pdf/font/transformation-matrix.ts +136 -0
  415. package/src/pdf/font/ttf-cmap.ts +180 -0
  416. package/src/pdf/font/ttf-global-metrics.ts +58 -0
  417. package/src/pdf/font/ttf-glyf.ts +125 -0
  418. package/src/pdf/font/ttf-glyph-metrics.ts +43 -0
  419. package/src/pdf/font/ttf-lite.ts +269 -0
  420. package/src/pdf/font/ttf-table-parser.ts +132 -0
  421. package/src/pdf/font/ttf-table-provider.ts +61 -0
  422. package/src/pdf/font/widths.ts +79 -0
  423. package/src/pdf/font-subset/font-registry.ts +127 -0
  424. package/src/pdf/header-footer-layout.ts +153 -0
  425. package/src/pdf/header-footer-painter.ts +209 -0
  426. package/src/pdf/header-footer-renderer.ts +357 -0
  427. package/src/pdf/header-footer-tokens.ts +55 -0
  428. package/src/pdf/header-footer.ts +25 -0
  429. package/src/pdf/layout-tree-builder.ts +261 -0
  430. package/src/pdf/page-painter.ts +241 -0
  431. package/src/pdf/pagination.ts +155 -0
  432. package/src/pdf/primitives/pdf-builder.ts +378 -0
  433. package/src/pdf/primitives/pdf-bytes.ts +40 -0
  434. package/src/pdf/primitives/pdf-document.ts +108 -0
  435. package/src/pdf/primitives/pdf-reference-manager.ts +47 -0
  436. package/src/pdf/primitives/pdf-resource-registries.ts +255 -0
  437. package/src/pdf/primitives/pdf-serializers.ts +194 -0
  438. package/src/pdf/primitives/pdf-types.ts +73 -0
  439. package/src/pdf/render.ts +210 -0
  440. package/src/pdf/renderer/box-painter.ts +236 -0
  441. package/src/pdf/renderer/page-paint.ts +102 -0
  442. package/src/pdf/renderer/paint-box-shadows.ts +218 -0
  443. package/src/pdf/renderer/radius.ts +58 -0
  444. package/src/pdf/renderers/graphics-state-manager.ts +40 -0
  445. package/src/pdf/renderers/image-renderer.ts +127 -0
  446. package/src/pdf/renderers/radius-utils.ts +80 -0
  447. package/src/pdf/renderers/rectangle-renderer.ts +129 -0
  448. package/src/pdf/renderers/rounded-rect-path.ts +120 -0
  449. package/src/pdf/renderers/shape-renderer.ts +563 -0
  450. package/src/pdf/renderers/shape-utils.ts +194 -0
  451. package/src/pdf/renderers/text-decoration-renderer.ts +313 -0
  452. package/src/pdf/renderers/text-encoder.ts +41 -0
  453. package/src/pdf/renderers/text-font-resolver.ts +75 -0
  454. package/src/pdf/renderers/text-renderer-utils.ts +28 -0
  455. package/src/pdf/renderers/text-renderer.ts +391 -0
  456. package/src/pdf/renderers/text-shadow-renderer.ts +300 -0
  457. package/src/pdf/shading/gradient-service.ts +525 -0
  458. package/src/pdf/shading/index.ts +1 -0
  459. package/src/pdf/stacking/build-stacking-contexts.ts +93 -0
  460. package/src/pdf/stacking/resolve-paint-order.ts +157 -0
  461. package/src/pdf/stacking/types.ts +40 -0
  462. package/src/pdf/svg/aspect-ratio.ts +81 -0
  463. package/src/pdf/svg/coordinate-mapper.ts +81 -0
  464. package/src/pdf/svg/geometry-builder.ts +45 -0
  465. package/src/pdf/svg/render-svg.ts +296 -0
  466. package/src/pdf/svg/shape-renderer.ts +463 -0
  467. package/src/pdf/svg/style-computer.ts +246 -0
  468. package/src/pdf/transform-adapter.ts +26 -0
  469. package/src/pdf/types.ts +377 -0
  470. package/src/pdf/utils/background-layer-resolver.ts +439 -0
  471. package/src/pdf/utils/background-tiles.ts +192 -0
  472. package/src/pdf/utils/border-dashes.ts +109 -0
  473. package/src/pdf/utils/border-radius-utils.ts +86 -0
  474. package/src/pdf/utils/box-dimensions-utils.ts +47 -0
  475. package/src/pdf/utils/clip-path-resolver.ts +50 -0
  476. package/src/pdf/utils/clipping-path-builder.ts +190 -0
  477. package/src/pdf/utils/color-utils.ts +102 -0
  478. package/src/pdf/utils/coordinate-transformer.ts +30 -0
  479. package/src/pdf/utils/drop-shadow-raster.ts +233 -0
  480. package/src/pdf/utils/encoding.ts +98 -0
  481. package/src/pdf/utils/glyph-atlas-registrar.ts +13 -0
  482. package/src/pdf/utils/glyph-run-renderer.ts +129 -0
  483. package/src/pdf/utils/image-command-partitioner.ts +28 -0
  484. package/src/pdf/utils/image-matrix-builder.ts +31 -0
  485. package/src/pdf/utils/image-utils.ts +26 -0
  486. package/src/pdf/utils/list-utils.ts +194 -0
  487. package/src/pdf/utils/node-text-run-factory.ts +202 -0
  488. package/src/pdf/utils/page-resource-registrar.ts +46 -0
  489. package/src/pdf/utils/result-combiner.ts +102 -0
  490. package/src/pdf/utils/shadow-utils.ts +127 -0
  491. package/src/pdf/utils/text-alignment-resolver.ts +76 -0
  492. package/src/pdf/utils/text-decoration-utils.ts +64 -0
  493. package/src/pdf/utils/text-layout-adjuster.ts +185 -0
  494. package/src/pdf/utils/text-utils.ts +193 -0
  495. package/src/pdf/utils/transform-scope-manager.ts +69 -0
  496. package/src/render/offset.ts +170 -0
  497. package/src/shim/empty.ts +2 -0
  498. package/src/shim/fs-empty.ts +5 -0
  499. package/src/shim/url-empty.ts +7 -0
  500. package/src/shim/zlib-empty.ts +9 -0
  501. package/src/style/shorthands/index.ts +19 -0
  502. package/src/style/ua/defaults.ts +69 -0
  503. package/src/svg/index.ts +4 -0
  504. package/src/svg/parser-registry.ts +71 -0
  505. package/src/svg/parser.ts +486 -0
  506. package/src/svg/path-data.ts +515 -0
  507. package/src/svg/types.ts +194 -0
  508. package/src/text/line-breaker.ts +321 -0
  509. package/src/text/text-transform.ts +43 -0
  510. package/src/text/text.ts +33 -0
  511. package/src/transform/css-parser.ts +95 -0
  512. package/src/types/fonts.ts +62 -0
  513. package/src/types/public.ts +19 -0
  514. package/src/units/page-utils.ts +58 -0
  515. package/src/units/units.ts +50 -0
  516. package/src/utils/base64.ts +24 -0
  517. package/test-output.txt +79 -0
  518. package/tests/css/background-parser.spec.ts +14 -0
  519. package/tests/css/clip-path-parser.spec.ts +66 -0
  520. package/tests/environment/path-resolution.spec.ts +104 -0
  521. package/tests/helpers/ai-layout-diagnostics.ts +141 -0
  522. package/tests/helpers/render-utils.ts +52 -0
  523. package/tests/helpers/text-geometry.ts +56 -0
  524. package/tests/layout/custom-properties.test.ts +38 -0
  525. package/tests/layout/gap-calculator.spec.ts +196 -0
  526. package/tests/layout/inline-background-alignment.spec.ts +93 -0
  527. package/tests/layout/inline-fragments.spec.ts +26 -0
  528. package/tests/layout/run-placer-baseline.spec.ts +108 -0
  529. package/tests/pdf/alignments.spec.ts +26 -0
  530. package/tests/pdf/background-clip.spec.ts +57 -0
  531. package/tests/pdf/background-repeat-space-round.spec.ts +35 -0
  532. package/tests/pdf/background-repeat.spec.ts +137 -0
  533. package/tests/pdf/border-radius.spec.ts +151 -0
  534. package/tests/pdf/clip-path.spec.ts +92 -0
  535. package/tests/pdf/radial-gradient.spec.ts +50 -0
  536. package/tests/pdf/svg-stroke-dash.spec.ts +81 -0
  537. package/tests/pdf/text-transform-matrix.spec.ts +43 -0
  538. package/tsconfig.json +17 -0
  539. package/types/fonts.js +10 -0
  540. package/vitest.config.ts +9 -0
@@ -0,0 +1,157 @@
1
+ import type { RenderBox } from "../types.js";
2
+ import { buildStackingContexts, getStackingFlags } from "./build-stacking-contexts.js";
3
+ import type { PaintStep, StackingContextId, StackingContextNode } from "./types.js";
4
+
5
+ /**
6
+ * Resolve a global paint order from the RenderBox tree using stacking contexts.
7
+ *
8
+ * Scope (step 1):
9
+ * - Support numeric z-index on positioned elements (absolute/fixed/etc.) vs auto.
10
+ * - Respect DOM order for ties.
11
+ * - Treat each box as an atomic paint unit (background+border+content together).
12
+ * - Keep API simple: return ordered RenderBox list.
13
+ */
14
+ export function resolvePaintOrder(root: RenderBox): RenderBox[] {
15
+ const { rootContextId, contexts } = buildStackingContexts(root);
16
+ const steps: PaintStep[] = [];
17
+
18
+ resolveContextPaintOrder(rootContextId, contexts, steps);
19
+ return steps.map((s) => s.box);
20
+ }
21
+
22
+ /**
23
+ * Compute paint order within a stacking context and append PaintSteps.
24
+ *
25
+ * Simplified rules (sufficient for our current constraints):
26
+ * For a given context:
27
+ * 1. Paint the context root box itself.
28
+ * 2. Paint descendants with negative z-index (positioned).
29
+ * 3. Paint normal-flow descendants (z-index: auto).
30
+ * 4. Paint positioned descendants with z-index >= 0 in ascending z-index.
31
+ *
32
+ * DOM order is preserved within each group as tie-breaker.
33
+ * Nested stacking contexts are treated as atomic units via recursion.
34
+ */
35
+ function resolveContextPaintOrder(
36
+ contextId: StackingContextId,
37
+ contexts: Map<StackingContextId, StackingContextNode>,
38
+ out: PaintStep[],
39
+ ): void {
40
+ const context = contexts.get(contextId);
41
+ if (!context) return;
42
+
43
+ const rootBox = context.box;
44
+
45
+ // 1. Paint the context root box as an atomic unit.
46
+ out.push({ box: rootBox });
47
+
48
+ // Collect direct descendants belonging to this context (non-root boxes).
49
+ const descendants: RenderBox[] = [];
50
+ collectDescendantsInContext(rootBox, contextId, contexts, descendants);
51
+
52
+ const negativeZ: RenderBox[] = [];
53
+ const normalFlowAutoZ: RenderBox[] = [];
54
+ const positionedNonNegative: RenderBox[] = [];
55
+
56
+ for (const box of descendants) {
57
+ const flags = getStackingFlags(box);
58
+
59
+ if (flags.isPositioned && flags.zIndex !== "auto") {
60
+ const z = flags.zIndex as number;
61
+ if (z < 0) {
62
+ negativeZ.push(box);
63
+ } else {
64
+ positionedNonNegative.push(box);
65
+ }
66
+ } else {
67
+ // Non-positioned or auto z-index participate in the auto flow group.
68
+ normalFlowAutoZ.push(box);
69
+ }
70
+ }
71
+
72
+ // 2. Negative z-index positioned descendants (ascending z, DOM order for ties).
73
+ negativeZ.sort((a, b) => {
74
+ const az = (getStackingFlags(a).zIndex as number) ?? 0;
75
+ const bz = (getStackingFlags(b).zIndex as number) ?? 0;
76
+ if (az !== bz) return az - bz;
77
+ return 0; // DOM order already in descendants list.
78
+ });
79
+ for (const box of negativeZ) {
80
+ appendBoxOrContext(box, contextId, contexts, out);
81
+ }
82
+
83
+ // 3. Normal flow / auto z-index descendants (DOM order).
84
+ for (const box of normalFlowAutoZ) {
85
+ appendBoxOrContext(box, contextId, contexts, out);
86
+ }
87
+
88
+ // 4. Positioned non-negative z-index descendants (ascending z, DOM order ties).
89
+ positionedNonNegative.sort((a, b) => {
90
+ const az = (getStackingFlags(a).zIndex as number) ?? 0;
91
+ const bz = (getStackingFlags(b).zIndex as number) ?? 0;
92
+ if (az !== bz) return az - bz;
93
+ return 0;
94
+ });
95
+ for (const box of positionedNonNegative) {
96
+ appendBoxOrContext(box, contextId, contexts, out);
97
+ }
98
+ }
99
+
100
+ /**
101
+ * Append either a nested stacking context or a regular box.
102
+ * Nested contexts are treated as atomic units: we delegate to resolveContextPaintOrder.
103
+ */
104
+ function appendBoxOrContext(
105
+ box: RenderBox,
106
+ parentContextId: StackingContextId,
107
+ contexts: Map<StackingContextId, StackingContextNode>,
108
+ out: PaintStep[],
109
+ ): void {
110
+ const nested = findContextByBox(box, parentContextId, contexts);
111
+ if (nested) {
112
+ resolveContextPaintOrder(nested.id, contexts, out);
113
+ } else {
114
+ out.push({ box });
115
+ }
116
+ }
117
+
118
+ /**
119
+ * Collect descendants that belong to the given context (i.e. do not start
120
+ * their own stacking context), in DOM order.
121
+ */
122
+ function collectDescendantsInContext(
123
+ box: RenderBox,
124
+ contextId: StackingContextId,
125
+ contexts: Map<StackingContextId, StackingContextNode>,
126
+ out: RenderBox[],
127
+ ): void {
128
+ for (const child of box.children) {
129
+ const childCtx = findContextByBox(child, null, contexts);
130
+ if (childCtx && childCtx.id !== contextId) {
131
+ // Child begins its own stacking context; treat that context atomically later.
132
+ // Do not inline its descendants here.
133
+ out.push(child);
134
+ continue;
135
+ }
136
+
137
+ out.push(child);
138
+ collectDescendantsInContext(child, contextId, contexts, out);
139
+ }
140
+ }
141
+
142
+ /**
143
+ * Find a stacking context node anchored at the given box.
144
+ * If parentContextId is provided, only match contexts whose parentId equals it.
145
+ */
146
+ function findContextByBox(
147
+ box: RenderBox,
148
+ parentContextId: StackingContextId | null,
149
+ contexts: Map<StackingContextId, StackingContextNode>,
150
+ ): StackingContextNode | undefined {
151
+ for (const ctx of contexts.values()) {
152
+ if (ctx.box === box && (parentContextId === null || ctx.parentId === parentContextId)) {
153
+ return ctx;
154
+ }
155
+ }
156
+ return undefined;
157
+ }
@@ -0,0 +1,40 @@
1
+ import type { RenderBox } from "../types.js";
2
+
3
+ export type StackingContextId = string;
4
+
5
+ /**
6
+ * Represents a stacking context anchored at a particular RenderBox.
7
+ * This structure is intentionally minimal and purely declarative:
8
+ * it does not encode paint-order algorithms.
9
+ */
10
+ export interface StackingContextNode {
11
+ /** Unique id for this stacking context. */
12
+ id: StackingContextId;
13
+ /** The box that establishes this stacking context. */
14
+ box: RenderBox;
15
+ /** Parent stacking context id (null for root). */
16
+ parentId: StackingContextId | null;
17
+ /** Child stacking contexts nested inside this one. */
18
+ childContextIds: StackingContextId[];
19
+ }
20
+
21
+ /**
22
+ * Lightweight view of stacking-relevant flags for a RenderBox.
23
+ * This mirrors CSS concepts but stays implementation-agnostic.
24
+ */
25
+ export interface StackingFlags {
26
+ /** Whether the element is positioned (absolute, fixed, sticky, etc.). */
27
+ isPositioned: boolean;
28
+ /** Numeric z-index value or "auto" when not explicitly set. */
29
+ zIndex: number | "auto";
30
+ /** Whether this element establishes a new stacking context. */
31
+ establishesContext: boolean;
32
+ }
33
+
34
+ /**
35
+ * Describes a resolved paint step for a single box.
36
+ * Higher-level algorithms will generate ordered sequences of these entries.
37
+ */
38
+ export interface PaintStep {
39
+ box: RenderBox;
40
+ }
@@ -0,0 +1,81 @@
1
+ export type AspectAlign =
2
+ | "none"
3
+ | "xMinYMin"
4
+ | "xMidYMin"
5
+ | "xMaxYMin"
6
+ | "xMinYMid"
7
+ | "xMidYMid"
8
+ | "xMaxYMid"
9
+ | "xMinYMax"
10
+ | "xMidYMax"
11
+ | "xMaxYMax";
12
+
13
+ export interface PreserveAspectRatioConfig {
14
+ align: AspectAlign;
15
+ meetOrSlice: "meet" | "slice";
16
+ }
17
+
18
+ export function parsePreserveAspectRatio(raw: string | undefined): PreserveAspectRatioConfig {
19
+ const defaultValue: PreserveAspectRatioConfig = { align: "xMidYMid", meetOrSlice: "meet" };
20
+ if (!raw) {
21
+ return defaultValue;
22
+ }
23
+ const tokens = raw.trim().split(/[\s,]+/).filter(Boolean);
24
+ if (tokens.length === 0) {
25
+ return defaultValue;
26
+ }
27
+
28
+ const validAlignments: Record<string, AspectAlign> = {
29
+ none: "none",
30
+ xminymin: "xMinYMin",
31
+ xmidymin: "xMidYMin",
32
+ xmaxymin: "xMaxYMin",
33
+ xminymid: "xMinYMid",
34
+ xmidymid: "xMidYMid",
35
+ xmaxymid: "xMaxYMid",
36
+ xminymax: "xMinYMax",
37
+ xmidymax: "xMidYMax",
38
+ xmaxymax: "xMaxYMax",
39
+ };
40
+
41
+ let index = 0;
42
+ let alignToken = tokens[index]?.toLowerCase() ?? "";
43
+ if (alignToken === "defer") {
44
+ index += 1;
45
+ alignToken = tokens[index]?.toLowerCase() ?? "";
46
+ }
47
+ index += 1;
48
+
49
+ let align = validAlignments[alignToken] ?? defaultValue.align;
50
+ if (align === "none") {
51
+ return { align: "none", meetOrSlice: "meet" };
52
+ }
53
+
54
+ let meetOrSlice: "meet" | "slice" = "meet";
55
+ for (; index < tokens.length; index += 1) {
56
+ const token = tokens[index]?.toLowerCase();
57
+ if (token === "meet") {
58
+ meetOrSlice = "meet";
59
+ break;
60
+ }
61
+ if (token === "slice") {
62
+ meetOrSlice = "slice";
63
+ break;
64
+ }
65
+ }
66
+
67
+ if (!validAlignments[alignToken]) {
68
+ align = defaultValue.align;
69
+ }
70
+
71
+ return { align, meetOrSlice };
72
+ }
73
+
74
+ export function getAlignFactors(align: AspectAlign): { x: number; y: number } {
75
+ if (align === "none") {
76
+ return { x: 0, y: 0 };
77
+ }
78
+ const horizontal = align.includes("xMid") ? 0.5 : align.includes("xMax") ? 1 : 0;
79
+ const vertical = align.includes("YMid") ? 0.5 : align.includes("YMax") ? 1 : 0;
80
+ return { x: horizontal, y: vertical };
81
+ }
@@ -0,0 +1,81 @@
1
+ import type { SvgPoint } from "../../svg/types.js";
2
+ import type { NormalizedPathCommand } from "../../svg/path-data.js";
3
+ import type { PathCommand } from "../renderers/shape-renderer.js";
4
+ import type { SvgRenderContext } from "./render-svg.js";
5
+ import { applyMatrixToPoint } from "../../geometry/matrix.js";
6
+
7
+ export function mapPoints(points: readonly SvgPoint[], context: SvgRenderContext): { x: number; y: number }[] {
8
+ const result: { x: number; y: number }[] = [];
9
+ for (const point of points) {
10
+ const mapped = mapSvgPoint(point.x, point.y, context);
11
+ if (!mapped) {
12
+ return [];
13
+ }
14
+ result.push(mapped);
15
+ }
16
+ return result;
17
+ }
18
+
19
+ export function mapPathSegments(segments: readonly NormalizedPathCommand[], context: SvgRenderContext): PathCommand[] | null {
20
+ const commands: PathCommand[] = [];
21
+ for (const segment of segments) {
22
+ switch (segment.type) {
23
+ case "M": {
24
+ const point = mapSvgPoint(segment.x, segment.y, context);
25
+ if (!point) {
26
+ return null;
27
+ }
28
+ commands.push({ type: "moveTo", x: point.x, y: point.y });
29
+ break;
30
+ }
31
+ case "L": {
32
+ const point = mapSvgPoint(segment.x, segment.y, context);
33
+ if (!point) {
34
+ return null;
35
+ }
36
+ commands.push({ type: "lineTo", x: point.x, y: point.y });
37
+ break;
38
+ }
39
+ case "C": {
40
+ const cp1 = mapSvgPoint(segment.x1, segment.y1, context);
41
+ const cp2 = mapSvgPoint(segment.x2, segment.y2, context);
42
+ const end = mapSvgPoint(segment.x, segment.y, context);
43
+ if (!cp1 || !cp2 || !end) {
44
+ return null;
45
+ }
46
+ commands.push({
47
+ type: "curveTo",
48
+ x1: cp1.x,
49
+ y1: cp1.y,
50
+ x2: cp2.x,
51
+ y2: cp2.y,
52
+ x: end.x,
53
+ y: end.y,
54
+ });
55
+ break;
56
+ }
57
+ case "Z":
58
+ commands.push({ type: "closePath" });
59
+ break;
60
+ default:
61
+ return null;
62
+ }
63
+ }
64
+ return commands;
65
+ }
66
+
67
+ export function mapSvgPoint(x: number, y: number, context: SvgRenderContext): { x: number; y: number } | null {
68
+ if (!Number.isFinite(x) || !Number.isFinite(y)) {
69
+ return null;
70
+ }
71
+ const mapped = mapPointToViewport(context, x, y);
72
+ if (!Number.isFinite(mapped.x) || !Number.isFinite(mapped.y)) {
73
+ return null;
74
+ }
75
+ return mapped;
76
+ }
77
+
78
+ function mapPointToViewport(context: SvgRenderContext, x: number, y: number): { x: number; y: number } {
79
+ const local = applyMatrixToPoint(context.transform, x, y);
80
+ return applyMatrixToPoint(context.viewportMatrix, local.x, local.y);
81
+ }
@@ -0,0 +1,45 @@
1
+ import type { NormalizedPathCommand } from "../../svg/path-data.js";
2
+
3
+ const CIRCLE_KAPPA = 0.5522847498307936;
4
+
5
+ export function buildRectSegments(x: number, y: number, width: number, height: number): NormalizedPathCommand[] {
6
+ return [
7
+ { type: "M", x, y },
8
+ { type: "L", x: x + width, y },
9
+ { type: "L", x: x + width, y: y + height },
10
+ { type: "L", x, y: y + height },
11
+ { type: "Z" },
12
+ ];
13
+ }
14
+
15
+ export function buildRoundedRectSegments(x: number, y: number, width: number, height: number, rx: number, ry: number): NormalizedPathCommand[] {
16
+ const right = x + width;
17
+ const bottom = y + height;
18
+ const kx = rx * CIRCLE_KAPPA;
19
+ const ky = ry * CIRCLE_KAPPA;
20
+ return [
21
+ { type: "M", x: x + rx, y },
22
+ { type: "L", x: right - rx, y },
23
+ { type: "C", x1: right - rx + kx, y1: y, x2: right, y2: y + ry - ky, x: right, y: y + ry },
24
+ { type: "L", x: right, y: bottom - ry },
25
+ { type: "C", x1: right, y1: bottom - ry + ky, x2: right - rx + kx, y2: bottom, x: right - rx, y: bottom },
26
+ { type: "L", x: x + rx, y: bottom },
27
+ { type: "C", x1: x + rx - kx, y1: bottom, x2: x, y2: bottom - ry + ky, x: x, y: bottom - ry },
28
+ { type: "L", x, y: y + ry },
29
+ { type: "C", x1: x, y1: y + ry - ky, x2: x + rx - kx, y2: y, x: x + rx, y },
30
+ { type: "Z" },
31
+ ];
32
+ }
33
+
34
+ export function buildEllipseSegments(cx: number, cy: number, rx: number, ry: number): NormalizedPathCommand[] {
35
+ const mx = rx * CIRCLE_KAPPA;
36
+ const my = ry * CIRCLE_KAPPA;
37
+ return [
38
+ { type: "M", x: cx, y: cy - ry },
39
+ { type: "C", x1: cx + mx, y1: cy - ry, x2: cx + rx, y2: cy - my, x: cx + rx, y: cy },
40
+ { type: "C", x1: cx + rx, y1: cy + my, x2: cx + mx, y2: cy + ry, x: cx, y: cy + ry },
41
+ { type: "C", x1: cx - mx, y1: cy + ry, x2: cx - rx, y2: cy + my, x: cx - rx, y: cy },
42
+ { type: "C", x1: cx - rx, y1: cy - my, x2: cx - mx, y2: cy - ry, x: cx, y: cy - ry },
43
+ { type: "Z" },
44
+ ];
45
+ }
@@ -0,0 +1,296 @@
1
+ import type { PagePainter } from "../page-painter.js";
2
+ import type { RenderBox } from "../types.js";
3
+ import type { SvgNode, SvgRootNode, SvgImageNode } from "../../svg/types.js";
4
+ import {
5
+ renderCircle,
6
+ renderEllipse,
7
+ renderLine,
8
+ renderPath,
9
+ renderPolygon,
10
+ renderPolyline,
11
+ renderRect,
12
+ renderText,
13
+ } from "./shape-renderer.js";
14
+ import { createDefaultStyle, deriveStyle, type SvgStyle } from "./style-computer.js";
15
+ import { computeStrokeScale, identityMatrix, multiplyMatrices, type Matrix } from "../../geometry/matrix.js";
16
+ import { parseTransform } from "../../transform/css-parser.js";
17
+ import { mapSvgPoint } from "./coordinate-mapper.js";
18
+ import { ImageService } from "../../image/image-service.js";
19
+ import { getAlignFactors, parsePreserveAspectRatio } from "./aspect-ratio.js";
20
+ import { decodeBase64ToUint8Array } from "../../utils/base64.js";
21
+ import type { Environment } from "../../environment/environment.js";
22
+
23
+ interface SvgCustomData {
24
+ root: SvgRootNode;
25
+ intrinsicWidth: number;
26
+ intrinsicHeight: number;
27
+ resourceBaseDir?: string;
28
+ assetRootDir?: string;
29
+ }
30
+
31
+ export interface SvgRenderContext {
32
+ painter: PagePainter;
33
+ viewportMatrix: Matrix;
34
+ transform: Matrix;
35
+ strokeScale: number;
36
+ // optional resource roots propagated from HTML conversion
37
+ resourceBaseDir?: string;
38
+ assetRootDir?: string;
39
+ environment?: Environment;
40
+ }
41
+
42
+ // Map of defs by id (gradients, clipPaths, etc.) built once per svg render
43
+ export type SvgDefsMap = Map<string, any>;
44
+
45
+ export async function renderSvgBox(painter: PagePainter, box: RenderBox, environment?: Environment): Promise<void> {
46
+ const svgData = extractSvgCustomData(box);
47
+ if (!svgData) {
48
+ return;
49
+ }
50
+
51
+ const { root } = svgData;
52
+ const content = box.contentBox;
53
+ const widthPx = Math.max(content.width, 0);
54
+ const heightPx = Math.max(content.height, 0);
55
+ if (widthPx <= 0 || heightPx <= 0) {
56
+ return;
57
+ }
58
+
59
+ const sourceWidth = resolvePositive(root.viewBox?.width ?? svgData.intrinsicWidth ?? widthPx);
60
+ const sourceHeight = resolvePositive(root.viewBox?.height ?? svgData.intrinsicHeight ?? heightPx);
61
+ const minX = root.viewBox?.minX ?? 0;
62
+ const minY = root.viewBox?.minY ?? 0;
63
+
64
+ const preserveAttr = root.attributes?.preserveAspectRatio ?? root.attributes?.preserveaspectratio;
65
+ const preserve = parsePreserveAspectRatio(typeof preserveAttr === "string" ? preserveAttr : undefined);
66
+
67
+ const baseScaleX = safeScale(widthPx / sourceWidth);
68
+ const baseScaleY = safeScale(heightPx / sourceHeight);
69
+
70
+ let scaleX = baseScaleX;
71
+ let scaleY = baseScaleY;
72
+ let offsetX = 0;
73
+ let offsetY = 0;
74
+
75
+ if (preserve.align !== "none") {
76
+ const uniformScale = preserve.meetOrSlice === "slice" ? Math.max(baseScaleX, baseScaleY) : Math.min(baseScaleX, baseScaleY);
77
+ scaleX = uniformScale;
78
+ scaleY = uniformScale;
79
+ const scaledWidth = sourceWidth * scaleX;
80
+ const scaledHeight = sourceHeight * scaleY;
81
+ const extraWidth = widthPx - scaledWidth;
82
+ const extraHeight = heightPx - scaledHeight;
83
+ const factors = getAlignFactors(preserve.align);
84
+ offsetX = extraWidth * factors.x;
85
+ offsetY = extraHeight * factors.y;
86
+ }
87
+
88
+ const viewportMatrix: Matrix = {
89
+ a: scaleX,
90
+ b: 0,
91
+ c: 0,
92
+ d: scaleY,
93
+ e: content.x + offsetX - minX * scaleX,
94
+ f: content.y + offsetY - minY * scaleY,
95
+ };
96
+
97
+ const initialTransform = identityMatrix();
98
+ const strokeScale = computeStrokeScale(viewportMatrix, initialTransform);
99
+
100
+ const context: SvgRenderContext = {
101
+ painter,
102
+ viewportMatrix,
103
+ transform: initialTransform,
104
+ strokeScale,
105
+ environment,
106
+ // resource roots will be set below from the box customData if available
107
+ };
108
+
109
+ // Build defs map (id -> node) so paint servers like gradients can be resolved during rendering
110
+ const defs = new Map<string, any>();
111
+ collectDefs(root, defs);
112
+ // Attach to context for downstream use
113
+ (context as any).defs = defs;
114
+
115
+ // If convertDomNode attached resource roots into the customData for this SVG, copy them to context
116
+ if ((svgData as any).resourceBaseDir) {
117
+ context.resourceBaseDir = (svgData as any).resourceBaseDir as string;
118
+ }
119
+ if ((svgData as any).assetRootDir) {
120
+ context.assetRootDir = (svgData as any).assetRootDir as string;
121
+ }
122
+
123
+ const baseStyle = createDefaultStyle();
124
+ await renderNode(root, baseStyle, context);
125
+ }
126
+
127
+ function collectDefs(node: SvgNode, map: Map<string, any>): void {
128
+ if (!node) return;
129
+ // If node has an id, register it
130
+ const id = (node as any).id;
131
+ if (id && typeof id === "string") {
132
+ map.set(id, node);
133
+ }
134
+ // Recurse into children for container nodes
135
+ if ((node as any).children && Array.isArray((node as any).children)) {
136
+ for (const child of (node as any).children) {
137
+ collectDefs(child, map);
138
+ }
139
+ }
140
+ }
141
+
142
+ function extractSvgCustomData(box: RenderBox): SvgCustomData | null {
143
+ const raw = box.customData?.svg;
144
+ if (!raw || typeof raw !== "object") {
145
+ return null;
146
+ }
147
+ const candidate = raw as Partial<SvgCustomData>;
148
+ if (!candidate.root) {
149
+ return null;
150
+ }
151
+ return {
152
+ root: candidate.root,
153
+ intrinsicWidth: resolvePositive(candidate.intrinsicWidth ?? 0),
154
+ intrinsicHeight: resolvePositive(candidate.intrinsicHeight ?? 0),
155
+ resourceBaseDir: candidate.resourceBaseDir,
156
+ assetRootDir: candidate.assetRootDir,
157
+ };
158
+ }
159
+
160
+ async function renderNode(node: SvgNode, style: SvgStyle, context: SvgRenderContext): Promise<void> {
161
+ let workingContext = context;
162
+ const nodeTransform = parseTransform(node.transform);
163
+ if (nodeTransform) {
164
+ const combined = multiplyMatrices(context.transform, nodeTransform);
165
+ workingContext = {
166
+ painter: context.painter,
167
+ viewportMatrix: context.viewportMatrix,
168
+ transform: combined,
169
+ strokeScale: computeStrokeScale(context.viewportMatrix, combined),
170
+ };
171
+ }
172
+
173
+ switch (node.type) {
174
+ case "svg":
175
+ case "g": {
176
+ const nextStyle = deriveStyle(style, node);
177
+ for (const child of node.children) {
178
+ await renderNode(child, nextStyle, workingContext);
179
+ }
180
+ return;
181
+ }
182
+ case "rect":
183
+ return renderRect(node, deriveStyle(style, node), workingContext);
184
+ case "circle":
185
+ return renderCircle(node, deriveStyle(style, node), workingContext);
186
+ case "ellipse":
187
+ return renderEllipse(node, deriveStyle(style, node), workingContext);
188
+ case "polygon":
189
+ return renderPolygon(node, deriveStyle(style, node), workingContext);
190
+ case "polyline":
191
+ return renderPolyline(node, deriveStyle(style, node), workingContext);
192
+ case "line":
193
+ return renderLine(node, deriveStyle(style, node), workingContext);
194
+ case "text":
195
+ return renderText(node, deriveStyle(style, node), workingContext);
196
+ case "image":
197
+ // Render SVG <image> elements by loading the referenced image and drawing it
198
+ return await renderImage(node as SvgImageNode, deriveStyle(style, node), workingContext);
199
+ case "path":
200
+ return renderPath(node, deriveStyle(style, node), workingContext);
201
+ default:
202
+ return;
203
+ }
204
+ }
205
+
206
+ function safeScale(value: number): number {
207
+ if (!Number.isFinite(value) || value === 0) {
208
+ return 1;
209
+ }
210
+ return value;
211
+ }
212
+
213
+ function resolvePositive(value: number): number {
214
+ if (!Number.isFinite(value) || value <= 0) {
215
+ return 1;
216
+ }
217
+ return value;
218
+ }
219
+
220
+ async function renderImage(node: SvgImageNode, _style: SvgStyle, context: SvgRenderContext): Promise<void> {
221
+ const hrefAttr = node.href ?? node.attributes?.href ?? node.attributes?.["xlink:href"];
222
+ if (!hrefAttr || typeof hrefAttr !== "string") {
223
+ return;
224
+ }
225
+
226
+ let imageInfo;
227
+ const imageService = ImageService.getInstance(context.environment);
228
+ try {
229
+ if (hrefAttr.startsWith("data:")) {
230
+ // data URI
231
+ const comma = hrefAttr.indexOf(",");
232
+ if (comma < 0) return;
233
+ const meta = hrefAttr.substring(5, comma);
234
+ const isBase64 = meta.endsWith(";base64");
235
+ const payload = hrefAttr.substring(comma + 1);
236
+ const bytes = isBase64 ? decodeBase64ToUint8Array(payload) : new TextEncoder().encode(decodeURIComponent(payload));
237
+ const copy = bytes.slice();
238
+ imageInfo = await imageService.decodeImage(copy.buffer);
239
+ } else if (/^https?:\/\//i.test(hrefAttr)) {
240
+ // Remote images not supported in this offline renderer
241
+ console.debug("Skipping remote image in SVG:", hrefAttr);
242
+ return;
243
+ } else {
244
+ // Local file reference. Resolve using assetRootDir for absolute (/images/...) or resourceBaseDir for relative
245
+ const hasProcess = typeof process !== "undefined" && !!process.versions?.node;
246
+ if (!hasProcess) {
247
+ console.debug("Skipping local SVG image in non-Node environment:", hrefAttr);
248
+ return;
249
+ }
250
+ const resolver = context.environment?.resolveLocal;
251
+ if (!resolver) {
252
+ console.debug("Skipping local SVG image (no resolver in environment):", hrefAttr);
253
+ return;
254
+ }
255
+ // Choose base: assetRootDir for paths starting with /, resourceBaseDir for relative paths
256
+ const base = hrefAttr.startsWith('/')
257
+ ? (context.assetRootDir ?? context.resourceBaseDir)
258
+ : (context.resourceBaseDir ?? context.assetRootDir);
259
+ const resolved = resolver(hrefAttr, base);
260
+ imageInfo = await imageService.loadImage(resolved);
261
+ (node as any)._resolvedHref = resolved;
262
+ }
263
+ } catch (err) {
264
+ console.debug("Failed to load SVG image", hrefAttr, err instanceof Error ? err.message : err);
265
+ return;
266
+ }
267
+
268
+ if (!imageInfo) return;
269
+
270
+ const drawWidth = Number.isFinite(node.width as number) ? (node.width as number) : imageInfo.width;
271
+ const drawHeight = Number.isFinite(node.height as number) ? (node.height as number) : imageInfo.height;
272
+
273
+ const p1 = mapSvgPoint(Number(node.x ?? 0), Number(node.y ?? 0), context);
274
+ const p2 = mapSvgPoint(Number(node.x ?? 0) + drawWidth, Number(node.y ?? 0) + drawHeight, context);
275
+ if (!p1 || !p2) {
276
+ return;
277
+ }
278
+
279
+ const rect = { x: p1.x, y: p1.y, width: p2.x - p1.x, height: p2.y - p1.y };
280
+
281
+ const imageRef = {
282
+ src: (node as any)._resolvedHref ?? hrefAttr,
283
+ width: imageInfo.width,
284
+ height: imageInfo.height,
285
+ format: imageInfo.format,
286
+ channels: imageInfo.channels,
287
+ bitsPerComponent: imageInfo.bitsPerChannel,
288
+ data: imageInfo.data,
289
+ } as const;
290
+
291
+ try {
292
+ context.painter.drawImage(imageRef as any, rect);
293
+ } catch (err) {
294
+ console.debug("Failed to draw image in SVG", hrefAttr, err instanceof Error ? err.message : err);
295
+ }
296
+ }