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,76 @@
1
+ import { Display, JustifyContent } from "../../css/enums.js";
2
+ import type { LayoutNode } from "../../dom/node.js";
3
+
4
+ export function resolveTextAlign(node: LayoutNode): string | undefined {
5
+ let current: LayoutNode | null = node;
6
+ while (current) {
7
+ const value = current.style.textAlign;
8
+ if (value && value !== "start" && value !== "auto") {
9
+ return value;
10
+ }
11
+ current = current.parent;
12
+ }
13
+ return undefined;
14
+ }
15
+
16
+ export function resolveFallbackStartX(node: LayoutNode, advanceWidth: number, textAlign?: string): number {
17
+ let startX = node.box.x;
18
+ if (advanceWidth > 0 && Number.isFinite(node.box.contentWidth) && node.box.contentWidth > 0) {
19
+ const slack = Math.max(node.box.contentWidth - advanceWidth, 0);
20
+ if (textAlign === "center") {
21
+ startX = node.box.x + slack / 2;
22
+ } else if (textAlign === "right" || textAlign === "end") {
23
+ startX = node.box.x + slack;
24
+ } else if (textAlign === "left" || textAlign === "start") {
25
+ startX = node.box.x;
26
+ }
27
+ }
28
+
29
+ const flexStart = resolveFlexSingleChildStart(node, advanceWidth);
30
+ if (flexStart !== null) {
31
+ startX = flexStart;
32
+ }
33
+ return startX;
34
+ }
35
+
36
+ export function resolveFlexSingleChildStart(node: LayoutNode, advanceWidth: number): number | null {
37
+ if (!node.parent || advanceWidth <= 0) {
38
+ return null;
39
+ }
40
+ const parent = node.parent;
41
+ if (parent.style.display !== Display.Flex) {
42
+ return null;
43
+ }
44
+ if (parent.children.length !== 1) {
45
+ return null;
46
+ }
47
+ const direction = parent.style.flexDirection ?? "row";
48
+ if (direction !== "row" && direction !== "row-reverse") {
49
+ return null;
50
+ }
51
+ const justify = parent.style.justifyContent ?? JustifyContent.FlexStart;
52
+ const parentWidth = Number.isFinite(parent.box.contentWidth) ? parent.box.contentWidth : 0;
53
+ if (!(parentWidth > 0)) {
54
+ return null;
55
+ }
56
+ const constrainedWidth = advanceWidth > parentWidth ? parentWidth : advanceWidth;
57
+ const slack = Math.max(parentWidth - constrainedWidth, 0);
58
+ const parentStart = parent.box.x;
59
+ const parentEnd = parentStart + parentWidth;
60
+ const isReverse = direction === "row-reverse";
61
+
62
+ switch (justify) {
63
+ case JustifyContent.Center:
64
+ return parentStart + slack / 2;
65
+ case JustifyContent.FlexEnd:
66
+ case JustifyContent.End:
67
+ case JustifyContent.Right:
68
+ return isReverse ? parentStart : parentEnd - constrainedWidth;
69
+ case JustifyContent.FlexStart:
70
+ case JustifyContent.Start:
71
+ case JustifyContent.Left:
72
+ return isReverse ? parentEnd - constrainedWidth : parentStart;
73
+ default:
74
+ return null;
75
+ }
76
+ }
@@ -0,0 +1,64 @@
1
+ import type { ComputedStyle } from "../../css/style.js";
2
+ import type { Decorations, RGBA } from "../types.js";
3
+ import { parseColor } from "./color-utils.js";
4
+
5
+ export function resolveDecorations(style: ComputedStyle): Decorations | undefined {
6
+ const value = style.textDecorationLine?.trim().toLowerCase();
7
+ if (!value || value === "none") {
8
+ return undefined;
9
+ }
10
+ const tokens = value.split(/\s+/);
11
+ const decoration: Decorations = {};
12
+ for (const token of tokens) {
13
+ switch (token) {
14
+ case "underline":
15
+ decoration.underline = true;
16
+ break;
17
+ case "overline":
18
+ decoration.overline = true;
19
+ break;
20
+ case "line-through":
21
+ decoration.lineThrough = true;
22
+ break;
23
+ default:
24
+ break;
25
+ }
26
+ }
27
+ if (!(decoration.underline || decoration.overline || decoration.lineThrough)) {
28
+ return undefined;
29
+ }
30
+
31
+ const styleKeyword = style.textDecorationStyle?.trim().toLowerCase();
32
+ switch (styleKeyword) {
33
+ case "solid":
34
+ case "double":
35
+ case "dotted":
36
+ case "dashed":
37
+ case "wavy":
38
+ decoration.style = styleKeyword;
39
+ break;
40
+ default:
41
+ decoration.style = "solid";
42
+ break;
43
+ }
44
+
45
+ const color = resolveDecorationColor(style);
46
+ if (color) {
47
+ decoration.color = color;
48
+ }
49
+ return decoration;
50
+ }
51
+
52
+ function resolveDecorationColor(style: ComputedStyle): RGBA | undefined {
53
+ const raw = style.textDecorationColor;
54
+ if (raw) {
55
+ if (raw.toLowerCase() === "currentcolor") {
56
+ return parseColor(style.color);
57
+ }
58
+ const parsed = parseColor(raw);
59
+ if (parsed) {
60
+ return parsed;
61
+ }
62
+ }
63
+ return parseColor(style.color);
64
+ }
@@ -0,0 +1,185 @@
1
+ import type { RenderBox, Run } from "../types.js";
2
+
3
+ /**
4
+ * Global pass to fix justified layout in the render tree.
5
+ *
6
+ * For each RenderBox with textAlign: "justify":
7
+ * - collect ALL runs from that box and its descendants
8
+ * - group runs by lineIndex
9
+ import type { RenderBox, Run } from "../types.js";
10
+
11
+ /**
12
+ * Global pass to fix justified layout in the render tree.
13
+ *
14
+ * For each RenderBox with textAlign: "justify":
15
+ * - collect ALL runs from that box and its descendants
16
+ * - group runs by lineIndex
17
+ * - sort each group by lineMatrix.e (X)
18
+ * -for each non-final line:
19
+ * * shift later runs by the cumulative expansion
20
+ * from previous runs: spacesInRun * wordSpacing
21
+ */
22
+ export function applyTextLayoutAdjustments(root: RenderBox): void {
23
+ traverse(root, (box) => {
24
+ // Only process boxes with justify alignment
25
+ if (box.textAlign !== "justify") return true; // Continue searching
26
+
27
+ // Collect ALL runs from this box and its descendants
28
+ const allRuns: Run[] = [];
29
+ collectRunsFromTree(box, allRuns);
30
+
31
+ const candidates = allRuns.filter(
32
+ (run) => typeof run.lineIndex === "number",
33
+ );
34
+ if (candidates.length === 0) return false; // Stop descending
35
+
36
+ const groups = groupRunsByLineIndex(candidates);
37
+ for (const lineRuns of groups) {
38
+ adjustLinePositions(lineRuns);
39
+ }
40
+
41
+ return false; // Stop descending into this justified box
42
+ });
43
+ }
44
+
45
+ function groupRunsByLineIndex(runs: Run[]): Run[][] {
46
+ const map = new Map<number, Run[]>();
47
+
48
+ for (const run of runs) {
49
+ const idx = run.lineIndex as number;
50
+ let group = map.get(idx);
51
+ if (!group) {
52
+ group = [];
53
+ map.set(idx, group);
54
+ }
55
+ group.push(run);
56
+ }
57
+
58
+ for (const group of map.values()) {
59
+ group.sort((a, b) => {
60
+ const ax = a.lineMatrix?.e ?? 0;
61
+ const bx = b.lineMatrix?.e ?? 0;
62
+ return ax - bx;
63
+ });
64
+ }
65
+
66
+ return Array.from(map.values());
67
+ }
68
+
69
+ /**
70
+ * Adjust positions on a single visual line.
71
+ *
72
+ * - Skip last lines (isLastLine === true).
73
+ * - Skip lines with no spaces.
74
+ * - Walk from left to right:
75
+ * * shift run.x by cumulativeOffset
76
+ * * cumulativeOffset += spacesInRun * wordSpacing
77
+ */
78
+ function adjustLinePositions(runs: Run[]): void {
79
+ if (runs.length === 0) return;
80
+
81
+ // Find first non-whitespace run to identify leading spaces
82
+ let firstContentIndex = 0;
83
+ while (
84
+ firstContentIndex < runs.length &&
85
+ /^\s+$/.test(runs[firstContentIndex].text)
86
+ ) {
87
+ firstContentIndex++;
88
+ }
89
+
90
+ // Calculate width to redistribute from leading spaces
91
+ let widthLost = 0;
92
+ for (let i = 0; i < firstContentIndex; i++) {
93
+ widthLost += runs[i].advanceWidth ?? 0;
94
+ }
95
+
96
+ // Count remaining spaces
97
+ let remainingSpaces = 0;
98
+ for (let i = firstContentIndex; i < runs.length; i++) {
99
+ remainingSpaces += runs[i].spacesInRun ?? 0;
100
+ }
101
+
102
+ const isLastLine = runs[0].isLastLine ?? false;
103
+
104
+ // Redistribute width if not last line and we have spaces to distribute to
105
+ if (!isLastLine && widthLost > 0 && remainingSpaces > 0) {
106
+ const adjustment = widthLost / remainingSpaces;
107
+ for (let i = firstContentIndex; i < runs.length; i++) {
108
+ const run = runs[i];
109
+ if ((run.spacesInRun ?? 0) > 0) {
110
+ run.wordSpacing = (run.wordSpacing ?? 0) + adjustment;
111
+ // Update advanceWidth to reflect the new spacing
112
+ run.advanceWidth =
113
+ (run.advanceWidth ?? 0) + (run.spacesInRun! * adjustment);
114
+ }
115
+ }
116
+ }
117
+
118
+ // Capture original positions and base widths to detect existing gaps
119
+ const originals = runs.map((run) => ({
120
+ x: run.lineMatrix?.e ?? 0,
121
+ baseWidth:
122
+ (run.advanceWidth ?? 0) -
123
+ (run.spacesInRun ?? 0) * (run.wordSpacing ?? 0),
124
+ fontSize: run.fontSize,
125
+ }));
126
+
127
+ let currentX = originals[0].x;
128
+
129
+ for (let i = 0; i < runs.length; i++) {
130
+ const run = runs[i];
131
+
132
+ // If it's a leading whitespace run, treat as zero width and skip
133
+ if (i < firstContentIndex) {
134
+ if (run.lineMatrix) {
135
+ run.lineMatrix.e = currentX;
136
+ }
137
+ // Set width to 0 to avoid overlaps in tests and rendering
138
+ run.advanceWidth = 0;
139
+ continue;
140
+ }
141
+
142
+ if (run.lineMatrix) {
143
+ run.lineMatrix.e = currentX;
144
+ }
145
+
146
+ const advanceWidth = run.advanceWidth ?? 0;
147
+ currentX += advanceWidth;
148
+
149
+ // If there's a significant gap between this run and the next (e.g. an image), preserve it.
150
+ // Otherwise, assume it's a text gap/overlap and close it (using our calculated advanceWidth).
151
+ if (i < runs.length - 1) {
152
+ const nextOriginal = originals[i + 1];
153
+ const currOriginal = originals[i];
154
+
155
+ const gap = nextOriginal.x - (currOriginal.x + currOriginal.baseWidth);
156
+ // Use the larger font size as a threshold for "is this a text gap?"
157
+ const threshold = Math.max(currOriginal.fontSize, nextOriginal.fontSize);
158
+
159
+ // Preserve gap if it's large, OR if the current run has 0 width (and wasn't stripped)
160
+ // This handles cases where layout engine produced 0-width runs but placed them with spacing.
161
+ const isStripped = i < firstContentIndex;
162
+ if (gap > threshold || (!isStripped && advanceWidth === 0 && gap > 0)) {
163
+ currentX += gap;
164
+ }
165
+ }
166
+ }
167
+ }
168
+
169
+ function collectRunsFromTree(box: RenderBox, accumulator: Run[]): void {
170
+ if (box.textRuns) {
171
+ accumulator.push(...box.textRuns);
172
+ }
173
+ for (const child of box.children) {
174
+ collectRunsFromTree(child, accumulator);
175
+ }
176
+ }
177
+
178
+ function traverse(box: RenderBox, visitor: (box: RenderBox) => boolean): void {
179
+ const continueTraversal = visitor(box);
180
+ if (continueTraversal) {
181
+ for (const child of box.children) {
182
+ traverse(child, visitor);
183
+ }
184
+ }
185
+ }
@@ -0,0 +1,193 @@
1
+ import { resolvedLineHeight } from "../../css/style.js";
2
+ import type { ComputedStyle } from "../../css/style.js";
3
+ import { estimateLineWidth } from "../../layout/utils/text-metrics.js";
4
+ import type { LayoutNode } from "../../dom/node.js";
5
+ import type { Run, Decorations, RGBA } from "../types.js";
6
+ import { applyTextTransform } from "../../text/text-transform.js";
7
+ import { resolveTextShadows } from "../../pdf/utils/shadow-utils.js";
8
+ import { resolveTextAlign, resolveFallbackStartX } from "./text-alignment-resolver.js";
9
+ import { resolveDecorations } from "./text-decoration-utils.js";
10
+
11
+ export function createTextRuns(node: LayoutNode, color: RGBA | undefined, inheritedDecorations?: Decorations): Run[] {
12
+ const runs: Run[] = [];
13
+ const defaultColor = color ?? { r: 0, g: 0, b: 0, a: 1 };
14
+ const effectiveTextAlign = resolveTextAlign(node) ?? node.style.textAlign;
15
+ const decoration = inheritedDecorations ?? resolveDecorations(node.style);
16
+ const fontFamily = node.style.fontFamily ?? "sans-serif";
17
+ const fontSize = node.style.fontSize;
18
+ const fontWeight = node.style.fontWeight;
19
+ const fontStyle = node.style.fontStyle;
20
+ const fontVariant = node.style.fontVariant;
21
+ const letterSpacing = node.style.letterSpacing;
22
+
23
+
24
+ // Se o layout calculou inlineRuns (novo builder), use-os.
25
+ if (node.inlineRuns && node.inlineRuns.length > 0) {
26
+ for (const inlineRun of node.inlineRuns) {
27
+ const justifyLine =
28
+ effectiveTextAlign === "justify" && !inlineRun.isLastLine;
29
+
30
+ let wordSpacing: number | undefined;
31
+ const targetWidth =
32
+ inlineRun.targetWidth ?? inlineRun.lineWidth ?? inlineRun.width;
33
+ const lineWidth = inlineRun.lineWidth ?? inlineRun.width;
34
+
35
+ if (justifyLine && inlineRun.spaceCount > 0) {
36
+ const slack = Math.max(targetWidth - lineWidth, 0);
37
+ if (slack > 0) {
38
+ wordSpacing = slack / inlineRun.spaceCount;
39
+ }
40
+ }
41
+
42
+ // Count spaces *inside this run's text*
43
+ const spacesInRun =
44
+ inlineRun.text.split("").reduce(
45
+ (count, ch) => (ch === " " ? count + 1 : count),
46
+ 0,
47
+ );
48
+
49
+ const extraWidth =
50
+ spacesInRun > 0 && wordSpacing !== undefined
51
+ ? spacesInRun * wordSpacing
52
+ : 0;
53
+
54
+ // Run-local visual width (base width + justified expansion)
55
+ const advanceWidth = inlineRun.width + extraWidth;
56
+
57
+ const resolvedShadows = resolveTextShadows(node, defaultColor);
58
+ const baseLineMatrix = {
59
+ a: 1,
60
+ b: 0,
61
+ c: 0,
62
+ d: 1,
63
+ e: inlineRun.startX,
64
+ f: inlineRun.baseline,
65
+ };
66
+
67
+ runs.push({
68
+ text: inlineRun.text.normalize("NFC"),
69
+ fontFamily,
70
+ fontSize,
71
+ fontWeight,
72
+ fontStyle,
73
+ fontVariant,
74
+ fill: defaultColor,
75
+ letterSpacing,
76
+ lineMatrix: baseLineMatrix,
77
+ wordSpacing,
78
+ decorations: decoration ? { ...decoration } : undefined,
79
+ advanceWidth,
80
+ textShadows: resolvedShadows,
81
+
82
+ // New metadata
83
+ lineIndex: inlineRun.lineIndex,
84
+ isLastLine: inlineRun.isLastLine,
85
+ spacesInRun,
86
+ });
87
+ }
88
+ return runs;
89
+ }
90
+
91
+ // Se o layout calculou caixas de linha, use-as (caminho leg legado).
92
+ if (node.lineBoxes && node.lineBoxes.length > 0) {
93
+ const lineHeight = resolvedLineHeight(node.style);
94
+ // For table cells, we need the parent cell's contentWidth, not the text node's width
95
+ const parentIsTableCell = node.parent && (node.parent.tagName === 'td' || node.parent.tagName === 'th');
96
+ const contentWidth = parentIsTableCell
97
+ ? Math.max(node.parent!.box.contentWidth, 0)
98
+ : Math.max(node.box.contentWidth, 0);
99
+ // Alignment logic
100
+ let alignY = node.box.y;
101
+ // Vertical alignment
102
+ let totalTextHeight = node.lineBoxes.length * lineHeight;
103
+ if (node.style.verticalAlign === "middle") {
104
+ alignY = node.box.y + (node.box.contentHeight - totalTextHeight) / 2;
105
+ } else if (node.style.verticalAlign === "bottom") {
106
+ alignY = node.box.y + (node.box.contentHeight - totalTextHeight);
107
+ }
108
+ const justify = effectiveTextAlign === "justify";
109
+ for (let i = 0; i < node.lineBoxes.length; i++) {
110
+ const line = node.lineBoxes[i];
111
+ const normalizedText = line.text.normalize("NFC");
112
+ const baseWidth = line.width ?? estimateLineWidth(normalizedText, node.style);
113
+ const lineYOffset = i * lineHeight;
114
+ const baseline = alignY + lineYOffset + fontSize;
115
+ let wordSpacing: number | undefined;
116
+ if (justify && i < node.lineBoxes.length - 1) {
117
+ const gapCount = line.spaceCount ?? 0;
118
+ if (gapCount > 0) {
119
+ const targetWidth = line.targetWidth ?? contentWidth ?? baseWidth;
120
+ const slack = Math.max(targetWidth - baseWidth, 0);
121
+ if (slack > 0) {
122
+ wordSpacing = slack / gapCount;
123
+ }
124
+ }
125
+ }
126
+ const targetWidth = line.targetWidth ?? baseWidth;
127
+ const advanceWidth =
128
+ wordSpacing !== undefined && wordSpacing !== 0 && targetWidth > 0
129
+ ? Math.max(targetWidth, baseWidth)
130
+ : Math.max(baseWidth, 0);
131
+
132
+ // Apply horizontal text alignment
133
+ let startX = node.box.x;
134
+ if (advanceWidth > 0 && Number.isFinite(contentWidth) && contentWidth > 0) {
135
+ const slack = Math.max(contentWidth - advanceWidth, 0);
136
+ if (effectiveTextAlign === "center") {
137
+ startX = node.box.x + slack / 2;
138
+ } else if (effectiveTextAlign === "right" || effectiveTextAlign === "end") {
139
+ startX = node.box.x + slack;
140
+ }
141
+ }
142
+
143
+ const resolvedShadows = resolveTextShadows(node, defaultColor);
144
+ const baseLineMatrix = { a: 1, b: 0, c: 0, d: 1, e: startX, f: baseline };
145
+ runs.push({
146
+ text: normalizedText,
147
+ fontFamily,
148
+ fontSize,
149
+ fontWeight,
150
+ fontStyle,
151
+ fontVariant,
152
+ fill: defaultColor,
153
+ letterSpacing,
154
+ lineMatrix: baseLineMatrix,
155
+ wordSpacing,
156
+ decorations: decoration ? { ...decoration } : undefined,
157
+ advanceWidth,
158
+ textShadows: resolvedShadows,
159
+ });
160
+ }
161
+ return runs;
162
+ }
163
+
164
+ // Fallback para o comportamento original se não houver quebra de linha calculada
165
+ if (node.textContent) {
166
+ const raw = node.textContent;
167
+ const transformed = applyTextTransform(raw, node.style.textTransform);
168
+ const normalized = transformed.normalize("NFC");
169
+ // Se não houver lineBoxes, a baseline é a calculada para a caixa inteira.
170
+ const baseline = node.box.baseline > 0 ? node.box.baseline : node.box.y + node.box.contentHeight;
171
+ const advanceWidth = Math.max(estimateLineWidth(normalized, node.style), 0);
172
+ const startX = resolveFallbackStartX(node, advanceWidth, effectiveTextAlign);
173
+
174
+ const resolvedShadows = resolveTextShadows(node, defaultColor);
175
+ const baseLineMatrix = { a: 1, b: 0, c: 0, d: 1, e: startX, f: baseline };
176
+ return [{
177
+ text: normalized,
178
+ fontFamily,
179
+ fontSize,
180
+ fontWeight,
181
+ fontStyle,
182
+ fontVariant,
183
+ letterSpacing,
184
+ fill: defaultColor,
185
+ lineMatrix: baseLineMatrix,
186
+ decorations: decoration ? { ...decoration } : undefined,
187
+ advanceWidth,
188
+ textShadows: resolvedShadows,
189
+ }];
190
+ }
191
+
192
+ return [];
193
+ }
@@ -0,0 +1,69 @@
1
+ import type { Rect, TextMatrix } from "../types.js";
2
+ import { CoordinateTransformer } from "./coordinate-transformer.js";
3
+ import { ShapeRenderer } from "../renderers/shape-renderer.js";
4
+ import { svgMatrixToPdf } from "../transform-adapter.js";
5
+
6
+ export class TransformScopeManager {
7
+ constructor(
8
+ private readonly coordinateTransformer: CoordinateTransformer,
9
+ private readonly shapeRenderer: ShapeRenderer,
10
+ ) {}
11
+
12
+ beginTransformScope(transform: TextMatrix, rect: Rect): void {
13
+ // Store transform info for shape renderer to use
14
+ const pdfMatrix = svgMatrixToPdf(transform);
15
+ if (!pdfMatrix) {
16
+ this.shapeRenderer.pushRawCommands(["q"]);
17
+ return;
18
+ }
19
+
20
+ // Convert rect position to PDF coordinates
21
+ const xPt = this.coordinateTransformer.convertPxToPt(rect.x);
22
+ const localY = rect.y - this.coordinateTransformer.pageOffsetPx;
23
+ const yPt = this.coordinateTransformer.pageHeightPt - this.coordinateTransformer.convertPxToPt(localY);
24
+
25
+ // CSS transforms default to a 50% / 50% origin. Model that by translating
26
+ // relative coordinates so the transform happens around the center of the
27
+ // border box.
28
+ const originOffsetX = Number.isFinite(rect.width) ? rect.width / 2 : 0;
29
+ const originOffsetY = Number.isFinite(rect.height) ? rect.height / 2 : 0;
30
+ const translateBackX = this.coordinateTransformer.convertPxToPt(-originOffsetX);
31
+ const translateBackY = this.coordinateTransformer.convertPxToPt(originOffsetY);
32
+ const translateForwardX = this.coordinateTransformer.convertPxToPt(originOffsetX);
33
+ const translateForwardY = this.coordinateTransformer.convertPxToPt(-originOffsetY);
34
+
35
+ // Set up transform context: translate to position, apply transform
36
+ // This way shapes can be drawn at origin (0,0) and will be positioned and transformed correctly
37
+ const cmds: string[] = [
38
+ "q",
39
+ // Position element on page (applied last in the matrix chain)
40
+ `1 0 0 1 ${formatNumber(xPt)} ${formatNumber(yPt)} cm`,
41
+ // Translate back from pivot after applying skew/rotate
42
+ `1 0 0 1 ${formatNumber(translateForwardX)} ${formatNumber(translateForwardY)} cm`,
43
+ // Apply the css transform (already converted into PDF space)
44
+ `${formatNumber(pdfMatrix.a)} ${formatNumber(pdfMatrix.b)} ${formatNumber(pdfMatrix.c)} ${formatNumber(pdfMatrix.d)} 0 0 cm`,
45
+ // Move the local coordinate system so the transform pivot is the center of the box
46
+ `1 0 0 1 ${formatNumber(translateBackX)} ${formatNumber(translateBackY)} cm`,
47
+ ];
48
+
49
+ // Add marker for testing
50
+ if (pdfMatrix.b !== 0 || pdfMatrix.c !== 0) {
51
+ cmds.push(`%PAGYRA_TRANSFORM ${formatNumber(pdfMatrix.a)} ${formatNumber(pdfMatrix.b)} ${formatNumber(pdfMatrix.c)} ${formatNumber(pdfMatrix.d)} ${formatNumber(pdfMatrix.e)} ${formatNumber(pdfMatrix.f)}`);
52
+ }
53
+
54
+ this.shapeRenderer.pushRawCommands(cmds);
55
+ this.shapeRenderer.setTransformContext(rect);
56
+ }
57
+
58
+ endTransformScope(): void {
59
+ this.shapeRenderer.pushRawCommands(["Q"]);
60
+ this.shapeRenderer.clearTransformContext();
61
+ }
62
+ }
63
+
64
+ function formatNumber(value: number): string {
65
+ if (!Number.isFinite(value)) {
66
+ return "0";
67
+ }
68
+ return Number.isInteger(value) ? value.toString() : value.toFixed(4).replace(/0+$/, "").replace(/\.$/, "");
69
+ }