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,215 @@
1
+ // src/css/custom-properties.ts
2
+
3
+ /**
4
+ * CSS Custom Properties (CSS Variables) support
5
+ * Implements variable definition and resolution per CSS Custom Properties spec
6
+ */
7
+
8
+ /**
9
+ * Storage for custom properties (CSS variables)
10
+ * Maps variable names (e.g., "--primary-color") to their raw string values
11
+ */
12
+ export class CustomPropertiesMap {
13
+ private properties: Map<string, string> = new Map();
14
+
15
+ /**
16
+ * Set a custom property value
17
+ */
18
+ set(name: string, value: string): void {
19
+ // Ensure the property name starts with --
20
+ if (!name.startsWith('--')) {
21
+ return;
22
+ }
23
+ this.properties.set(name, value.trim());
24
+ }
25
+
26
+ /**
27
+ * Get a custom property value
28
+ */
29
+ get(name: string): string | undefined {
30
+ return this.properties.get(name);
31
+ }
32
+
33
+ /**
34
+ * Check if a custom property exists
35
+ */
36
+ has(name: string): boolean {
37
+ return this.properties.has(name);
38
+ }
39
+
40
+ /**
41
+ * Clone the custom properties map
42
+ */
43
+ clone(): CustomPropertiesMap {
44
+ const cloned = new CustomPropertiesMap();
45
+ this.properties.forEach((value, key) => {
46
+ cloned.set(key, value);
47
+ });
48
+ return cloned;
49
+ }
50
+
51
+ /**
52
+ * Inherit properties from a parent map
53
+ * Child properties override parent properties
54
+ */
55
+ inherit(parent: CustomPropertiesMap | undefined): CustomPropertiesMap {
56
+ if (!parent) {
57
+ return this.clone();
58
+ }
59
+
60
+ const inherited = parent.clone();
61
+ this.properties.forEach((value, key) => {
62
+ inherited.set(key, value);
63
+ });
64
+ return inherited;
65
+ }
66
+
67
+ /**
68
+ * Get all property names
69
+ */
70
+ keys(): string[] {
71
+ return Array.from(this.properties.keys());
72
+ }
73
+
74
+ /**
75
+ * Get the number of properties
76
+ */
77
+ get size(): number {
78
+ return this.properties.size;
79
+ }
80
+ }
81
+
82
+ /**
83
+ * Regular expression to match var() function calls
84
+ * Matches: var(--name) or var(--name, fallback)
85
+ */
86
+ const VAR_FUNCTION_REGEX = /var\s*\(\s*(--[a-zA-Z0-9_-]+)\s*(?:,\s*([^)]+))?\s*\)/g;
87
+
88
+ /**
89
+ * Check if a CSS value contains var() references
90
+ */
91
+ export function containsVariableReference(value: string): boolean {
92
+ return value.includes('var(');
93
+ }
94
+
95
+ /**
96
+ * Extract variable name from var() function
97
+ * Example: "var(--primary-color)" -> "--primary-color"
98
+ */
99
+ export function extractVariableName(varFunctionCall: string): string | null {
100
+ const match = varFunctionCall.match(/var\s*\(\s*(--[a-zA-Z0-9_-]+)/);
101
+ return match ? match[1] : null;
102
+ }
103
+
104
+ /**
105
+ * Parse var() function to extract variable name and fallback
106
+ * Returns: { name: string, fallback?: string }
107
+ */
108
+ export function parseVarFunction(varFunctionCall: string): { name: string; fallback?: string } | null {
109
+ const trimmed = varFunctionCall.trim();
110
+
111
+ // Match var(--name) or var(--name, fallback)
112
+ const match = trimmed.match(/^var\s*\(\s*(--[a-zA-Z0-9_-]+)\s*(?:,\s*(.+))?\s*\)$/);
113
+
114
+ if (!match) {
115
+ return null;
116
+ }
117
+
118
+ return {
119
+ name: match[1],
120
+ fallback: match[2]?.trim(),
121
+ };
122
+ }
123
+
124
+ /**
125
+ * Resolve CSS value by replacing var() references with actual values
126
+ * Supports nested var() calls and fallback values
127
+ */
128
+ export function resolveVariableReferences(
129
+ value: string,
130
+ customProperties: CustomPropertiesMap,
131
+ maxDepth = 10
132
+ ): string {
133
+ if (!containsVariableReference(value)) {
134
+ return value;
135
+ }
136
+
137
+ if (maxDepth <= 0) {
138
+ // Prevent infinite recursion
139
+ console.warn('CSS variable resolution depth limit reached');
140
+ return value;
141
+ }
142
+
143
+ let resolved = value;
144
+ const matches = value.matchAll(VAR_FUNCTION_REGEX);
145
+
146
+ for (const match of matches) {
147
+ const fullMatch = match[0];
148
+ const varName = match[1];
149
+ const fallback = match[2]?.trim();
150
+
151
+ // Try to get the variable value
152
+ let replacement = customProperties.get(varName);
153
+
154
+ // If variable not found, use fallback
155
+ if (replacement === undefined) {
156
+ if (fallback !== undefined) {
157
+ replacement = fallback;
158
+ } else {
159
+ // If no fallback, leave the var() call as-is (invalid)
160
+ console.warn(`CSS variable ${varName} not found and no fallback provided`);
161
+ continue;
162
+ }
163
+ }
164
+
165
+ // Replace this var() call
166
+ resolved = resolved.replace(fullMatch, replacement);
167
+ }
168
+
169
+ // Recursively resolve any nested var() calls
170
+ if (containsVariableReference(resolved)) {
171
+ resolved = resolveVariableReferences(resolved, customProperties, maxDepth - 1);
172
+ }
173
+
174
+ return resolved.trim();
175
+ }
176
+
177
+ /**
178
+ * Apply custom properties from declarations to the custom properties map
179
+ * Extracts all properties that start with "--"
180
+ */
181
+ export function extractCustomProperties(
182
+ declarations: Record<string, string>
183
+ ): CustomPropertiesMap {
184
+ const customProps = new CustomPropertiesMap();
185
+
186
+ for (const [property, value] of Object.entries(declarations)) {
187
+ if (property.startsWith('--')) {
188
+ customProps.set(property, value);
189
+ }
190
+ }
191
+
192
+ return customProps;
193
+ }
194
+
195
+ /**
196
+ * Resolve all property values in declarations by replacing var() references
197
+ */
198
+ export function resolveDeclarationsWithVariables(
199
+ declarations: Record<string, string>,
200
+ customProperties: CustomPropertiesMap
201
+ ): Record<string, string> {
202
+ const resolved: Record<string, string> = {};
203
+
204
+ for (const [property, value] of Object.entries(declarations)) {
205
+ // Don't resolve custom properties themselves (they can contain var())
206
+ if (property.startsWith('--')) {
207
+ resolved[property] = value;
208
+ } else {
209
+ // Resolve var() references in regular properties
210
+ resolved[property] = resolveVariableReferences(value, customProperties);
211
+ }
212
+ }
213
+
214
+ return resolved;
215
+ }
@@ -0,0 +1,127 @@
1
+ export enum Display {
2
+ Block = "block",
3
+ Inline = "inline",
4
+ InlineBlock = "inline-block",
5
+ Flex = "flex",
6
+ InlineFlex = "inline-flex",
7
+ Grid = "grid",
8
+ InlineGrid = "inline-grid",
9
+ Table = "table",
10
+ InlineTable = "inline-table",
11
+ TableRowGroup = "table-row-group",
12
+ TableHeaderGroup = "table-header-group",
13
+ TableFooterGroup = "table-footer-group",
14
+ TableRow = "table-row",
15
+ TableCell = "table-cell",
16
+ TableCaption = "table-caption",
17
+ ListItem = "list-item",
18
+ None = "none",
19
+ FlowRoot = "flow-root",
20
+ }
21
+
22
+ export enum Position {
23
+ Static = "static",
24
+ Relative = "relative",
25
+ Absolute = "absolute",
26
+ Fixed = "fixed",
27
+ Sticky = "sticky",
28
+ }
29
+
30
+ export enum FloatMode {
31
+ None = "none",
32
+ Left = "left",
33
+ Right = "right",
34
+ }
35
+
36
+ export enum ClearMode {
37
+ None = "none",
38
+ Left = "left",
39
+ Right = "right",
40
+ Both = "both",
41
+ InlineStart = "inline-start",
42
+ InlineEnd = "inline-end",
43
+ }
44
+
45
+ export enum OverflowMode {
46
+ Visible = "visible",
47
+ Hidden = "hidden",
48
+ Scroll = "scroll",
49
+ Auto = "auto",
50
+ Clip = "clip",
51
+ }
52
+
53
+ export enum WhiteSpace {
54
+ Normal = "normal",
55
+ NoWrap = "nowrap",
56
+ Pre = "pre",
57
+ PreWrap = "pre-wrap",
58
+ PreLine = "pre-line",
59
+ }
60
+
61
+ export enum TextWrap {
62
+ Wrap = "wrap",
63
+ NoWrap = "nowrap",
64
+ Balance = "balance",
65
+ }
66
+
67
+ export enum AlignItems {
68
+ Stretch = "stretch",
69
+ FlexStart = "flex-start",
70
+ FlexEnd = "flex-end",
71
+ Center = "center",
72
+ Baseline = "baseline",
73
+ }
74
+
75
+ export enum JustifyContent {
76
+ FlexStart = "flex-start",
77
+ FlexEnd = "flex-end",
78
+ Center = "center",
79
+ SpaceBetween = "space-between",
80
+ SpaceAround = "space-around",
81
+ SpaceEvenly = "space-evenly",
82
+ Start = "start",
83
+ End = "end",
84
+ Left = "left",
85
+ Right = "right",
86
+ }
87
+
88
+ export enum AlignContent {
89
+ Stretch = "stretch",
90
+ FlexStart = "flex-start",
91
+ FlexEnd = "flex-end",
92
+ Center = "center",
93
+ SpaceBetween = "space-between",
94
+ SpaceAround = "space-around",
95
+ SpaceEvenly = "space-evenly",
96
+ }
97
+
98
+ export enum VerticalAlign {
99
+ Baseline = "baseline",
100
+ Top = "top",
101
+ Middle = "middle",
102
+ Bottom = "bottom",
103
+ TextTop = "text-top",
104
+ TextBottom = "text-bottom",
105
+ Sub = "sub",
106
+ Super = "super",
107
+ }
108
+
109
+ export enum WritingMode {
110
+ HorizontalTb = "horizontal-tb",
111
+ VerticalRl = "vertical-rl",
112
+ VerticalLr = "vertical-lr",
113
+ }
114
+
115
+ export enum TableLayoutMode {
116
+ Auto = "auto",
117
+ Fixed = "fixed",
118
+ }
119
+
120
+ export enum BorderModel {
121
+ Separate = "separate",
122
+ Collapse = "collapse",
123
+ }
124
+
125
+ export type FlexDirection = "row" | "row-reverse" | "column" | "column-reverse";
126
+
127
+ export type AlignSelfValue = "auto" | AlignItems;
@@ -0,0 +1,233 @@
1
+ /**
2
+ * CSS @font-face parsing utilities.
3
+ *
4
+ * This module handles parsing of CSS @font-face rules from stylesheets,
5
+ * including font family list parsing, weight matching, and source extraction.
6
+ */
7
+
8
+ import type { CSSFontFace, StyleSheets } from "../pdf/types.js";
9
+ import { normalizeFontWeight, parseFontWeightValue } from "./font-weight.js";
10
+
11
+ /**
12
+ * Result of parsing font faces from stylesheets.
13
+ */
14
+ export interface ParsedFontFaceMap {
15
+ /**
16
+ * Map of normalized font family names to their @font-face definitions.
17
+ */
18
+ facesByFamily: Map<string, CSSFontFace[]>;
19
+ }
20
+
21
+ /**
22
+ * Parse all @font-face definitions from stylesheets into a family-keyed map.
23
+ *
24
+ * @param stylesheets - StyleSheets containing font face definitions
25
+ * @returns Map of font families to their face definitions
26
+ *
27
+ * @example
28
+ * ```typescript
29
+ * const { facesByFamily } = parseFontFaces(stylesheets);
30
+ * const tinosFaces = facesByFamily.get("tinos");
31
+ * ```
32
+ */
33
+ export function parseFontFaces(stylesheets: StyleSheets): ParsedFontFaceMap {
34
+ const facesByFamily = new Map<string, CSSFontFace[]>();
35
+
36
+ for (const face of stylesheets.fontFaces ?? []) {
37
+ const family = normalizeToken(face.family);
38
+ if (!family) {
39
+ continue;
40
+ }
41
+ const list = facesByFamily.get(family) ?? [];
42
+ list.push(face);
43
+ facesByFamily.set(family, list);
44
+ }
45
+
46
+ return { facesByFamily };
47
+ }
48
+
49
+ /**
50
+ * Select the best @font-face from a list based on requested weight.
51
+ *
52
+ * Chooses the face with the smallest weight difference from the requested weight.
53
+ * Falls back to the first face if no weight information is available.
54
+ *
55
+ * @param faces - Array of font faces to select from
56
+ * @param requestedWeight - Desired font weight (100-900)
57
+ * @returns Best matching font face, or undefined if faces array is empty
58
+ */
59
+ export function selectFaceForWeight(faces: CSSFontFace[], requestedWeight: number): CSSFontFace | undefined {
60
+ let bestFace: CSSFontFace | undefined;
61
+ let smallestDiff = Number.POSITIVE_INFINITY;
62
+
63
+ for (const face of faces) {
64
+ const faceWeight = parseFaceWeight(face.weight, requestedWeight);
65
+ if (faceWeight === null) {
66
+ if (!bestFace) {
67
+ bestFace = face;
68
+ }
69
+ continue;
70
+ }
71
+ const diff = Math.abs(faceWeight - requestedWeight);
72
+ if (diff < smallestDiff) {
73
+ smallestDiff = diff;
74
+ bestFace = face;
75
+ }
76
+ }
77
+
78
+ return bestFace ?? faces[0];
79
+ }
80
+
81
+ /**
82
+ * Parse a font family list (comma-separated) into individual family names.
83
+ *
84
+ * Handles quoted family names and filters out empty tokens.
85
+ *
86
+ * @param value - Comma-separated font family string
87
+ * @returns Array of normalized family names
88
+ *
89
+ * @example
90
+ * ```typescript
91
+ * parseFamilyList("'Times New Roman', Times, serif")
92
+ * // => ["Times New Roman", "Times", "serif"]
93
+ * ```
94
+ */
95
+ export function parseFamilyList(value: string | undefined): string[] {
96
+ if (!value) {
97
+ return [];
98
+ }
99
+ return value
100
+ .split(",")
101
+ .map((token) => stripQuotes(token.trim()))
102
+ .filter((token) => token.length > 0);
103
+ }
104
+
105
+ /**
106
+ * Check if a font style is italic or oblique.
107
+ *
108
+ * @param style - Font style value
109
+ * @returns True if style is "italic" or "oblique" (case-insensitive)
110
+ */
111
+ export function isItalicStyle(style: string | undefined): boolean {
112
+ if (!style) {
113
+ return false;
114
+ }
115
+ const normalized = style.toLowerCase();
116
+ return normalized === "italic" || normalized === "oblique";
117
+ }
118
+
119
+ /**
120
+ * Normalize a font family or token to lowercase and remove quotes.
121
+ *
122
+ * @param value - Token to normalize
123
+ * @returns Normalized lowercase string, or empty string if value is undefined
124
+ */
125
+ export function normalizeToken(value: string | undefined): string {
126
+ if (!value) {
127
+ return "";
128
+ }
129
+ return stripQuotes(value).trim().toLowerCase();
130
+ }
131
+
132
+ /**
133
+ * Remove surrounding quotes from a string.
134
+ *
135
+ * Handles both single and double quotes.
136
+ *
137
+ * @param value - String potentially wrapped in quotes
138
+ * @returns String with quotes removed, or original value if not quoted
139
+ */
140
+ export function stripQuotes(value: string): string {
141
+ if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
142
+ return value.slice(1, -1);
143
+ }
144
+ return value;
145
+ }
146
+
147
+ /**
148
+ * Extract base font name from a @font-face definition using aliases.
149
+ *
150
+ * Checks both local() sources and the family name against the provided alias map.
151
+ *
152
+ * @param face - @font-face definition
153
+ * @param aliases - Map of font names to their aliases
154
+ * @returns Base font name if found, null otherwise
155
+ */
156
+ export function baseFontFromFace(face: CSSFontFace, aliases: ReadonlyMap<string, string>): string | null {
157
+ const localName = extractLocalSource(face.src);
158
+ if (localName) {
159
+ const normalized = normalizeToken(localName);
160
+ const alias = aliases.get(normalized);
161
+ if (alias) {
162
+ return alias;
163
+ }
164
+ }
165
+ const familyAlias = aliases.get(normalizeToken(face.family));
166
+ if (familyAlias) {
167
+ return familyAlias;
168
+ }
169
+ return null;
170
+ }
171
+
172
+ /**
173
+ * Extract the first local() font source from a src list.
174
+ *
175
+ * @param srcList - Array of font source strings (e.g., ["local('Arial')", "url(...))"])
176
+ * @returns Local font name if found, null otherwise
177
+ *
178
+ * @example
179
+ * ```typescript
180
+ * extractLocalSource(["local('Arial')", "url(arial.woff2)"])
181
+ * // => "Arial"
182
+ * ```
183
+ */
184
+ export function extractLocalSource(srcList: string[]): string | null {
185
+ for (const src of srcList) {
186
+ const match = src.match(/local\(([^)]+)\)/i);
187
+ if (match) {
188
+ return stripQuotes(match[1].trim());
189
+ }
190
+ }
191
+ return null;
192
+ }
193
+
194
+ /**
195
+ * Parse a @font-face weight value into a numeric weight.
196
+ *
197
+ * Handles:
198
+ * - Numeric weights (already normalized)
199
+ * - String values ("normal", "bold", "400", etc.)
200
+ * - Multiple weights (chooses closest to fallback)
201
+ *
202
+ * @param value - Weight value from @font-face
203
+ * @param fallback - Fallback weight to use for choosing from multiple values
204
+ * @returns Normalized numeric weight (100-900), or null if unparseable
205
+ */
206
+ function parseFaceWeight(value: string | number | undefined, fallback: number): number | null {
207
+ if (typeof value === "number") {
208
+ return normalizeFontWeight(value);
209
+ }
210
+ if (typeof value !== "string") {
211
+ return null;
212
+ }
213
+ const parts = value.split(/\s+/).filter(Boolean);
214
+ if (parts.length > 1) {
215
+ const parsedWeights = parts
216
+ .map((part) => parseFontWeightValue(part, fallback))
217
+ .filter((weight): weight is number => weight !== undefined)
218
+ .map((weight) => normalizeFontWeight(weight));
219
+ if (parsedWeights.length === 0) {
220
+ return null;
221
+ }
222
+ return parsedWeights.reduce((closest, candidate) => {
223
+ const candidateDiff = Math.abs(candidate - fallback);
224
+ const closestDiff = Math.abs(closest - fallback);
225
+ return candidateDiff < closestDiff ? candidate : closest;
226
+ }, parsedWeights[0]);
227
+ }
228
+ const parsed = parseFontWeightValue(value, fallback);
229
+ if (parsed === undefined) {
230
+ return null;
231
+ }
232
+ return normalizeFontWeight(parsed);
233
+ }
@@ -0,0 +1,65 @@
1
+ const FONT_WEIGHT_STEPS = [100, 200, 300, 400, 500, 600, 700, 800, 900] as const;
2
+
3
+ type FontWeightDirection = "bolder" | "lighter";
4
+
5
+ export function normalizeFontWeight(weight: number | undefined): number {
6
+ if (!Number.isFinite(weight ?? NaN)) {
7
+ return 400;
8
+ }
9
+ const clamped = Math.min(900, Math.max(100, weight as number));
10
+ let nearest = 400;
11
+ let smallestDiff = Math.abs(nearest - clamped);
12
+ for (const step of FONT_WEIGHT_STEPS) {
13
+ const diff = Math.abs(step - clamped);
14
+ if (diff < smallestDiff) {
15
+ smallestDiff = diff;
16
+ nearest = step;
17
+ }
18
+ }
19
+ return nearest;
20
+ }
21
+
22
+ export function stepFontWeight(weight: number | undefined, direction: FontWeightDirection): number {
23
+ const normalized = normalizeFontWeight(weight);
24
+ const index = FONT_WEIGHT_STEPS.indexOf(normalized as (typeof FONT_WEIGHT_STEPS)[number]);
25
+ if (index === -1) {
26
+ return normalized;
27
+ }
28
+ if (direction === "bolder") {
29
+ return FONT_WEIGHT_STEPS[Math.min(FONT_WEIGHT_STEPS.length - 1, index + 1)];
30
+ }
31
+ return FONT_WEIGHT_STEPS[Math.max(0, index - 1)];
32
+ }
33
+
34
+ export function parseFontWeightValue(value: string, inherited?: number): number | undefined {
35
+ const token = value.trim().toLowerCase();
36
+ switch (token) {
37
+ case "normal":
38
+ return 400;
39
+ case "bold":
40
+ return 700;
41
+ case "bolder":
42
+ return stepFontWeight(inherited, "bolder");
43
+ case "lighter":
44
+ return stepFontWeight(inherited, "lighter");
45
+ case "inherit":
46
+ return inherited !== undefined ? normalizeFontWeight(inherited) : undefined;
47
+ case "initial":
48
+ return 400;
49
+ default: {
50
+ const numeric = Number.parseFloat(token);
51
+ if (Number.isFinite(numeric)) {
52
+ return normalizeFontWeight(numeric);
53
+ }
54
+ return undefined;
55
+ }
56
+ }
57
+ }
58
+
59
+ export function fontWeightCacheKey(weight: number | undefined): string {
60
+ return normalizeFontWeight(weight).toString();
61
+ }
62
+
63
+ export function isBoldFontWeight(weight: number | undefined): boolean {
64
+ return normalizeFontWeight(weight) >= 600;
65
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Parse inline style attribute
3
+ * Responsibility: Convert style string to property map
4
+ */
5
+ export function parseInlineStyle(style: string): Record<string, string> {
6
+ const properties: Record<string, string> = {};
7
+
8
+ if (!style || typeof style !== 'string') {
9
+ return properties;
10
+ }
11
+
12
+ const declarations = style.split(';');
13
+
14
+ for (const decl of declarations) {
15
+ const colonIndex = decl.indexOf(':');
16
+ if (colonIndex === -1) continue;
17
+
18
+ const property = decl.substring(0, colonIndex).trim();
19
+ const value = decl.substring(colonIndex + 1).trim();
20
+
21
+ if (property && value) {
22
+ properties[property] = value;
23
+ }
24
+ }
25
+
26
+ return properties;
27
+ }