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,362 @@
1
+ import type { TtfMetrics, GlyphMetrics, CmapData, GlyphOutlineCmd } from '../../types/fonts.js';
2
+ import type { FontTableData } from '../parsers/base-parser.js';
3
+
4
+ /**
5
+ * Font Metrics Extractor - Single Responsibility: Extract font metrics from table data
6
+ *
7
+ * This extractor is responsible ONLY for:
8
+ * - Reading font metrics from table data
9
+ * - Extracting character-to-glyph mappings
10
+ * - Computing font-wide measurements
11
+ *
12
+ * It does NOT handle:
13
+ * - Font format parsing
14
+ * - PDF embedding
15
+ * - Font file I/O
16
+ */
17
+ export interface FontMetrics {
18
+ readonly metrics: TtfMetrics;
19
+ readonly glyphMetrics: ReadonlyMap<number, GlyphMetrics>;
20
+ readonly cmap: CmapData;
21
+ readonly headBBox?: readonly [number, number, number, number];
22
+ }
23
+
24
+ /**
25
+ * Interface for extracting metrics from any font table data
26
+ */
27
+ export interface MetricsExtractor<T extends FontTableData = FontTableData> {
28
+ extractMetrics(tableData: T): FontMetrics;
29
+ getSupportedFormats(): string[];
30
+ }
31
+
32
+ /**
33
+ * WOFF2 Metrics Extractor - Extract metrics directly from WOFF2 table data
34
+ *
35
+ * This allows us to avoid TTF conversion and work directly with WOFF2 data,
36
+ * maintaining performance and compression benefits.
37
+ */
38
+ export class Woff2MetricsExtractor implements MetricsExtractor {
39
+
40
+ getSupportedFormats(): string[] {
41
+ return ['woff2'];
42
+ }
43
+
44
+ extractMetrics(tableData: any): FontMetrics {
45
+ // Validate we have the required WOFF2 table data
46
+ if (!tableData.tables || typeof tableData.tables !== 'object') {
47
+ throw new Error('Invalid WOFF2 table data: missing tables');
48
+ }
49
+
50
+ const tables = tableData.tables;
51
+
52
+ // Extract basic metrics from required tables
53
+ const headTable = tables['head'];
54
+ const hheaTable = tables['hhea'];
55
+ const maxpTable = tables['maxp'];
56
+ const nameTable = tables['name'];
57
+ const os2Table = tables['OS/2'];
58
+
59
+ if (!headTable || !hheaTable || !maxpTable) {
60
+ throw new Error('WOFF2 font missing required tables (head, hhea, maxp)');
61
+ }
62
+
63
+ // Parse metrics from tables
64
+ const metrics = this.parseMetrics(headTable, hheaTable, maxpTable, os2Table);
65
+ const glyphMetrics = this.parseGlyphMetrics(tableData);
66
+ const cmap = this.parseCharacterMap(tables['cmap']);
67
+ const headBBox = this.parseHeadBBox(headTable);
68
+
69
+ return {
70
+ metrics,
71
+ glyphMetrics,
72
+ cmap,
73
+ headBBox
74
+ };
75
+ }
76
+
77
+ private parseMetrics(headTable: Uint8Array, hheaTable: Uint8Array, maxpTable: Uint8Array, os2Table?: Uint8Array): TtfMetrics {
78
+ const head = new DataView(headTable.buffer, headTable.byteOffset, headTable.length);
79
+ const hhea = new DataView(hheaTable.buffer, hheaTable.byteOffset, hheaTable.length);
80
+
81
+ // Read unitsPerEm from head table (offset 18, length 2, big-endian)
82
+ const unitsPerEm = head.getUint16(18, false);
83
+
84
+ // Read ascender/descender from hhea table
85
+ const ascender = hhea.getInt16(4, false); // offset 4, length 2, signed
86
+ const descender = hhea.getInt16(6, false); // offset 6, length 2, signed
87
+
88
+ // Read line gap from hhea table
89
+ const lineGap = hhea.getInt16(8, false); // offset 8, length 2, signed
90
+
91
+ // Extract additional metrics from OS/2 table if available
92
+ let capHeight = 700; // Default fallback
93
+ let xHeight = 500; // Default fallback
94
+
95
+ if (os2Table) {
96
+ const os2 = new DataView(os2Table.buffer, os2Table.byteOffset, os2Table.length);
97
+
98
+ // OS/2 version 2+ has sCapHeight at offset 88
99
+ if (os2Table.length >= 90) {
100
+ try {
101
+ capHeight = os2.getInt16(88, false);
102
+ } catch {
103
+ // Use default if reading fails
104
+ }
105
+ }
106
+
107
+ // OS/2 has sxHeight at offset 86 (version 2+)
108
+ if (os2Table.length >= 88) {
109
+ try {
110
+ xHeight = os2.getInt16(86, false);
111
+ } catch {
112
+ // Use default if reading fails
113
+ }
114
+ }
115
+ }
116
+
117
+ return {
118
+ unitsPerEm,
119
+ ascender,
120
+ descender,
121
+ lineGap,
122
+ capHeight,
123
+ xHeight
124
+ };
125
+ }
126
+
127
+ private parseGlyphMetrics(tableData: any): ReadonlyMap<number, GlyphMetrics> {
128
+ const tables = tableData.tables;
129
+ const hmtxTable = tables['hmtx'];
130
+ const maxpTable = tables['maxp'];
131
+
132
+ if (!hmtxTable || !maxpTable) {
133
+ throw new Error('WOFF2 font missing required tables for glyph metrics (hmtx, maxp)');
134
+ }
135
+
136
+ const maxp = new DataView(maxpTable.buffer, maxpTable.byteOffset, maxpTable.length);
137
+ const numGlyphs = maxp.getUint16(4, false); // offset 4, length 2
138
+
139
+ const hmtx = new DataView(hmtxTable.buffer, hmtxTable.byteOffset, hmtxTable.length);
140
+ const glyphMetrics = new Map<number, GlyphMetrics>();
141
+
142
+ // hmtx format: longHorMetric[] followed by leftSideBearing[]
143
+ // Number of longHorMetric entries = number of advance widths in hhea
144
+ const hheaTable = tables['hhea'];
145
+ const numLongHorMetrics = new DataView(hheaTable.buffer, hheaTable.byteOffset, hheaTable.length).getUint16(34, false);
146
+
147
+ let hmtxOffset = 0;
148
+ let lastAdvanceWidth = 0;
149
+
150
+ // Process longHorMetric entries (advanceWidth + leftSideBearing)
151
+ for (let i = 0; i < numLongHorMetrics && i < numGlyphs; i++) {
152
+ const advanceWidth = hmtx.getUint16(hmtxOffset, false);
153
+ const leftSideBearing = hmtx.getInt16(hmtxOffset + 2, false);
154
+
155
+ glyphMetrics.set(i, {
156
+ advanceWidth,
157
+ leftSideBearing
158
+ });
159
+
160
+ lastAdvanceWidth = advanceWidth;
161
+ hmtxOffset += 4; // 2 bytes advanceWidth + 2 bytes leftSideBearing
162
+ }
163
+
164
+ // Process remaining glyphs with just leftSideBearing
165
+ // Per OpenType spec: these glyphs reuse the last advanceWidth value
166
+ for (let i = numLongHorMetrics; i < numGlyphs; i++) {
167
+ const leftSideBearing = hmtx.getInt16(hmtxOffset, false);
168
+
169
+ glyphMetrics.set(i, {
170
+ advanceWidth: lastAdvanceWidth,
171
+ leftSideBearing
172
+ });
173
+
174
+ hmtxOffset += 2;
175
+ }
176
+
177
+ return glyphMetrics;
178
+ }
179
+
180
+ private parseCharacterMap(cmapTable?: Uint8Array): CmapData {
181
+ if (!cmapTable) {
182
+ // Return basic fallback cmap
183
+ return {
184
+ getGlyphId: () => 0,
185
+ hasCodePoint: () => false,
186
+ unicodeMap: new Map()
187
+ };
188
+ }
189
+
190
+ const cmap = new DataView(cmapTable.buffer, cmapTable.byteOffset, cmapTable.length);
191
+
192
+ // Parse cmap table structure
193
+ const version = cmap.getUint16(0, false);
194
+ const numTables = cmap.getUint16(2, false);
195
+
196
+ let bestSubtable: { offset: number; length: number; format: number } | null = null;
197
+
198
+ // Find the best encoding subtable (prefer format 4, then format 12)
199
+ for (let i = 0; i < numTables; i++) {
200
+ const tableOffset = 4 + i * 8; // Each table entry is 8 bytes
201
+ const platformID = cmap.getUint16(tableOffset, false);
202
+ const encodingID = cmap.getUint16(tableOffset + 2, false);
203
+ const subtableOffset = cmap.getUint32(tableOffset + 4, false);
204
+
205
+ // Read subtable format
206
+ const subtableFormat = cmap.getUint16(subtableOffset, false);
207
+
208
+ // Prefer Unicode (platformID = 0 or 3) with format 4 or 12
209
+ const isUnicode = platformID === 0 || (platformID === 3 && encodingID === 1);
210
+ const isPreferredFormat = subtableFormat === 4 || subtableFormat === 12;
211
+
212
+ if (isUnicode && isPreferredFormat && (!bestSubtable || subtableFormat === 4)) {
213
+ bestSubtable = {
214
+ offset: subtableOffset,
215
+ length: 0, // Will be computed
216
+ format: subtableFormat
217
+ };
218
+
219
+ if (subtableFormat === 4) {
220
+ break; // Format 4 is preferred
221
+ }
222
+ }
223
+ }
224
+
225
+ if (!bestSubtable) {
226
+ // Fallback to first available subtable
227
+ const tableOffset = 4; // First table
228
+ const subtableOffset = cmap.getUint32(tableOffset + 4, false);
229
+ bestSubtable = {
230
+ offset: subtableOffset,
231
+ length: 0,
232
+ format: cmap.getUint16(subtableOffset, false)
233
+ };
234
+ }
235
+
236
+ // Parse the chosen subtable
237
+ if (bestSubtable.format === 4) {
238
+ return this.parseCmapFormat4(cmap, bestSubtable.offset);
239
+ } else if (bestSubtable.format === 12) {
240
+ return this.parseCmapFormat12(cmap, bestSubtable.offset);
241
+ } else {
242
+ // Unsupported format - return fallback
243
+ return {
244
+ getGlyphId: () => 0,
245
+ hasCodePoint: () => false,
246
+ unicodeMap: new Map()
247
+ };
248
+ }
249
+ }
250
+
251
+ private parseCmapFormat4(cmap: DataView, offset: number): CmapData {
252
+ const format = cmap.getUint16(offset, false);
253
+ if (format !== 4) {
254
+ throw new Error(`Expected format 4, got ${format}`);
255
+ }
256
+
257
+ const length = cmap.getUint16(offset + 2, false);
258
+ const language = cmap.getUint16(offset + 4, false); // Should be 0 for Unicode
259
+ const segCountX2 = cmap.getUint16(offset + 6, false);
260
+ const segCount = segCountX2 / 2;
261
+
262
+ const searchRange = cmap.getUint16(offset + 8, false);
263
+ const entrySelector = cmap.getUint16(offset + 10, false);
264
+ const rangeShift = cmap.getUint16(offset + 12, false);
265
+
266
+ // Find the offset to endCodes array
267
+ const endCodesOffset = offset + 14;
268
+ const startCodesOffset = endCodesOffset + segCount * 2 + 2; // +2 for reservedPad
269
+ const idDeltaOffset = startCodesOffset + segCount * 2;
270
+ const idRangeOffsetOffset = idDeltaOffset + segCount * 2;
271
+
272
+ const glyphIdArrayOffset = idRangeOffsetOffset + segCount * 2;
273
+
274
+ const unicodeMap = new Map<number, number>();
275
+
276
+ // Parse segments
277
+ for (let i = 0; i < segCount; i++) {
278
+ const endCode = cmap.getUint16(endCodesOffset + i * 2, false);
279
+ const startCode = cmap.getUint16(startCodesOffset + i * 2, false);
280
+ const idDelta = cmap.getUint16(idDeltaOffset + i * 2, false);
281
+ const idRangeOffset = cmap.getUint16(idRangeOffsetOffset + i * 2, false);
282
+
283
+ if (endCode === 0xFFFF) {
284
+ break; // Last segment marker
285
+ }
286
+
287
+ if (idRangeOffset === 0) {
288
+ // Simple case: glyphId = (unicode + idDelta) mod 65536
289
+ for (let unicode = startCode; unicode <= endCode; unicode++) {
290
+ const glyphId = (unicode + idDelta) & 0xFFFF;
291
+ unicodeMap.set(unicode, glyphId);
292
+ }
293
+ } else {
294
+ // Complex case: use idRangeOffset
295
+ const glyphIdOffset = idRangeOffsetOffset + i * 2 + idRangeOffset;
296
+
297
+ for (let unicode = startCode; unicode <= endCode; unicode++) {
298
+ const offsetInRange = glyphIdOffset + (unicode - startCode) * 2;
299
+ const glyphId = cmap.getUint16(offsetInRange, false);
300
+
301
+ if (glyphId !== 0) {
302
+ const finalGlyphId = (glyphId + idDelta) & 0xFFFF;
303
+ unicodeMap.set(unicode, finalGlyphId);
304
+ }
305
+ }
306
+ }
307
+ }
308
+
309
+ return {
310
+ getGlyphId: (unicode: number) => unicodeMap.get(unicode) || 0,
311
+ hasCodePoint: (unicode: number) => unicodeMap.has(unicode),
312
+ unicodeMap
313
+ };
314
+ }
315
+
316
+ private parseCmapFormat12(cmap: DataView, offset: number): CmapData {
317
+ const format = cmap.getUint16(offset, false);
318
+ if (format !== 12) {
319
+ throw new Error(`Expected format 12, got ${format}`);
320
+ }
321
+
322
+ const reserved = cmap.getUint16(offset + 2, false);
323
+ const length = cmap.getUint32(offset + 4, false);
324
+ const language = cmap.getUint32(offset + 8, false);
325
+ const nGroups = cmap.getUint32(offset + 12, false);
326
+
327
+ const unicodeMap = new Map<number, number>();
328
+
329
+ let groupOffset = offset + 16;
330
+
331
+ for (let i = 0; i < nGroups; i++) {
332
+ const startCharCode = cmap.getUint32(groupOffset, false);
333
+ const endCharCode = cmap.getUint32(groupOffset + 4, false);
334
+ const startGlyphID = cmap.getUint32(groupOffset + 8, false);
335
+
336
+ for (let unicode = startCharCode; unicode <= endCharCode; unicode++) {
337
+ const glyphId = startGlyphID + (unicode - startCharCode);
338
+ unicodeMap.set(unicode, glyphId);
339
+ }
340
+
341
+ groupOffset += 12; // Each group is 12 bytes
342
+ }
343
+
344
+ return {
345
+ getGlyphId: (unicode: number) => unicodeMap.get(unicode) || 0,
346
+ hasCodePoint: (unicode: number) => unicodeMap.has(unicode),
347
+ unicodeMap
348
+ };
349
+ }
350
+
351
+ private parseHeadBBox(headTable: Uint8Array): readonly [number, number, number, number] {
352
+ const head = new DataView(headTable.buffer, headTable.byteOffset, headTable.length);
353
+
354
+ // head table format: xMin, yMin, xMax, yMax at offsets 36, 38, 40, 42
355
+ const xMin = head.getInt16(36, false);
356
+ const yMin = head.getInt16(38, false);
357
+ const xMax = head.getInt16(40, false);
358
+ const yMax = head.getInt16(42, false);
359
+
360
+ return [xMin, yMin, xMax, yMax];
361
+ }
362
+ }
@@ -0,0 +1,132 @@
1
+ import type { FontResolver, UnifiedFont } from "../fonts/types.js";
2
+ import type { FontRegistry, FontResource } from "../pdf/font/font-registry.js";
3
+
4
+ /**
5
+ * Adapter that implements FontResolver using FontRegistry.
6
+ * Allows the layout system to resolve fonts during render tree building.
7
+ */
8
+ export class FontRegistryResolver implements FontResolver {
9
+ private readonly rawTableAccessors = new WeakMap<FontResource, (tag: string) => Uint8Array | null>();
10
+
11
+ constructor(private readonly fontRegistry: FontRegistry) { }
12
+
13
+ async resolve(family: string, weight?: number, style?: string): Promise<UnifiedFont> {
14
+ const fontResource = await this.fontRegistry.ensureFontResource(family, weight, style);
15
+
16
+ return this.toUnifiedFont(fontResource, family, weight, style) ?? this.createFallbackFont(family, weight, style);
17
+ }
18
+
19
+ resolveSync(family: string, weight?: number, style?: string): UnifiedFont | undefined {
20
+ const fontResource = this.fontRegistry.ensureFontResourceSync(family, weight, style);
21
+
22
+ return this.toUnifiedFont(fontResource, family, weight, style);
23
+ }
24
+
25
+ private createFallbackFont(family: string, weight?: number, style?: string): UnifiedFont {
26
+ return {
27
+ metrics: {
28
+ metrics: {
29
+ unitsPerEm: 1000,
30
+ ascender: 800,
31
+ descender: -200,
32
+ lineGap: 0,
33
+ capHeight: 700,
34
+ xHeight: 500,
35
+ },
36
+ glyphMetrics: new Map([
37
+ [0, { advanceWidth: 500, leftSideBearing: 0 }],
38
+ ]),
39
+ cmap: {
40
+ getGlyphId: () => 0,
41
+ hasCodePoint: () => false,
42
+ unicodeMap: new Map(),
43
+ },
44
+ },
45
+ program: {
46
+ sourceFormat: "ttf",
47
+ unitsPerEm: 1000,
48
+ glyphCount: 1,
49
+ },
50
+ css: {
51
+ family,
52
+ weight: weight ?? 400,
53
+ style: (style as 'normal' | 'italic') ?? 'normal',
54
+ },
55
+ };
56
+ }
57
+
58
+ private toUnifiedFont(fontResource: FontResource, family: string, weight?: number, style?: string): UnifiedFont | undefined {
59
+ const metrics = fontResource.metrics;
60
+ if (!metrics) return undefined;
61
+
62
+ const getRawTableData = this.lookupRawTableAccessor(fontResource);
63
+ return {
64
+ metrics: {
65
+ metrics: metrics.metrics,
66
+ glyphMetrics: metrics.glyphMetrics,
67
+ cmap: metrics.cmap,
68
+ headBBox: metrics.headBBox,
69
+ kerning: metrics.kerning,
70
+ },
71
+ program: {
72
+ sourceFormat: "ttf",
73
+ unitsPerEm: metrics.metrics.unitsPerEm,
74
+ glyphCount: metrics.glyphMetrics.size,
75
+ getGlyphOutline: metrics.getGlyphOutline,
76
+ getRawTableData,
77
+ },
78
+ css: {
79
+ family,
80
+ weight: weight ?? 400,
81
+ style: (style as 'normal' | 'italic') ?? 'normal',
82
+ },
83
+ };
84
+ }
85
+
86
+ private lookupRawTableAccessor(fontResource: FontResource): ((tag: string) => Uint8Array | null) | undefined {
87
+ const rawBytes: Uint8Array | undefined = fontResource.embedded?.subset;
88
+ if (!rawBytes || rawBytes.byteLength < 12) {
89
+ return undefined;
90
+ }
91
+
92
+ const cached = this.rawTableAccessors.get(fontResource);
93
+ if (cached) return cached;
94
+
95
+ const accessor = createRawTableAccessor(rawBytes);
96
+ this.rawTableAccessors.set(fontResource, accessor);
97
+ return accessor;
98
+ }
99
+ }
100
+
101
+ function createRawTableAccessor(ttfBytes: Uint8Array): (tag: string) => Uint8Array | null {
102
+ const directory = parseTtfTableDirectory(ttfBytes);
103
+ return (tag: string) => directory.get(tag) ?? null;
104
+ }
105
+
106
+ function parseTtfTableDirectory(ttfBytes: Uint8Array): Map<string, Uint8Array> {
107
+ const tables = new Map<string, Uint8Array>();
108
+ if (ttfBytes.byteLength < 12) return tables;
109
+
110
+ const view = new DataView(ttfBytes.buffer, ttfBytes.byteOffset, ttfBytes.byteLength);
111
+ const numTables = view.getUint16(4, false);
112
+
113
+ for (let i = 0; i < numTables; i++) {
114
+ const entryOffset = 12 + i * 16;
115
+ if (entryOffset + 16 > ttfBytes.byteLength) break;
116
+
117
+ const tag = String.fromCharCode(
118
+ ttfBytes[entryOffset],
119
+ ttfBytes[entryOffset + 1],
120
+ ttfBytes[entryOffset + 2],
121
+ ttfBytes[entryOffset + 3]
122
+ );
123
+
124
+ const offset = view.getUint32(entryOffset + 8, false);
125
+ const length = view.getUint32(entryOffset + 12, false);
126
+
127
+ if (offset + length > ttfBytes.byteLength) continue;
128
+ tables.set(tag, ttfBytes.subarray(offset, offset + length));
129
+ }
130
+
131
+ return tables;
132
+ }
@@ -0,0 +1,3 @@
1
+ export type { ParsedFont, UnifiedFont, FontFormat } from './types.js';
2
+ export { FontOrchestrator } from './orchestrator.js';
3
+ export { detectFontFormat, isSupportedFontFormat } from './detector.js';
@@ -0,0 +1,92 @@
1
+ import { detectFontFormat } from './detector.js';
2
+ import { TtfEngine } from './engines/ttf-engine.js';
3
+ import { WoffEngine } from './engines/woff-engine.js';
4
+ import { Woff2Engine } from './engines/woff2-engine.js';
5
+ import type { FontFormat, UnifiedFont } from './types.js';
6
+
7
+ export class FontOrchestrator {
8
+ private engines = new Map<FontFormat, any>();
9
+
10
+ constructor() {
11
+ this.engines.set('ttf', new TtfEngine());
12
+ this.engines.set('woff', new WoffEngine());
13
+ this.engines.set('woff2', new Woff2Engine());
14
+ this.engines.set('otf', new TtfEngine()); // OTF uses the same engine as TTF for now
15
+ }
16
+
17
+ async parseFont(fontData: Uint8Array): Promise<UnifiedFont> {
18
+ const format = detectFontFormat(fontData);
19
+ if (!format) {
20
+ throw new Error('Unsupported font format');
21
+ }
22
+
23
+ const engine = this.engines.get(format);
24
+ if (!engine) {
25
+ throw new Error(`No engine available for font format: ${format}`);
26
+ }
27
+
28
+ try {
29
+ console.log(`FONT-ORCH: Parsing ${format} font with ${engine.constructor.name}`);
30
+
31
+ let parsed;
32
+ if (format === 'ttf' || format === 'otf') {
33
+ // TTF engine works with TtfFontMetrics directly
34
+ parsed = engine.parse(fontData);
35
+ } else {
36
+ // WOFF/WOFF2 engines work with ParsedFont
37
+ parsed = await engine.parse(fontData);
38
+ }
39
+
40
+ let unifiedFont: UnifiedFont;
41
+ if (format === 'ttf' || format === 'otf') {
42
+ unifiedFont = engine.convertToUnified(parsed);
43
+ } else {
44
+ unifiedFont = await engine.convertToUnified(parsed);
45
+ }
46
+
47
+ console.log(`FONT-ORCH: Successfully parsed ${format} font`);
48
+ return unifiedFont;
49
+ } catch (error) {
50
+ const errorMessage = error instanceof Error ? error.message : String(error);
51
+ console.warn(`FONT-ORCH: Failed to parse ${format} font:`, errorMessage);
52
+ throw error instanceof Error ? error : new Error(errorMessage);
53
+ }
54
+ }
55
+
56
+ getSupportedFormats(): FontFormat[] {
57
+ return Array.from(this.engines.keys());
58
+ }
59
+
60
+ private getFallbackFont(format: FontFormat): UnifiedFont {
61
+ console.log(`FONT-ORCH: Creating fallback font for ${format} format`);
62
+
63
+ return {
64
+ metrics: {
65
+ metrics: {
66
+ unitsPerEm: 1000,
67
+ ascender: 800,
68
+ descender: -200,
69
+ lineGap: 0,
70
+ capHeight: 700,
71
+ xHeight: 500,
72
+ },
73
+ glyphMetrics: new Map([
74
+ [0, { advanceWidth: 500, leftSideBearing: 0 }],
75
+ [1, { advanceWidth: 250, leftSideBearing: 0 }],
76
+ [2, { advanceWidth: 500, leftSideBearing: 0 }],
77
+ ]),
78
+ cmap: {
79
+ getGlyphId: () => 1,
80
+ hasCodePoint: () => true,
81
+ unicodeMap: new Map([[65, 1]]),
82
+ },
83
+ headBBox: [0, -200, 1000, 800]
84
+ },
85
+ program: {
86
+ sourceFormat: format,
87
+ getRawTableData: () => null,
88
+ getGlyphOutline: () => null,
89
+ },
90
+ };
91
+ }
92
+ }
@@ -0,0 +1,23 @@
1
+ // Base parser interface for all font formats
2
+ export interface FontTableData {
3
+ flavor: number;
4
+ tables: Record<string, Uint8Array>;
5
+ compressionInfo?: CompressionInfo;
6
+ }
7
+
8
+ export interface CompressionInfo {
9
+ type: 'none' | 'woff' | 'woff2';
10
+ tables: Map<string, TableCompressionInfo>;
11
+ }
12
+
13
+ export interface TableCompressionInfo {
14
+ compressed: boolean;
15
+ originalLength?: number;
16
+ compressedLength?: number;
17
+ }
18
+
19
+ // Generic font parser interface
20
+ export interface FontParser<T extends FontTableData = FontTableData> {
21
+ parseTables(fontData: Uint8Array): Promise<T>;
22
+ getFormat(): string;
23
+ }
@@ -0,0 +1,85 @@
1
+ import type { TtfMetrics, GlyphMetrics, CmapData, GlyphOutlineCmd, KerningMap } from '../types/fonts.js';
2
+
3
+ // Raw parsed font (low-level table data)
4
+ export interface ParsedFont {
5
+ flavor: number;
6
+ numTables: number;
7
+ tables: Record<string, Uint8Array>;
8
+ }
9
+
10
+ /**
11
+ * Processed font metrics for layout and text shaping/rendering.
12
+ * Pure data, no raw binary access.
13
+ */
14
+ export interface FontMetrics {
15
+ readonly metrics: TtfMetrics;
16
+ readonly glyphMetrics: ReadonlyMap<number, GlyphMetrics>;
17
+ readonly cmap: CmapData;
18
+ readonly headBBox?: readonly [number, number, number, number];
19
+ readonly kerning?: KerningMap;
20
+ }
21
+
22
+ /**
23
+ * Low-level font program data for PDF embedding, subsetting, and glyph paths.
24
+ * Corresponds to "font program" in PDF/TTF/OTF terminology.
25
+ */
26
+ export interface FontProgram {
27
+ readonly sourceFormat: FontFormat;
28
+ readonly unitsPerEm?: number;
29
+ readonly glyphCount?: number;
30
+ readonly getRawTableData?: (tag: string) => Uint8Array | null;
31
+ readonly getGlyphOutline?: (gid: number) => GlyphOutlineCmd[] | null;
32
+ }
33
+
34
+ /**
35
+ * CSS font-face resolution info.
36
+ */
37
+ export interface CssFontInfo {
38
+ family: string;
39
+ weight: number;
40
+ style: 'normal' | 'italic';
41
+ }
42
+
43
+ /**
44
+ * Loaded and unified font: ready for layout/PDF use.
45
+ * Combines metrics + program + CSS resolution metadata.
46
+ */
47
+ export interface LoadedFont {
48
+ readonly metrics: FontMetrics;
49
+ readonly program: FontProgram;
50
+ readonly css?: CssFontInfo;
51
+ }
52
+
53
+ // Backward-compatible alias for gradual migration
54
+ export type UnifiedFont = LoadedFont;
55
+
56
+ export type FontFormat = 'ttf' | 'woff' | 'woff2' | 'otf';
57
+
58
+ export interface FontEngine<T = ParsedFont> {
59
+ parse(fontData: Uint8Array): Promise<T> | T;
60
+ convertToUnified(parsedFont: T): Promise<UnifiedFont> | UnifiedFont;
61
+ }
62
+
63
+ /**
64
+ * WOFF2-specific types for enhanced separation
65
+ */
66
+ export interface Woff2FontProgram extends FontProgram {
67
+ readonly sourceFormat: 'woff2';
68
+ readonly compressionInfo?: {
69
+ type: 'woff2';
70
+ tables: Map<string, any>;
71
+ };
72
+ readonly transformInfo?: {
73
+ glyphTransformed: boolean;
74
+ locaTransformed: boolean;
75
+ };
76
+ }
77
+
78
+ export interface Woff2Font extends LoadedFont {
79
+ readonly program: Woff2FontProgram;
80
+ }
81
+
82
+ export interface FontResolver {
83
+ resolve(family: string, weight?: number, style?: string): UnifiedFont | Promise<UnifiedFont>;
84
+ resolveSync?(family: string, weight?: number, style?: string): UnifiedFont | undefined;
85
+ }