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,127 @@
1
+ import type { BitmapMask } from "./rasterizer.js";
2
+ import { flattenOutline, rasterizeContours } from "./rasterizer.js";
3
+ import type { TtfFontMetrics } from "../../types/fonts.js";
4
+ import type { GlyphOutlineCmd } from "../../types/fonts.js";
5
+ import { globalGlyphAtlas } from "./glyph-atlas.js";
6
+ import type { AtlasPlacement } from "./glyph-atlas.js";
7
+ import { blurAlpha } from "./blur.js";
8
+
9
+ /**
10
+ * Glyph mask cache with simple LRU eviction.
11
+ *
12
+ * - Keyed by (unitsPerEm + headBBox) | gid | fontSizePx | supersample
13
+ * - Stores BitmapMask values (alpha-only or RGBA depending on usage)
14
+ * - Default maxEntries set to 500 (configurable)
15
+ *
16
+ * This keeps the previous helper behavior but adds a bounded cache to limit memory.
17
+ */
18
+
19
+ type CacheKey = string;
20
+
21
+ let maxEntries = 500;
22
+ const glyphMaskCache = new Map<CacheKey, BitmapMask>();
23
+ // store atlas placements per cached key to allow later atlas-based rendering
24
+ const glyphAtlasPlacements = new Map<CacheKey, AtlasPlacement | null>();
25
+
26
+ export function setGlyphMaskCacheMaxEntries(n: number) {
27
+ if (!Number.isFinite(n) || n <= 0) return;
28
+ maxEntries = Math.max(1, Math.floor(n));
29
+ // Trim if needed
30
+ trimCacheIfNeeded();
31
+ }
32
+
33
+ function trimCacheIfNeeded() {
34
+ while (glyphMaskCache.size > maxEntries) {
35
+ // Evict oldest entry (Map preserves insertion order)
36
+ const oldestKey = glyphMaskCache.keys().next().value;
37
+ if (!oldestKey) break;
38
+ glyphMaskCache.delete(oldestKey);
39
+ }
40
+ }
41
+
42
+ function makeKey(metrics: TtfFontMetrics, gid: number, fontSizePx: number, supersample = 4, blurPx = 0): CacheKey {
43
+ const uid = `${metrics.metrics.unitsPerEm}-${(metrics.headBBox ? metrics.headBBox.join(",") : "nbb")}`;
44
+ // Use one-decimal precision for blur in the key to avoid unnecessary cache churn for fractional radii
45
+ const blurKey = Math.round(Math.max(0, blurPx || 0) * 10) / 10;
46
+ return `${uid}|gid:${gid}|size:${Math.round(fontSizePx)}|ss:${supersample}|blur:${blurKey}`;
47
+ }
48
+
49
+ /**
50
+ * Get or create an alpha mask for a glyph.
51
+ * Returns null if outline is not available or rasterization failed.
52
+ */
53
+ export function getGlyphMask(metrics: TtfFontMetrics, gid: number, fontSizePx: number, supersample = 4, blurPx = 0): BitmapMask | null {
54
+ const key = makeKey(metrics, gid, fontSizePx, supersample, blurPx);
55
+ const cached = glyphMaskCache.get(key);
56
+ if (cached) {
57
+ // LRU: move to most-recently-used (end)
58
+ glyphMaskCache.delete(key);
59
+ glyphMaskCache.set(key, cached);
60
+ return cached;
61
+ }
62
+
63
+ if (!metrics.getGlyphOutline) return null;
64
+ const cmds: GlyphOutlineCmd[] | null = metrics.getGlyphOutline(gid);
65
+ if (!cmds || cmds.length === 0) return null;
66
+
67
+ const unitsPerEm = metrics.metrics.unitsPerEm || 1000;
68
+ const scale = fontSizePx / unitsPerEm;
69
+
70
+ try {
71
+ const { contours } = flattenOutline(cmds, scale, 0.5);
72
+ const mask = rasterizeContours(contours, supersample);
73
+ if (!mask) return null;
74
+
75
+ // If blur requested, apply blur to alpha buffer first
76
+ let alphaBuf = mask.data;
77
+ if (blurPx && blurPx > 0) {
78
+ alphaBuf = blurAlpha(mask.data, mask.width, mask.height, blurPx);
79
+ }
80
+
81
+ // Compute extra padding to reserve space in atlas for blur bleed (more conservative heuristic)
82
+ // Use a larger multiplier and a small safety margin to avoid clipped blurred glyphs.
83
+ const extraPadding = blurPx && blurPx > 0 ? Math.ceil(blurPx * 2) + 2 : 0;
84
+
85
+ // Attempt to pack into global atlas (best-effort) using alphaBuf and extra padding
86
+ try {
87
+ const placement = globalGlyphAtlas.pack(key, alphaBuf, mask.width, mask.height, extraPadding);
88
+ glyphAtlasPlacements.set(key, placement);
89
+ } catch {
90
+ glyphAtlasPlacements.set(key, null);
91
+ }
92
+
93
+ // Cache the final mask (with blurred alpha if applied)
94
+ // Ensure the stored buffer matches the BitmapMask type (Uint8ClampedArray)
95
+ const clamped = alphaBuf instanceof Uint8ClampedArray ? alphaBuf : new Uint8ClampedArray(alphaBuf);
96
+ const finalMask: BitmapMask = {
97
+ width: mask.width,
98
+ height: mask.height,
99
+ data: clamped,
100
+ offsetX: mask.offsetX,
101
+ offsetY: mask.offsetY,
102
+ };
103
+ glyphMaskCache.set(key, finalMask);
104
+ trimCacheIfNeeded();
105
+ return finalMask;
106
+ } catch {
107
+ // Defensive: don't throw; return null to let caller fallback
108
+ return null;
109
+ }
110
+ }
111
+
112
+ /** Utility to clear cache (useful for tests) */
113
+ export function clearGlyphMaskCache(): void {
114
+ glyphMaskCache.clear();
115
+ glyphAtlasPlacements.clear();
116
+ }
117
+
118
+ /** Return atlas placement for a cached glyph key (if available) */
119
+ export function getGlyphAtlasPlacement(metrics: TtfFontMetrics, gid: number, fontSizePx: number, supersample = 4, blurPx = 0): AtlasPlacement | null {
120
+ const key = makeKey(metrics, gid, fontSizePx, supersample, blurPx);
121
+ return glyphAtlasPlacements.get(key) ?? null;
122
+ }
123
+
124
+ /** For debugging / introspection */
125
+ export function glyphMaskCacheStats(): { entries: number; maxEntries: number } {
126
+ return { entries: glyphMaskCache.size, maxEntries };
127
+ }
@@ -0,0 +1,109 @@
1
+ import type { DataViewReader } from "./ttf-table-provider.js";
2
+
3
+ /**
4
+ * Glyph offset range in the glyf table.
5
+ */
6
+ export interface GlyphOffsetRange {
7
+ start: number;
8
+ end: number;
9
+ }
10
+
11
+ /**
12
+ * Reads and manages glyph offsets from the TrueType 'loca' table.
13
+ *
14
+ * The loca (location) table maps glyph IDs to byte offsets in the glyf table.
15
+ * It supports two formats:
16
+ * - Format 0: Short offsets (uint16), actual offset = value * 2
17
+ * - Format 1: Long offsets (uint32)
18
+ */
19
+ export class LocaTableReader {
20
+ private readonly offsets: number[];
21
+
22
+ /**
23
+ * Creates a new LocaTableReader.
24
+ * @param locaTable - DataView of the loca table
25
+ * @param indexToLocFormat - Format: 0 for short offsets, 1 for long offsets
26
+ * @param reader - Binary data reader
27
+ */
28
+ constructor(
29
+ locaTable: DataView,
30
+ indexToLocFormat: number,
31
+ reader: DataViewReader
32
+ ) {
33
+ this.offsets = [];
34
+
35
+ try {
36
+ if (indexToLocFormat === 0) {
37
+ // Short format: uint16 entries, actual offset = entry * 2
38
+ const entryCount = locaTable.byteLength / 2;
39
+ for (let i = 0; i < entryCount; i++) {
40
+ const v = reader.getUint16(locaTable, i * 2);
41
+ this.offsets.push(v * 2);
42
+ }
43
+ } else {
44
+ // Long format: uint32 entries
45
+ const entryCount = locaTable.byteLength / 4;
46
+ for (let i = 0; i < entryCount; i++) {
47
+ const v = reader.getUint32(locaTable, i * 4);
48
+ this.offsets.push(v);
49
+ }
50
+ }
51
+ } catch {
52
+ // Malformed loca table - leave offsets empty
53
+ this.offsets = [];
54
+ }
55
+ }
56
+
57
+ /**
58
+ * Gets the byte offset range for a glyph in the glyf table.
59
+ * @param glyphId - The glyph ID
60
+ * @returns Offset range or null if invalid glyph ID
61
+ */
62
+ getGlyphOffset(glyphId: number): GlyphOffsetRange | null {
63
+ if (glyphId < 0 || glyphId >= this.offsets.length - 1) {
64
+ return null;
65
+ }
66
+
67
+ const start = this.offsets[glyphId];
68
+ const end = this.offsets[glyphId + 1];
69
+
70
+ // Validate range
71
+ if (start < 0 || start > end) {
72
+ return null;
73
+ }
74
+
75
+ return { start, end };
76
+ }
77
+
78
+ /**
79
+ * Checks if a glyph is empty (has no outline data).
80
+ * Empty glyphs have start === end in the loca table.
81
+ * @param glyphId - The glyph ID
82
+ * @returns True if the glyph is empty, false otherwise
83
+ */
84
+ isEmptyGlyph(glyphId: number): boolean {
85
+ if (glyphId < 0 || glyphId >= this.offsets.length - 1) {
86
+ return true;
87
+ }
88
+
89
+ return this.offsets[glyphId] === this.offsets[glyphId + 1];
90
+ }
91
+
92
+ /**
93
+ * Gets the total number of glyphs in the font.
94
+ * @returns Number of glyphs
95
+ */
96
+ getGlyphCount(): number {
97
+ return Math.max(0, this.offsets.length - 1);
98
+ }
99
+
100
+ /**
101
+ * Validates that a glyph offset range is within the glyf table bounds.
102
+ * @param range - The offset range to validate
103
+ * @param glyfTableSize - Size of the glyf table in bytes
104
+ * @returns True if valid, false otherwise
105
+ */
106
+ validateRange(range: GlyphOffsetRange, glyfTableSize: number): boolean {
107
+ return range.start >= 0 && range.end <= glyfTableSize && range.start <= range.end;
108
+ }
109
+ }
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Manages the lifecycle and caching of FontResource objects.
3
+ *
4
+ * This manager is responsible for:
5
+ * 1. Caching resolved FontResources by family/weight/style.
6
+ * 2. Caching FontResources by base font name.
7
+ * 3. Creating and registering standard (Base14) font resources with the PDF document.
8
+ * 4. Generating unique resource names (aliases) for fonts.
9
+ */
10
+
11
+ import { PdfDocument } from "../../primitives/pdf-document.js";
12
+ import type { FontResource } from "../font-registry.js";
13
+
14
+ export class FontResourceManager {
15
+ private readonly fontsByFamilyWeight = new Map<string, FontResource>();
16
+ private readonly fontsByBaseFont = new Map<string, FontResource>();
17
+ private aliasCounter = 1;
18
+
19
+ constructor(private readonly doc: PdfDocument) { }
20
+
21
+ /**
22
+ * Retrieve a cached FontResource by its unique key.
23
+ */
24
+ getCached(key: string): FontResource | undefined {
25
+ return this.fontsByFamilyWeight.get(key);
26
+ }
27
+
28
+ /**
29
+ * Cache a FontResource by its unique key.
30
+ */
31
+ setCached(key: string, resource: FontResource): void {
32
+ this.fontsByFamilyWeight.set(key, resource);
33
+ }
34
+
35
+ /**
36
+ * Ensure a FontResource exists for a given base font name.
37
+ *
38
+ * If the resource already exists in the cache, it is returned.
39
+ * Otherwise, a new standard font resource is registered with the PDF document.
40
+ */
41
+ ensureBaseFontResource(baseFont: string): FontResource {
42
+ const existing = this.fontsByBaseFont.get(baseFont);
43
+ if (existing) {
44
+ return existing;
45
+ }
46
+
47
+ const ref = this.doc.registerStandardFont(baseFont);
48
+ const alias = `F${this.aliasCounter++}`;
49
+
50
+ const BASE14_FAMILIES = new Set([
51
+ "Helvetica",
52
+ "Helvetica-Bold",
53
+ "Helvetica-Oblique",
54
+ "Helvetica-BoldOblique",
55
+ "Times-Roman",
56
+ "Times-Bold",
57
+ "Times-Italic",
58
+ "Times-BoldItalic",
59
+ "Courier",
60
+ "Courier-Bold",
61
+ "Courier-Oblique",
62
+ "Courier-BoldOblique",
63
+ "Symbol",
64
+ "ZapfDingbats",
65
+ ]);
66
+
67
+ const isBase14 = BASE14_FAMILIES.has(baseFont);
68
+ const resource: FontResource = { baseFont, resourceName: alias, ref, isBase14 };
69
+
70
+ this.fontsByBaseFont.set(baseFont, resource);
71
+ return resource;
72
+ }
73
+ }
@@ -0,0 +1,164 @@
1
+ /**
2
+ * Manages font subsets and their materialization into PDF objects.
3
+ *
4
+ * This manager is responsible for:
5
+ * 1. Tracking glyph usage via GlyphSubsetRegistry.
6
+ * 2. Creating and caching SubsetFontResources.
7
+ * 3. Materializing font subsets (generating FontDescriptor, CIDFont, Type0 dictionaries).
8
+ * 4. Computing PDF font flags.
9
+ */
10
+
11
+ import { PdfDocument, type PdfObjectRef } from "../../primitives/pdf-document.js";
12
+ import { PdfFontRegistry as GlyphSubsetRegistry } from "../../font-subset/font-registry.js";
13
+ import type { PdfFontSubset } from "../font-subset.js";
14
+ import type { GlyphRun } from "../../../layout/text-run.js";
15
+ import type { UnifiedFont } from "../../../fonts/types.js";
16
+ import type { TtfFontMetrics } from "../../../types/fonts.js";
17
+ import type { FontResource } from "../font-registry.js";
18
+ import { computeWidths } from "../widths.js";
19
+ import { log } from "../../../logging/debug.js";
20
+
21
+ const DEFAULT_STEM_V = 80;
22
+
23
+ export interface SubsetFontResource {
24
+ readonly alias: string;
25
+ readonly subset: PdfFontSubset;
26
+ readonly ref: PdfObjectRef;
27
+ readonly font: FontResource;
28
+ }
29
+
30
+ export class SubsetResourceManager {
31
+ private readonly glyphSubsetRegistry = new GlyphSubsetRegistry();
32
+ private readonly subsetResources = new Map<string, SubsetFontResource>();
33
+
34
+ constructor(private readonly doc: PdfDocument) { }
35
+
36
+ registerGlyphRun(glyphRun: GlyphRun): void {
37
+ this.glyphSubsetRegistry.registerGlyphRun(glyphRun);
38
+ }
39
+
40
+ ensureSubsetForGlyphRun(glyphRun: GlyphRun, font: FontResource): SubsetFontResource {
41
+ this.registerGlyphRun(glyphRun);
42
+ const handle = this.glyphSubsetRegistry.ensureSubsetFor(glyphRun.font as UnifiedFont);
43
+
44
+ const baseAlias = handle.subset.name.startsWith("/") ? handle.subset.name.slice(1) : handle.subset.name;
45
+ const alias = `GS${baseAlias}`;
46
+ const existing = this.subsetResources.get(alias);
47
+ const subsetForUse: PdfFontSubset = { ...handle.subset, name: `/${alias}` };
48
+
49
+ if (existing && this.subsetMatches(existing.subset, subsetForUse)) {
50
+ return existing;
51
+ }
52
+
53
+ const ref = this.materializeSubsetFont(subsetForUse, font, handle.unifiedFont);
54
+ const resource: SubsetFontResource = { alias, subset: subsetForUse, ref, font };
55
+ this.subsetResources.set(alias, resource);
56
+ return resource;
57
+ }
58
+
59
+ private subsetMatches(current: PdfFontSubset, next: PdfFontSubset): boolean {
60
+ if (current.glyphIds.length !== next.glyphIds.length) {
61
+ return false;
62
+ }
63
+ for (let i = 0; i < current.glyphIds.length; i++) {
64
+ if (current.glyphIds[i] !== next.glyphIds[i]) {
65
+ return false;
66
+ }
67
+ }
68
+ return true;
69
+ }
70
+
71
+ private materializeSubsetFont(subset: PdfFontSubset, font: FontResource, unifiedFont: UnifiedFont): PdfObjectRef {
72
+ const metrics = font.metrics ?? (unifiedFont.metrics as unknown as TtfFontMetrics | undefined);
73
+ if (!metrics) {
74
+ return font.ref;
75
+ }
76
+
77
+ const unitsPerEm = metrics.metrics.unitsPerEm;
78
+ const scaleTo1000 = (v: number) => Math.round((v / unitsPerEm) * 1000);
79
+ const fontBBox: [number, number, number, number] = metrics.headBBox
80
+ ? [
81
+ scaleTo1000(metrics.headBBox[0]),
82
+ scaleTo1000(metrics.headBBox[1]),
83
+ scaleTo1000(metrics.headBBox[2]),
84
+ scaleTo1000(metrics.headBBox[3]),
85
+ ]
86
+ : [-1000, -1000, 1000, 1000];
87
+
88
+ const fontFile = font.embedded?.subset ?? subset.fontFile;
89
+ if (!fontFile || fontFile.length === 0) {
90
+ log("font", "warn", "missing-font-file-for-subset", { baseFont: font.baseFont, alias: subset.name });
91
+ return font.ref;
92
+ }
93
+
94
+ const { DW, W } = computeWidths(metrics);
95
+ const subsetTag = subset.name.startsWith("/") ? subset.name.slice(1) : subset.name;
96
+ const subsetBaseName = `${subsetTag}+${font.baseFont}`;
97
+
98
+ const fontFileRef = this.doc.registerStream(fontFile, {});
99
+ const fontDescriptor = {
100
+ Type: "/FontDescriptor",
101
+ FontName: `/${subsetBaseName}`,
102
+ Flags: this.computePdfFlags(font, unifiedFont),
103
+ FontBBox: fontBBox,
104
+ ItalicAngle: unifiedFont.css?.style === "italic" ? -12 : 0,
105
+ Ascent: scaleTo1000(metrics.metrics.ascender),
106
+ Descent: scaleTo1000(metrics.metrics.descender),
107
+ CapHeight: scaleTo1000(metrics.metrics.capHeight ?? metrics.metrics.ascender),
108
+ XHeight: scaleTo1000(metrics.metrics.xHeight ?? metrics.metrics.ascender / 2),
109
+ StemV: DEFAULT_STEM_V,
110
+ FontFile2: fontFileRef,
111
+ };
112
+ const fontDescriptorRef = this.doc.register(fontDescriptor);
113
+
114
+ const cidFontDict = {
115
+ Type: "/Font",
116
+ Subtype: "/CIDFontType2",
117
+ BaseFont: `/${subsetBaseName}`,
118
+ CIDSystemInfo: {
119
+ Registry: "(Adobe)",
120
+ Ordering: "(Identity)",
121
+ Supplement: 0,
122
+ },
123
+ FontDescriptor: fontDescriptorRef,
124
+ DW,
125
+ W,
126
+ CIDToGIDMap: "/Identity",
127
+ };
128
+ const cidFontRef = this.doc.register(cidFontDict);
129
+
130
+ const toUnicodeRef = this.doc.registerStream(new TextEncoder().encode(subset.toUnicodeCMap), {});
131
+ const type0Font = {
132
+ Type: "/Font",
133
+ Subtype: "/Type0",
134
+ BaseFont: `/${subsetBaseName}-Identity-H`,
135
+ Encoding: "/Identity-H",
136
+ DescendantFonts: [cidFontRef],
137
+ ToUnicode: toUnicodeRef,
138
+ };
139
+
140
+ return this.doc.register(type0Font);
141
+ }
142
+
143
+ private computePdfFlags(font: FontResource, unifiedFont: UnifiedFont): number {
144
+ const family = unifiedFont.css?.family?.toLowerCase() ?? font.baseFont.toLowerCase();
145
+ const style = unifiedFont.css?.style ?? "";
146
+ const isItalic = /italic|oblique/i.test(style) || /italic|oblique/i.test(font.baseFont);
147
+ const isSerif = family.includes("serif");
148
+ const isSymbol = family.includes("symbol") || family.includes("dingbat") || font.baseFont.toLowerCase().includes("symbol");
149
+
150
+ let flags = 0;
151
+ if (isSymbol) {
152
+ flags |= 1 << 2;
153
+ } else {
154
+ flags |= 1 << 5;
155
+ }
156
+ if (isSerif) {
157
+ flags |= 1 << 1;
158
+ }
159
+ if (isItalic) {
160
+ flags |= 1 << 6;
161
+ }
162
+ return flags;
163
+ }
164
+ }