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,194 @@
1
+ import { estimateLineWidth } from "../../layout/utils/text-metrics.js";
2
+ import type { LayoutNode } from "../../dom/node.js";
3
+ import type { RenderBox, Run, RGBA, Rect } from "../types.js";
4
+
5
+ export function createListMarkerRun(
6
+ node: LayoutNode,
7
+ contentBox: Rect,
8
+ children: RenderBox[],
9
+ fallbackColor: RGBA,
10
+ ): Run | undefined {
11
+ const styleType = resolveListStyleType(node);
12
+ if (!styleType || styleType === "none") {
13
+ return undefined;
14
+ }
15
+ const markerIndex = computeListItemIndex(node);
16
+ const markerText = formatListMarker(styleType, markerIndex);
17
+ if (!markerText) {
18
+ return undefined;
19
+ }
20
+
21
+ const firstRun = findFirstDescendantTextRun(children);
22
+ const fontFamily = node.style.fontFamily ?? "sans-serif";
23
+ const fontSize = node.style.fontSize;
24
+ const fontWeight = node.style.fontWeight;
25
+ const color = fallbackColor ?? { r: 0, g: 0, b: 0, a: 1 };
26
+
27
+ const baseline =
28
+ firstRun?.lineMatrix.f ??
29
+ (node.box.baseline > 0 ? node.box.baseline : contentBox.y + fontSize);
30
+ const textStartX = firstRun?.lineMatrix.e ?? contentBox.x;
31
+ const markerWidth = Math.max(estimateLineWidth(markerText, node.style), 0);
32
+ const gap = Math.max(fontSize * 0.5, 6);
33
+ const markerX = textStartX - gap - markerWidth;
34
+
35
+ return {
36
+ text: markerText,
37
+ fontFamily,
38
+ fontSize,
39
+ fontWeight,
40
+ fill: color,
41
+ lineMatrix: { a: 1, b: 0, c: 0, d: 1, e: markerX, f: baseline },
42
+ advanceWidth: markerWidth + gap,
43
+ };
44
+ }
45
+
46
+ export function findFirstDescendantTextRun(children: RenderBox[]): Run | undefined {
47
+ for (const child of children) {
48
+ if (child.textRuns.length > 0) {
49
+ return child.textRuns[0];
50
+ }
51
+ const nested = findFirstDescendantTextRun(child.children);
52
+ if (nested) {
53
+ return nested;
54
+ }
55
+ }
56
+ return undefined;
57
+ }
58
+
59
+ export function computeListItemIndex(node: LayoutNode): number {
60
+ const parent = node.parent;
61
+ if (!parent) {
62
+ return 1;
63
+ }
64
+ let index = 0;
65
+ for (const sibling of parent.children) {
66
+ if (sibling.tagName === "li") {
67
+ index += 1;
68
+ }
69
+ if (sibling === node) {
70
+ break;
71
+ }
72
+ }
73
+ return Math.max(index, 1);
74
+ }
75
+
76
+ export const LIST_STYLE_MARKERS: Record<"disc" | "circle" | "square", string> = {
77
+ disc: "\u2022", // • BULLET
78
+ circle: "\u25E6", // ◦ WHITE BULLET
79
+ square: "\u25AA", // ▪ BLACK SMALL SQUARE
80
+ };
81
+
82
+ const DEFAULT_MARKER = LIST_STYLE_MARKERS.disc;
83
+
84
+ export function formatListMarker(styleType: string, index: number): string | undefined {
85
+ const normalized = styleType.trim().toLowerCase();
86
+ switch (normalized) {
87
+ case "none":
88
+ return undefined;
89
+ case "decimal":
90
+ return `${index}.`;
91
+ case "decimal-leading-zero":
92
+ return `${String(index).padStart(2, "0")}.`;
93
+ case "lower-alpha":
94
+ return `${toAlphaSequence(index).toLowerCase()}.`;
95
+ case "upper-alpha":
96
+ return `${toAlphaSequence(index).toUpperCase()}.`;
97
+ case "lower-roman": {
98
+ const roman = toRomanNumeral(index);
99
+ return roman ? `${roman.toLowerCase()}.` : `${index}.`;
100
+ }
101
+ case "upper-roman": {
102
+ const roman = toRomanNumeral(index);
103
+ return roman ? `${roman.toUpperCase()}.` : `${index}.`;
104
+ }
105
+ case "disc":
106
+ case "circle":
107
+ case "square":
108
+ return LIST_STYLE_MARKERS[normalized];
109
+ default:
110
+ return DEFAULT_MARKER;
111
+ }
112
+ }
113
+
114
+ function toAlphaSequence(index: number): string {
115
+ let n = Math.max(1, Math.floor(index));
116
+ let result = "";
117
+ while (n > 0) {
118
+ n -= 1;
119
+ const charCode = 65 + (n % 26);
120
+ result = String.fromCharCode(charCode) + result;
121
+ n = Math.floor(n / 26);
122
+ }
123
+ return result;
124
+ }
125
+
126
+ function toRomanNumeral(index: number): string | undefined {
127
+ if (!Number.isFinite(index) || index <= 0 || index >= 4000) {
128
+ return undefined;
129
+ }
130
+ const romanPairs: Array<[number, string]> = [
131
+ [1000, "M"],
132
+ [900, "CM"],
133
+ [500, "D"],
134
+ [400, "CD"],
135
+ [100, "C"],
136
+ [90, "XC"],
137
+ [50, "L"],
138
+ [40, "XL"],
139
+ [10, "X"],
140
+ [9, "IX"],
141
+ [5, "V"],
142
+ [4, "IV"],
143
+ [1, "I"],
144
+ ];
145
+ let remainder = Math.floor(index);
146
+ let result = "";
147
+ for (const [value, numeral] of romanPairs) {
148
+ while (remainder >= value) {
149
+ result += numeral;
150
+ remainder -= value;
151
+ }
152
+ if (remainder === 0) {
153
+ break;
154
+ }
155
+ }
156
+ return result;
157
+ }
158
+
159
+ export function resolveListStyleType(node: LayoutNode): string | undefined {
160
+ const own = normalizeListStyleType(node.style.listStyleType);
161
+ if (own === "none") {
162
+ return "none";
163
+ }
164
+ if (own && own !== "disc") {
165
+ return own;
166
+ }
167
+
168
+ const parent = node.parent;
169
+ if (parent) {
170
+ const parentStyle = normalizeListStyleType(parent.style.listStyleType);
171
+ if (parentStyle === "none") {
172
+ return "none";
173
+ }
174
+ if (parentStyle) {
175
+ return parentStyle;
176
+ }
177
+ if (parent.tagName === "ol") {
178
+ return "decimal";
179
+ }
180
+ if (parent.tagName === "ul") {
181
+ return "disc";
182
+ }
183
+ }
184
+
185
+ return own ?? "disc";
186
+ }
187
+
188
+ export function normalizeListStyleType(value: string | undefined): string | undefined {
189
+ if (!value) {
190
+ return undefined;
191
+ }
192
+ const normalized = value.trim().toLowerCase();
193
+ return normalized.length > 0 ? normalized : undefined;
194
+ }
@@ -0,0 +1,202 @@
1
+ import type { LayoutNode } from "../../dom/node.js";
2
+ import type { RenderBox, Rect, RGBA, Run, Decorations, GradientBackground } from "../types.js";
3
+ import type { Matrix } from "../../geometry/matrix.js";
4
+ import { createTextRuns } from "./text-utils.js";
5
+ import { createListMarkerRun } from "./list-utils.js";
6
+ import { multiplyMatrices } from "../../geometry/matrix.js";
7
+ import type { FontResolver } from "../../fonts/types.js";
8
+ import type { GlyphRun } from "../../layout/text-run.js";
9
+ import type { UnifiedFont } from "../../fonts/types.js";
10
+ import type { KerningMap } from "../../types/fonts.js";
11
+
12
+ export interface NodeTextRunContext {
13
+ node: LayoutNode;
14
+ children: RenderBox[];
15
+ borderBox: Rect;
16
+ contentBox: Rect;
17
+ textColor?: RGBA;
18
+ decorations?: Decorations;
19
+ transform?: Matrix;
20
+ fallbackColor: RGBA;
21
+ fontResolver?: FontResolver;
22
+ textGradient?: GradientBackground;
23
+ }
24
+
25
+ export function buildNodeTextRuns(context: NodeTextRunContext): Run[] {
26
+ const { node, children, borderBox, contentBox, textColor, decorations, transform, fallbackColor, fontResolver, textGradient } = context;
27
+ const textRuns = createTextRuns(node, textColor, decorations);
28
+
29
+ // If we have a fontResolver, enhance text runs with GlyphRun data
30
+ if (fontResolver) {
31
+ enrichTextRunsWithGlyphs(textRuns, fontResolver);
32
+ }
33
+
34
+ if (node.tagName === "li") {
35
+ const markerRun = createListMarkerRun(node, contentBox, children, textColor ?? fallbackColor);
36
+ if (markerRun) {
37
+ textRuns.unshift(markerRun);
38
+ }
39
+ }
40
+
41
+ if (textGradient) {
42
+ for (const run of textRuns) {
43
+ run.textGradient = textGradient;
44
+ }
45
+ }
46
+
47
+ if (transform && textRuns.length > 0) {
48
+ applyTransformToTextRuns(textRuns, transform, borderBox);
49
+ }
50
+
51
+ return textRuns;
52
+ }
53
+
54
+ function applyTransformToTextRuns(runs: Run[], cssMatrix: Matrix, originBox: Rect): void {
55
+ if (runs.length === 0) {
56
+ return;
57
+ }
58
+ const baseOriginX = Number.isFinite(originBox.x) ? originBox.x : 0;
59
+ const baseOriginY = Number.isFinite(originBox.y) ? originBox.y : 0;
60
+ const originWidth = Number.isFinite(originBox.width) ? originBox.width : 0;
61
+ const originHeight = Number.isFinite(originBox.height) ? originBox.height : 0;
62
+ const originX = baseOriginX + originWidth / 2;
63
+ const originY = baseOriginY + originHeight / 2;
64
+ const toOrigin = translationMatrix(-originX, -originY);
65
+ const fromOrigin = translationMatrix(originX, originY);
66
+ for (const run of runs) {
67
+ const baseMatrix = run.lineMatrix ?? { a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 };
68
+ const localMatrix = multiplyMatrices(toOrigin, baseMatrix);
69
+ const transformedLocal = multiplyMatrices(cssMatrix, localMatrix);
70
+ run.lineMatrix = multiplyMatrices(fromOrigin, transformedLocal);
71
+ }
72
+ }
73
+
74
+ function translationMatrix(tx: number, ty: number): Matrix {
75
+ return { a: 1, b: 0, c: 0, d: 1, e: tx, f: ty };
76
+ }
77
+
78
+ /**
79
+ * Enriches text runs with GlyphRun data for TTF-based rendering.
80
+ * For each run, resolves the font, maps text to glyph IDs, and computes positions.
81
+ */
82
+ function enrichTextRunsWithGlyphs(runs: Run[], fontResolver: FontResolver): void {
83
+ for (const run of runs) {
84
+ try {
85
+ // Resolve the font synchronously if possible
86
+ const font = fontResolver.resolveSync
87
+ ? fontResolver.resolveSync(run.fontFamily, run.fontWeight, run.fontStyle)
88
+ : undefined;
89
+
90
+ if (!font) {
91
+ // If we can't resolve synchronously, skip glyph enrichment for now
92
+ continue;
93
+ }
94
+
95
+ // Map Unicode text to glyph IDs
96
+ const glyphRun = computeGlyphRun(font, run.text, run.fontSize, run.letterSpacing ?? 0);
97
+ // Carry through any word spacing for justified lines so glyph positioning can reflect it.
98
+ if (run.wordSpacing !== undefined && glyphRun.positions.length > 0) {
99
+ applyWordSpacingToGlyphRun(glyphRun, run.text, run.wordSpacing);
100
+ }
101
+
102
+ run.glyphs = glyphRun;
103
+ } catch (error) {
104
+ // If font resolution or glyph mapping fails, continue without glyph data
105
+ console.warn(`Failed to create GlyphRun for text "${run.text}": ${error}`);
106
+ }
107
+ }
108
+ }
109
+
110
+ /**
111
+ * Map text to glyph IDs and compute positions with optional letter spacing.
112
+ * Letter spacing is added between glyphs (not after the last glyph) in CSS px units.
113
+ */
114
+ export function computeGlyphRun(font: UnifiedFont, text: string, fontSize: number, letterSpacing: number): GlyphRun {
115
+ const glyphIds: number[] = [];
116
+ const positions: { x: number; y: number }[] = [];
117
+ let currentX = 0;
118
+ const kerning = font.metrics.kerning;
119
+ const unitsPerEm = font.metrics.metrics.unitsPerEm;
120
+ let prevGid: number | null = null;
121
+
122
+ for (let i = 0; i < text.length; i++) {
123
+ const codePoint = text.codePointAt(i) ?? 0;
124
+ const glyphId = font.metrics.cmap.getGlyphId(codePoint);
125
+
126
+ glyphIds.push(glyphId);
127
+
128
+ // Get advance width for this glyph
129
+ const glyphMetric = font.metrics.glyphMetrics.get(glyphId);
130
+ const advanceWidth = glyphMetric?.advanceWidth ?? 0;
131
+
132
+ // Apply kerning adjustment from previous glyph if present
133
+ if (prevGid !== null && kerning) {
134
+ const kernAdjust = getKerningAdjustment(kerning, prevGid, glyphId);
135
+ if (kernAdjust !== 0) {
136
+ currentX += (kernAdjust / unitsPerEm) * fontSize;
137
+ }
138
+ }
139
+
140
+ // Scale advance width to font size
141
+ const scaledAdvance = (advanceWidth / unitsPerEm) * fontSize;
142
+
143
+ positions.push({ x: currentX, y: 0 });
144
+ currentX += scaledAdvance;
145
+
146
+ // Apply letter-spacing between glyphs
147
+ if (i < text.length - 1) {
148
+ currentX += letterSpacing;
149
+ }
150
+
151
+ // Handle surrogate pairs (advance i if this was a surrogate pair)
152
+ if (codePoint > 0xFFFF) {
153
+ i++;
154
+ }
155
+
156
+ prevGid = glyphId;
157
+ }
158
+
159
+ return {
160
+ font,
161
+ glyphIds,
162
+ positions,
163
+ text,
164
+ fontSize,
165
+ width: currentX,
166
+ };
167
+ }
168
+
169
+ export function applyWordSpacingToGlyphRun(glyphRun: GlyphRun, text: string, wordSpacing: number | undefined): void {
170
+ // Optimization 1: Simpler truthy check covers (undefined, null, 0)
171
+ if (!wordSpacing) {
172
+ return;
173
+ }
174
+
175
+ let accumulatedSpacing = 0;
176
+ // Optimization 2: Cache the length.
177
+ // Accessing .length on every iteration can be slightly slower in some JS engines.
178
+ const len = glyphRun.positions.length;
179
+
180
+ for (let i = 0; i < len; i++) {
181
+ if (accumulatedSpacing > 0) {
182
+ glyphRun.positions[i].x += accumulatedSpacing;
183
+ }
184
+
185
+ // Optimization 3: Use charCodeAt instead of string comparison.
186
+ // text[i] === " " creates a temporary string object for every character.
187
+ // charCodeAt(i) === 32 compares integers, which is faster and generates no garbage.
188
+ if (text.charCodeAt(i) === 32) {
189
+ accumulatedSpacing += wordSpacing;
190
+ }
191
+ }
192
+
193
+ if (accumulatedSpacing > 0) {
194
+ glyphRun.width = (glyphRun.width ?? 0) + accumulatedSpacing;
195
+ }
196
+ }
197
+
198
+ function getKerningAdjustment(map: KerningMap, left: number, right: number): number {
199
+ const rightMap = map.get(left);
200
+ if (!rightMap) return 0;
201
+ return rightMap.get(right) ?? 0;
202
+ }
@@ -0,0 +1,46 @@
1
+ import type { PdfObjectRef } from "../primitives/pdf-document.js";
2
+ import type { PdfDocument } from "../primitives/pdf-document.js";
3
+ import type { PainterResult } from "../page-painter.js";
4
+
5
+ export interface PageResources {
6
+ fonts: Map<string, PdfObjectRef>;
7
+ xObjects: Map<string, PdfObjectRef>;
8
+ extGStates: Map<string, PdfObjectRef>;
9
+ shadings: Map<string, PdfObjectRef>;
10
+ patterns: Map<string, PdfObjectRef>;
11
+ }
12
+
13
+ export function registerPageResources(doc: PdfDocument, result: PainterResult): PageResources {
14
+ const xObjects = new Map<string, PdfObjectRef>();
15
+ for (const image of result.images) {
16
+ const ref = doc.registerImage(image.image);
17
+ image.ref = ref;
18
+ xObjects.set(image.alias, ref);
19
+ }
20
+
21
+ const extGStates = new Map<string, PdfObjectRef>();
22
+ for (const [name, alpha] of result.graphicsStates) {
23
+ const ref = doc.registerExtGState(alpha);
24
+ extGStates.set(name, ref);
25
+ }
26
+
27
+ const shadings = new Map<string, PdfObjectRef>();
28
+ for (const [name, dict] of result.shadings) {
29
+ const ref = doc.registerShading(name, dict);
30
+ shadings.set(name, ref);
31
+ }
32
+
33
+ const patterns = new Map<string, PdfObjectRef>();
34
+ for (const [name, dict] of result.patterns ?? []) {
35
+ const ref = doc.registerPattern(name, dict);
36
+ patterns.set(name, ref);
37
+ }
38
+
39
+ return {
40
+ fonts: result.fonts,
41
+ xObjects,
42
+ extGStates,
43
+ shadings,
44
+ patterns,
45
+ };
46
+ }
@@ -0,0 +1,102 @@
1
+ import type { PdfObjectRef } from "../primitives/pdf-document.js";
2
+ import { TextRenderer } from "../renderers/text-renderer.js";
3
+ import { ImageRenderer } from "../renderers/image-renderer.js";
4
+ import { ShapeRenderer } from "../renderers/shape-renderer.js";
5
+ import { GraphicsStateManager } from "../renderers/graphics-state-manager.js";
6
+ import { registerGlyphAtlasPages } from "./glyph-atlas-registrar.js";
7
+ import { partitionImageCommands } from "./image-command-partitioner.js";
8
+
9
+ export interface PainterResult {
10
+ readonly content: string;
11
+ readonly fonts: Map<string, PdfObjectRef>;
12
+ readonly images: PainterImageResource[];
13
+ readonly graphicsStates: Map<string, number>;
14
+ readonly shadings: Map<string, string>;
15
+ readonly patterns?: Map<string, string>;
16
+ }
17
+
18
+ export interface PainterImageResource {
19
+ readonly alias: string;
20
+ readonly image: {
21
+ src: string;
22
+ width: number;
23
+ height: number;
24
+ format: "jpeg" | "png" | "gif" | "webp";
25
+ channels: number;
26
+ bitsPerComponent: number;
27
+ data: Uint8Array;
28
+ };
29
+ ref?: PdfObjectRef;
30
+ }
31
+
32
+ export class ResultCombiner {
33
+ constructor(
34
+ private readonly textRenderer: TextRenderer,
35
+ private readonly imageRenderer: ImageRenderer,
36
+ private readonly shapeRenderer: ShapeRenderer,
37
+ private readonly graphicsStateManager: GraphicsStateManager,
38
+ ) {}
39
+
40
+ combineResults(): PainterResult {
41
+ const textResult = this.textRenderer.getResult();
42
+ registerGlyphAtlasPages(this.imageRenderer);
43
+ const imageResult = this.imageRenderer.getResult();
44
+ const shapeResult = this.shapeRenderer.getResult();
45
+ const graphicsStates = new Map<string, number>();
46
+ for (const [name, alpha] of this.graphicsStateManager.getGraphicsStates()) {
47
+ graphicsStates.set(name, alpha);
48
+ }
49
+
50
+ // Partition image commands: shadow rasters (drawn beneath shapes) vs others
51
+ const shadowAliases = new Set<string>();
52
+ for (const [, res] of imageResult.images) {
53
+ if (res.image.src && typeof res.image.src === 'string' && res.image.src.startsWith('internal:shadow:')) {
54
+ shadowAliases.add(res.alias);
55
+ }
56
+ }
57
+
58
+ const { preShadow, post } = partitionImageCommands(imageResult.commands, shadowAliases);
59
+ const allCommands = [...preShadow, ...shapeResult.commands, ...textResult.commands, ...post];
60
+
61
+ // Process image resources to match the expected format
62
+ const processedImages: PainterImageResource[] = [];
63
+ for (const [, resource] of imageResult.images) {
64
+ // Convert the image resource to the expected format
65
+ processedImages.push({
66
+ alias: resource.alias,
67
+ image: {
68
+ src: resource.image.src,
69
+ width: resource.image.width,
70
+ height: resource.image.height,
71
+ format: resource.image.format,
72
+ channels: resource.image.channels,
73
+ bitsPerComponent: resource.image.bitsPerComponent,
74
+ data: new Uint8Array(resource.image.data), // Convert ArrayBuffer to Uint8Array
75
+ },
76
+ ref: resource.ref
77
+ });
78
+ }
79
+
80
+ const combinedShadings = new Map<string, string>();
81
+ for (const [name, dict] of shapeResult.shadings) {
82
+ combinedShadings.set(name, dict);
83
+ }
84
+ for (const [name, dict] of textResult.shadings) {
85
+ combinedShadings.set(name, dict);
86
+ }
87
+
88
+ const combinedPatterns = new Map<string, string>();
89
+ for (const [name, dict] of textResult.patterns ?? []) {
90
+ combinedPatterns.set(name, dict);
91
+ }
92
+
93
+ return {
94
+ content: allCommands.join("\n"),
95
+ fonts: textResult.fonts,
96
+ images: processedImages,
97
+ graphicsStates,
98
+ shadings: combinedShadings,
99
+ patterns: combinedPatterns,
100
+ };
101
+ }
102
+ }
@@ -0,0 +1,127 @@
1
+ import type { LayoutNode } from "../../dom/node.js";
2
+ import type { Rect, ShadowLayer, RGBA } from "../types.js";
3
+ import { parseColor, cloneColor } from "./color-utils.js";
4
+
5
+ export function resolveBoxShadows(node: LayoutNode, fallbackColor: RGBA): ShadowLayer[] {
6
+ const result: ShadowLayer[] = [];
7
+ const shadows = node.style.boxShadows ?? [];
8
+ for (const shadow of shadows) {
9
+ const color = resolveShadowColor(shadow.color, node.style.color, fallbackColor);
10
+ if (!color) {
11
+ continue;
12
+ }
13
+ result.push({
14
+ inset: shadow.inset,
15
+ offsetX: shadow.offsetX,
16
+ offsetY: shadow.offsetY,
17
+ blur: clampNonNegative(shadow.blurRadius),
18
+ spread: shadow.spreadRadius ?? 0,
19
+ color,
20
+ });
21
+ }
22
+ return result;
23
+ }
24
+
25
+ export function resolveTextShadows(node: LayoutNode, fallbackColor: RGBA): ShadowLayer[] {
26
+ const result: ShadowLayer[] = [];
27
+ const shadows = (node.style as any).textShadows ?? [];
28
+ for (const shadow of shadows) {
29
+ // shadow here is expected to have numeric offsetX/offsetY/blurRadius and optional color
30
+ const offsetX = shadow.offsetX ?? 0;
31
+ const offsetY = shadow.offsetY ?? 0;
32
+ const blur = clampNonNegative(shadow.blurRadius ?? 0);
33
+ const color = resolveShadowColor(shadow.color, node.style.color, fallbackColor);
34
+ if (!color) {
35
+ continue;
36
+ }
37
+ result.push({
38
+ inset: false,
39
+ offsetX,
40
+ offsetY,
41
+ blur,
42
+ spread: 0,
43
+ color,
44
+ });
45
+ }
46
+ return result;
47
+ }
48
+
49
+ export function resolveShadowColor(specified: string | undefined, styleColor: string | undefined, fallbackColor: RGBA): RGBA | undefined {
50
+ if (!specified || specified.trim().length === 0) {
51
+ return cloneColor(fallbackColor);
52
+ }
53
+ const normalized = specified.trim().toLowerCase();
54
+ if (normalized === "transparent") {
55
+ return undefined;
56
+ }
57
+ if (normalized === "currentcolor") {
58
+ const current = parseColor(styleColor);
59
+ return cloneColor(current ?? fallbackColor);
60
+ }
61
+ const parsed = parseColor(specified);
62
+ if (parsed) {
63
+ return cloneColor(parsed);
64
+ }
65
+ return cloneColor(fallbackColor);
66
+ }
67
+
68
+ export function computeShadowVisualOverflow(base: Rect, shadow: ShadowLayer): Rect | null {
69
+ const spread = shadow.spread;
70
+ const blur = shadow.blur;
71
+ const baseWidth = Math.max(base.width + spread * 2, 0);
72
+ const baseHeight = Math.max(base.height + spread * 2, 0);
73
+ const baseX = base.x + shadow.offsetX - spread;
74
+ const baseY = base.y + shadow.offsetY - spread;
75
+ const finalWidth = baseWidth + blur * 2;
76
+ const finalHeight = baseHeight + blur * 2;
77
+ if (finalWidth <= 0 || finalHeight <= 0) {
78
+ return null;
79
+ }
80
+ return {
81
+ x: baseX - blur,
82
+ y: baseY - blur,
83
+ width: finalWidth,
84
+ height: finalHeight,
85
+ };
86
+ }
87
+
88
+ export function calculateVisualOverflow(_node: LayoutNode, borderBox: Rect, boxShadows: ShadowLayer[]): Rect {
89
+ const visualOverflow = cloneRect(borderBox);
90
+
91
+ for (const shadow of boxShadows) {
92
+ if (shadow.inset) {
93
+ continue;
94
+ }
95
+ const shadowRect = computeShadowVisualOverflow(borderBox, shadow);
96
+ if (shadowRect) {
97
+ expandRectToInclude(visualOverflow, shadowRect);
98
+ }
99
+ }
100
+
101
+ return visualOverflow;
102
+ }
103
+
104
+ function clampNonNegative(value: number): number {
105
+ if (!Number.isFinite(value)) {
106
+ return 0;
107
+ }
108
+ return value < 0 ? 0 : value;
109
+ }
110
+
111
+ function cloneRect(rect: Rect): Rect {
112
+ return { x: rect.x, y: rect.y, width: rect.width, height: rect.height };
113
+ }
114
+
115
+ function expandRectToInclude(target: Rect, addition: Rect): void {
116
+ if (addition.width <= 0 || addition.height <= 0) {
117
+ return;
118
+ }
119
+ const minX = Math.min(target.x, addition.x);
120
+ const minY = Math.min(target.y, addition.y);
121
+ const maxX = Math.max(target.x + target.width, addition.x + addition.width);
122
+ const maxY = Math.max(target.y + target.height, addition.y + addition.height);
123
+ target.x = minX;
124
+ target.y = minY;
125
+ target.width = Math.max(0, maxX - minX);
126
+ target.height = Math.max(0, maxY - minY);
127
+ }