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,395 @@
1
+ import type { PdfObjectRef, PdfDocument } from "../primitives/pdf-document.js";
2
+ import { parseTtfBuffer } from "./ttf-lite.js";
3
+ import type { FontFaceDef, FontConfig, TtfFontMetrics } from "../../types/fonts.js";
4
+ import { log } from "../../logging/debug.js";
5
+ import { normalizeFontWeight } from "../../css/font-weight.js";
6
+ import { detectFontFormat } from "../../fonts/detector.js";
7
+ import { reconstructTtf } from "../../fonts/utils/ttf-reconstructor.js";
8
+ import { computeWidths } from "./widths.js";
9
+ import { createToUnicodeCMapText } from "./to-unicode.js";
10
+ import { BASE_FONT_ALIASES, GENERIC_FAMILIES } from "./font-config.js";
11
+
12
+ export { computeWidths } from "./widths.js";
13
+ export { createToUnicodeCMapText } from "./to-unicode.js";
14
+
15
+ const TYPICAL_STEM_V = 80;
16
+
17
+ export interface EmbeddedFont {
18
+ readonly resourceName: string;
19
+ readonly ref: PdfObjectRef;
20
+ readonly baseFont: string;
21
+ readonly metrics: TtfFontMetrics;
22
+ readonly subset: Uint8Array;
23
+ }
24
+
25
+ interface FontDescriptor {
26
+ readonly Type: "/FontDescriptor";
27
+ readonly FontName: string;
28
+ readonly Flags: number;
29
+ readonly FontBBox: readonly [number, number, number, number];
30
+ readonly ItalicAngle: number;
31
+ readonly Ascent: number;
32
+ readonly Descent: number;
33
+ readonly CapHeight: number;
34
+ readonly XHeight: number;
35
+ readonly StemV: number;
36
+ readonly FontFile2: PdfObjectRef;
37
+ }
38
+
39
+ interface FontDictionary {
40
+ readonly Type: "/Font";
41
+ readonly Subtype: "/Type0";
42
+ readonly BaseFont: string;
43
+ readonly Encoding: "/Identity-H";
44
+ readonly DescendantFonts: readonly [PdfObjectRef];
45
+ readonly ToUnicode: PdfObjectRef;
46
+ }
47
+
48
+ interface CIDFontDictionary {
49
+ readonly Type: "/Font";
50
+ readonly Subtype: "/CIDFontType2";
51
+ readonly BaseFont: string;
52
+ readonly CIDSystemInfo: {
53
+ readonly Registry: string; // emitted as literal strings "(Adobe)"
54
+ readonly Ordering: string; // emitted as literal strings "(Identity)"
55
+ readonly Supplement: 0;
56
+ };
57
+ readonly FontDescriptor: PdfObjectRef;
58
+ readonly DW?: number;
59
+ readonly W: readonly (number | readonly number[])[];
60
+ readonly CIDToGIDMap: PdfObjectRef | string;
61
+ }
62
+
63
+ export class FontEmbedder {
64
+ private embeddedFonts = new Map<string, EmbeddedFont>();
65
+ private faceMetrics = new Map<string, TtfFontMetrics>();
66
+
67
+ constructor(private readonly config: FontConfig, private readonly doc: PdfDocument) { }
68
+
69
+ async initialize(): Promise<void> {
70
+ for (const face of this.config.fontFaceDefs) {
71
+ if (!face.data) {
72
+ log("font", "error", `Missing data for font ${face.name}`);
73
+ continue;
74
+ }
75
+ try {
76
+ let fontData = new Uint8Array(face.data);
77
+ const format = detectFontFormat(fontData);
78
+ let ttfBuffer: ArrayBuffer;
79
+ if (format === "woff") {
80
+ const { decodeWoff } = await import("../../fonts/woff/decoder.js");
81
+ const decoded = await decodeWoff(fontData);
82
+ ttfBuffer = reconstructTtf(decoded);
83
+ fontData = new Uint8Array(ttfBuffer);
84
+ (face as any).data = ttfBuffer;
85
+ } else if (format === "woff2") {
86
+ const { decodeWoff2 } = await import("../../fonts/woff2/decoder.js");
87
+ const decoded = await decodeWoff2(fontData);
88
+ // Copy into a fresh ArrayBuffer to avoid SharedArrayBuffer unions
89
+ const ttfCopy = decoded.ttfBuffer.slice();
90
+ ttfBuffer = ttfCopy.buffer;
91
+ fontData = new Uint8Array(ttfBuffer);
92
+ (face as any).data = ttfBuffer;
93
+ } else {
94
+ ttfBuffer = face.data!;
95
+ }
96
+
97
+ const metrics = parseTtfBuffer(ttfBuffer);
98
+ this.faceMetrics.set(face.name, metrics);
99
+ } catch (error) {
100
+ log("font", "error", `Failed to load font ${face.name}`, { error: error instanceof Error ? error.message : String(error) });
101
+ }
102
+ }
103
+ }
104
+
105
+ ensureFont(familyStack: string[], fontWeight?: number, fontStyle?: string): EmbeddedFont | null {
106
+ const targetWeight = normalizeFontWeight(fontWeight);
107
+ const wantsItalic = isItalic(fontStyle);
108
+
109
+ for (const family of familyStack) {
110
+ const normalizedFamily = family.toLowerCase().trim();
111
+ const candidates = this.config.fontFaceDefs.filter((f) => {
112
+ return (f.family || "").toLowerCase().trim() === normalizedFamily;
113
+ });
114
+
115
+ if (candidates.length === 0) {
116
+ continue;
117
+ }
118
+
119
+ const face = pickFaceByWeight(candidates, targetWeight, wantsItalic);
120
+ if (!face) {
121
+ continue;
122
+ }
123
+
124
+ // Use normalized face name as key to avoid accidental mismatches due to spacing/case
125
+ const faceKey = (face.name || "").toLowerCase().trim();
126
+ const existing = this.embeddedFonts.get(face.name) ?? this.embeddedFonts.get(faceKey);
127
+ if (existing) return existing;
128
+
129
+ const embedded = this.embedFont(face);
130
+ if (embedded) {
131
+ // store with both canonical and normalized keys for resilient lookups
132
+ this.embeddedFonts.set(face.name, embedded);
133
+ this.embeddedFonts.set(faceKey, embedded);
134
+ return embedded;
135
+ }
136
+ }
137
+
138
+ return null;
139
+ }
140
+
141
+ private embedFont(face: FontFaceDef): EmbeddedFont | null {
142
+ const metrics = this.faceMetrics.get(face.name);
143
+ if (!metrics) return null;
144
+ const unitsPerEm = metrics.metrics.unitsPerEm;
145
+ const scaleTo1000 = (v: number) => Math.round((v / unitsPerEm) * 1000);
146
+
147
+ let fontBBox: [number, number, number, number];
148
+ if (metrics.headBBox) {
149
+ const hb = metrics.headBBox;
150
+ fontBBox = [scaleTo1000(hb[0]), scaleTo1000(hb[1]), scaleTo1000(hb[2]), scaleTo1000(hb[3])];
151
+ } else {
152
+ // Fallback if headBBox is missing
153
+ fontBBox = [-1000, -1000, 1000, 1000];
154
+ }
155
+
156
+ // Register the font file stream
157
+ // We need the full font data here. In the initialize method we updated face.data
158
+ // but here we need to access it.
159
+ // Since we can't easily change the interface, we'll assume face.data is the source.
160
+ // If it was converted to TTF in initialize, face.data (casted) holds the TTF buffer.
161
+ const fullFontData = new Uint8Array(face.data!);
162
+ // FontFile2 streams require only /Length (PdfDocument adds it); avoid Type1-specific Length1 headers.
163
+ const fontFileRef = this.doc.registerStream(fullFontData, {});
164
+
165
+ const fontDescriptor: FontDescriptor = {
166
+ Type: "/FontDescriptor",
167
+ FontName: `/${face.name}`,
168
+ Flags: computePdfFlagsFromFace(face),
169
+ FontBBox: fontBBox,
170
+ ItalicAngle: face.style === "italic" ? -12 : 0,
171
+ Ascent: scaleTo1000(metrics.metrics.ascender),
172
+ Descent: scaleTo1000(metrics.metrics.descender),
173
+ CapHeight: scaleTo1000(metrics.metrics.capHeight),
174
+ XHeight: scaleTo1000(metrics.metrics.xHeight),
175
+ StemV: TYPICAL_STEM_V,
176
+ FontFile2: fontFileRef
177
+ };
178
+
179
+ const fontDescriptorRef = this.doc.register(fontDescriptor);
180
+
181
+ // Compute DW and compressed W
182
+ const { DW, W } = computeWidths(metrics);
183
+
184
+ // Create CID font dictionary (include DW)
185
+ // CID fonts must declare string-valued CIDSystemInfo entries per PDF spec.
186
+ const cidFontDict: CIDFontDictionary = {
187
+ Type: "/Font",
188
+ Subtype: "/CIDFontType2",
189
+ BaseFont: `/${face.name}`,
190
+ CIDSystemInfo: {
191
+ Registry: "(Adobe)",
192
+ Ordering: "(Identity)",
193
+ Supplement: 0
194
+ },
195
+ FontDescriptor: fontDescriptorRef,
196
+ DW,
197
+ W,
198
+ // Rely on built-in Identity CIDToGID mapping to keep font dictionaries simple/compatible.
199
+ CIDToGIDMap: "/Identity"
200
+ };
201
+
202
+ const cidFontRef = this.doc.register(cidFontDict);
203
+
204
+ // Create Unicode mapping (ToUnicode CMap)
205
+ const toUnicodeRef = this.createToUnicodeCMap(metrics);
206
+
207
+ // Create Type0 font dictionary
208
+ // Per PDF spec, Type0 BaseFont should include the CMap suffix (e.g. "-Identity-H")
209
+ // to make the composite font name unambiguous for parsers/renderers.
210
+ const type0Font: FontDictionary = {
211
+ Type: "/Font",
212
+ Subtype: "/Type0",
213
+ BaseFont: `/${face.name}-Identity-H`,
214
+ Encoding: "/Identity-H",
215
+ DescendantFonts: [cidFontRef],
216
+ ToUnicode: toUnicodeRef
217
+ };
218
+
219
+ const fontRef = this.doc.register(type0Font);
220
+
221
+ return {
222
+ resourceName: `F${this.embeddedFonts.size + 1}`,
223
+ ref: fontRef,
224
+ baseFont: face.name,
225
+ metrics,
226
+ subset: fullFontData
227
+ };
228
+ }
229
+
230
+
231
+
232
+ private createToUnicodeCMap(metrics: TtfFontMetrics, _uniqueUnicodes: number[] = []): PdfObjectRef {
233
+ // Build inverse mapping gid -> unicode (pick first unicode when multiple map to same gid)
234
+ const unicodeMap = metrics.cmap["unicodeMap"] as Map<number, number>;
235
+ log("font", "debug", "createToUnicodeCMap - unicodeMap size", { size: unicodeMap.size });
236
+
237
+ // Sample first few entries for debugging
238
+ const samples: Array<{ unicode: number, char: string, gid: number }> = [];
239
+ let count = 0;
240
+ for (const [unicode, gid] of unicodeMap.entries()) {
241
+ if (count < 10) {
242
+ samples.push({ unicode, char: String.fromCodePoint(unicode), gid });
243
+ }
244
+ count++;
245
+ }
246
+ log("font", "debug", "createToUnicodeCMap - sample entries", { samples });
247
+
248
+ const gidToUni = new Map<number, number>();
249
+ for (const [unicode, gid] of unicodeMap.entries()) {
250
+ if (!gidToUni.has(gid)) gidToUni.set(gid, unicode);
251
+ }
252
+
253
+ // Debug: log sample gid->unicode mappings
254
+ const gidSamples: Array<{ gid: number, unicode: number, char: string }> = [];
255
+ let gidCount = 0;
256
+ for (const [gid, unicode] of gidToUni.entries()) {
257
+ if (gidCount < 20) {
258
+ gidSamples.push({ gid, unicode, char: String.fromCodePoint(unicode) });
259
+ }
260
+ gidCount++;
261
+ }
262
+ log("font", "debug", "createToUnicodeCMap - gid->unicode sample", { samples: gidSamples });
263
+
264
+ const entries = Array.from(gidToUni.entries())
265
+ .map(([gid, unicode]) => ({ gid, unicode }))
266
+ .sort((a, b) => a.gid - b.gid);
267
+
268
+ const cmapText = createToUnicodeCMapText(entries);
269
+ return this.doc.registerStream(new TextEncoder().encode(cmapText), {});
270
+ }
271
+
272
+
273
+ /**
274
+ * Return parsed TTF metrics for a loaded face by name or family, or null if not available.
275
+ * Exposed to allow rendering code to access outlines/metrics for embedding masks.
276
+ *
277
+ * The lookup tries:
278
+ * 1. Exact match by face name (e.g., "Tinos-Bold")
279
+ * 2. Match by family name (e.g., "Tinos") with weight/style matching
280
+ * 3. Case-insensitive match by family name
281
+ * 4. If input contains comma-separated font stack (e.g., "Tinos, serif"), tries each font
282
+ * 5. Uses BASE_FONT_ALIASES to resolve CSS font names to embedded fonts (e.g., "Times New Roman" → "Tinos")
283
+ *
284
+ * @param faceName - Font family name or font stack
285
+ * @param fontWeight - Optional font weight (100-900), defaults to 400
286
+ * @param fontStyle - Optional font style ('normal' | 'italic' | 'oblique'), defaults to 'normal'
287
+ */
288
+ public getMetrics(faceName: string, fontWeight: number = 400, fontStyle: string = "normal"): TtfFontMetrics | null {
289
+ // Try exact match first
290
+ const exact = this.faceMetrics.get(faceName);
291
+ if (exact) return exact;
292
+
293
+ const wantsItalic = fontStyle === "italic" || fontStyle === "oblique";
294
+
295
+ // Handle font-family stacks like "'Times New Roman', Times, serif"
296
+ const fontStack = faceName.split(",").map(f => f.trim().replace(/^["']|["']$/g, ""));
297
+
298
+ for (const fontName of fontStack) {
299
+ const normalizedQuery = fontName.toLowerCase().trim();
300
+
301
+ // Try exact match with this font name
302
+ const exactMatch = this.faceMetrics.get(fontName);
303
+ if (exactMatch) return exactMatch;
304
+
305
+ // Check if this is an alias that maps to an embedded font
306
+ const aliasedFont = BASE_FONT_ALIASES.get(normalizedQuery) || GENERIC_FAMILIES.get(normalizedQuery);
307
+ const targetFamily = aliasedFont ? aliasedFont.toLowerCase() : normalizedQuery;
308
+
309
+ // Collect all faces matching the family
310
+ const matchingFaces: FontFaceDef[] = [];
311
+ for (const face of this.config.fontFaceDefs) {
312
+ const faceFamily = (face.family || "").toLowerCase().trim();
313
+ if (faceFamily === targetFamily) {
314
+ matchingFaces.push(face);
315
+ }
316
+ }
317
+
318
+ // Pick the best face by weight and style
319
+ if (matchingFaces.length > 0) {
320
+ const bestFace = pickFaceByWeight(matchingFaces, fontWeight, wantsItalic);
321
+ if (bestFace) {
322
+ const metrics = this.faceMetrics.get(bestFace.name);
323
+ if (metrics) return metrics;
324
+ }
325
+ }
326
+ }
327
+
328
+ return null;
329
+ }
330
+ }
331
+
332
+ function pickFaceByWeight(faces: FontFaceDef[], requestedWeight: number, wantsItalic: boolean): FontFaceDef | null {
333
+ if (faces.length === 0) {
334
+ return null;
335
+ }
336
+ // Prefer faces that match the requested style; if none do, fall back to any style.
337
+ const styleFiltered = faces.filter((face) => isItalic(face.style) === wantsItalic);
338
+ const pool = styleFiltered.length > 0 ? styleFiltered : faces;
339
+
340
+ let bestFace = pool[0];
341
+ let bestDiff = Math.abs(normalizeFontWeight(bestFace.weight) - requestedWeight);
342
+ for (const face of pool) {
343
+ const normalized = normalizeFontWeight(face.weight);
344
+ const diff = Math.abs(normalized - requestedWeight);
345
+ if (diff < bestDiff) {
346
+ bestDiff = diff;
347
+ bestFace = face;
348
+ }
349
+ }
350
+ return bestFace;
351
+ }
352
+
353
+ function isItalic(style: string | undefined): boolean {
354
+ if (!style) return false;
355
+ const s = style.toLowerCase();
356
+ return s === "italic" || s === "oblique";
357
+ }
358
+
359
+ // Compute PDF Flags based on font family/style heuristics.
360
+ // See AGENTS.md notes for bits: Symbolic vs Nonsymbolic, Serif, Italic, etc.
361
+ function computePdfFlagsFromFace(face: FontFaceDef): number {
362
+ let flags = 0;
363
+ const family = (face.family || "").toLowerCase().trim();
364
+ const name = (face.name || "").toLowerCase().trim();
365
+ const style = (face.style || "").toLowerCase();
366
+
367
+ const isItalic = /italic|oblique/i.test(style);
368
+ const isSerif = /serif/i.test(family) || /serif/i.test(name);
369
+ const isSymbol = /symbol|dingbat|dingbats|zapfdingbats/i.test(family) || /symbol|dingbat|dingbats/i.test(name);
370
+
371
+ if (isSymbol) flags |= 1 << 2; // Symbolic
372
+ else flags |= 1 << 5; // Nonsymbolic
373
+
374
+ if (isSerif) flags |= 1 << 1; // Serif
375
+ if (isItalic) flags |= 1 << 6; // Italic
376
+
377
+ return flags;
378
+ }
379
+
380
+ export async function getEmbeddedFont(name: "NotoSans-Regular" | "DejaVuSans", doc: PdfDocument, config: FontConfig): Promise<EmbeddedFont | null> {
381
+ const embedder = new FontEmbedder(config, doc);
382
+ const face = config.fontFaceDefs.find(f => f.name === name);
383
+ if (!face) return null;
384
+
385
+ const existing = embedder['embeddedFonts'].get(name);
386
+ if (existing) return existing;
387
+
388
+ // @ts-ignore: TypeScript complains about private method, but this is intentional for testing
389
+ const embedded = embedder['embedFont'](face);
390
+ if (embedded) {
391
+ embedder['embeddedFonts'].set(name, embedded);
392
+ return embedded;
393
+ }
394
+ return null;
395
+ }
@@ -0,0 +1,190 @@
1
+ /**
2
+ * Font configuration and mapping data for PDF font resolution.
3
+ *
4
+ * This module centralizes all font alias mappings, generic family mappings,
5
+ * and Base14 font variant configurations used throughout the font registry system.
6
+ */
7
+
8
+ /**
9
+ * Base14 variant types supported by PDF standard fonts.
10
+ */
11
+ export type Base14Variant = "normal" | "italic" | "bold" | "boldItalic";
12
+
13
+ /**
14
+ * Base14 font family names.
15
+ */
16
+ export type Base14Family = "Helvetica" | "Times-Roman" | "Courier";
17
+
18
+ /**
19
+ * Variant configuration for a Base14 font family.
20
+ */
21
+ export interface Base14Variants {
22
+ normal: string;
23
+ italic: string;
24
+ bold: string;
25
+ boldItalic: string;
26
+ }
27
+
28
+ /**
29
+ * Maps common font family names to embedded font aliases.
30
+ *
31
+ * This enables fallback to built-in fonts when requested fonts aren't available.
32
+ * For example, "Arial" → "Arimo", "Times New Roman" → "Tinos"
33
+ */
34
+ export const BASE_FONT_ALIASES: ReadonlyMap<string, string> = new Map([
35
+ // Sans/UI families
36
+ ["helvetica", "Lato"],
37
+ ["arial", "Arimo"],
38
+ ["arial black", "Arimo"],
39
+ ["segoe ui", "Roboto"],
40
+ ["open sans", "Lato"],
41
+ ["calibri", "Roboto"],
42
+ ["roboto", "Roboto"],
43
+ ["arimo", "Arimo"],
44
+ ["lato", "Lato"],
45
+ ["noto sans", "Noto Sans"],
46
+ ["notosans-regular", "Noto Sans"],
47
+ ["dejavu sans", "DejaVu Sans"],
48
+
49
+ // Serif
50
+ ["times", "Tinos"],
51
+ ["times-roman", "Tinos"],
52
+ ["times new roman", "Tinos"],
53
+ ["georgia", "Tinos"],
54
+ ["garamond", "Tinos"],
55
+ ["baskerville", "Tinos"],
56
+
57
+ // Mono
58
+ ["courier", "Fira Code"],
59
+ ["courier new", "Fira Code"],
60
+ ["consolas", "Fira Code"],
61
+ ["menlo", "Fira Code"],
62
+ ["monaco", "Fira Code"],
63
+ ["source code pro", "Fira Code"],
64
+
65
+ // Decorative / cursive
66
+ ["comic sans", "Caveat"],
67
+ ["comic sans ms", "Caveat"],
68
+ ["fantasy", "Cinzel Decorative"],
69
+ ["cursive", "Caveat"],
70
+
71
+ // Emoji / math / symbols
72
+ ["noto emoji", "Noto Emoji"],
73
+ ["noto color emoji", "Noto Emoji"],
74
+ ["segoe ui emoji", "Noto Emoji"],
75
+ ["apple color emoji", "Noto Emoji"],
76
+ ["twemoji", "Noto Emoji"],
77
+ ["cambria math", "STIX Two Math"],
78
+ ["stix two math", "STIX Two Math"],
79
+ ["math", "STIX Two Math"],
80
+ ["symbol", "Symbol"],
81
+ ["zapfdingbats", "ZapfDingbats"],
82
+ ]);
83
+
84
+ /**
85
+ * Maps CSS generic font families to specific embedded fonts.
86
+ *
87
+ * Used when no specific font family is requested or as a final fallback.
88
+ */
89
+ export const GENERIC_FAMILIES: ReadonlyMap<string, string> = new Map([
90
+ ["serif", "Tinos"],
91
+ ["sans-serif", "Lato"],
92
+ ["monospace", "Fira Code"],
93
+ ["system-ui", "Roboto"],
94
+ ["cursive", "Caveat"],
95
+ ["fantasy", "Cinzel Decorative"],
96
+ ["emoji", "Noto Emoji"],
97
+ ["math", "STIX Two Math"],
98
+ ]);
99
+
100
+ /**
101
+ * Maps common font family names to Base14 standard font fallbacks.
102
+ *
103
+ * Used when font embedding is disabled or not available.
104
+ */
105
+ export const BASE14_FALLBACKS: ReadonlyMap<string, string> = new Map([
106
+ ["times", "Times-Roman"],
107
+ ["times-roman", "Times-Roman"],
108
+ ["times new roman", "Times-Roman"],
109
+ ["georgia", "Times-Roman"],
110
+ ["garamond", "Times-Roman"],
111
+ ["baskerville", "Times-Roman"],
112
+ ["serif", "Times-Roman"],
113
+ ["helvetica", "Helvetica"],
114
+ ["arial", "Helvetica"],
115
+ ["segoe ui", "Helvetica"],
116
+ ["open sans", "Helvetica"],
117
+ ["calibri", "Helvetica"],
118
+ ["sans-serif", "Helvetica"],
119
+ ["system-ui", "Helvetica"],
120
+ ["courier", "Courier"],
121
+ ["courier new", "Courier"],
122
+ ["monospace", "Courier"],
123
+ ]);
124
+
125
+ /**
126
+ * Base14 font family variants mapping.
127
+ *
128
+ * Defines the standard variant names for each Base14 font family.
129
+ * Used to apply weight/style transformations to Base14 fonts.
130
+ */
131
+ export const BASE14_FAMILY_VARIANTS: Record<Base14Family, Base14Variants> = {
132
+ "Helvetica": {
133
+ normal: "Helvetica",
134
+ italic: "Helvetica-Oblique",
135
+ bold: "Helvetica-Bold",
136
+ boldItalic: "Helvetica-BoldOblique",
137
+ },
138
+ "Times-Roman": {
139
+ normal: "Times-Roman",
140
+ italic: "Times-Italic",
141
+ bold: "Times-Bold",
142
+ boldItalic: "Times-BoldItalic",
143
+ },
144
+ "Courier": {
145
+ normal: "Courier",
146
+ italic: "Courier-Oblique",
147
+ bold: "Courier-Bold",
148
+ boldItalic: "Courier-BoldOblique",
149
+ },
150
+ } as const;
151
+
152
+ /**
153
+ * Reverse lookup map: Base14 variant name → family + variant type.
154
+ *
155
+ * Built automatically from BASE14_FAMILY_VARIANTS.
156
+ * Used to detect which Base14 family a font belongs to.
157
+ */
158
+ export const BASE14_VARIANT_LOOKUP: ReadonlyMap<string, { family: Base14Family; variant: Base14Variant }> = (() => {
159
+ const lookup = new Map<string, { family: Base14Family; variant: Base14Variant }>();
160
+
161
+ for (const [family, variants] of Object.entries(BASE14_FAMILY_VARIANTS) as Array<[Base14Family, Record<Base14Variant, string>]>) {
162
+ for (const [variant, name] of Object.entries(variants) as Array<[Base14Variant, string]>) {
163
+ lookup.set(name.toLowerCase(), { family, variant });
164
+ }
165
+ }
166
+
167
+ return lookup;
168
+ })();
169
+
170
+ /**
171
+ * Detect the Base14 family for a given font name.
172
+ *
173
+ * @param baseFont - Font name to check
174
+ * @returns Base14 family name if found, null otherwise
175
+ */
176
+ export function detectBase14Family(baseFont: string): Base14Family | null {
177
+ const entry = BASE14_VARIANT_LOOKUP.get(baseFont.toLowerCase());
178
+ return entry ? entry.family : null;
179
+ }
180
+
181
+ /**
182
+ * Classify the variant type of a Base14 font.
183
+ *
184
+ * @param baseFont - Font name to check
185
+ * @returns Variant type (normal, italic, bold, boldItalic)
186
+ */
187
+ export function classifyBase14Variant(baseFont: string): Base14Variant {
188
+ const entry = BASE14_VARIANT_LOOKUP.get(baseFont.toLowerCase());
189
+ return entry ? entry.variant : "normal";
190
+ }