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,117 @@
1
+ import { LayoutNode } from "../../dom/node.js";
2
+ import { Display } from "../../css/enums.js";
3
+ import { resolveLength } from "../../css/length.js";
4
+ import { inFlow } from "./node-math.js";
5
+
6
+ type LayoutDebug = (...args: unknown[]) => void;
7
+
8
+ /**
9
+ * Handles measurement and adjustment of intrinsic content width for layout nodes.
10
+ *
11
+ * This class is responsible for:
12
+ * - Measuring the actual width used by in-flow children
13
+ * - Calculating offsets for content alignment
14
+ * - Shifting children horizontally when needed
15
+ *
16
+ * Primarily used for inline-block sizing where the content width
17
+ * needs to shrink-to-fit based on children's actual space usage.
18
+ */
19
+ export class ContentMeasurer {
20
+ constructor(private readonly layoutDebug: LayoutDebug) {}
21
+ /**
22
+ * Measures the intrinsic width of in-flow content within a node.
23
+ *
24
+ * Calculates the minimum and maximum horizontal extents of all in-flow
25
+ * children, taking into account their margins, borders, and padding.
26
+ *
27
+ * @param node - The parent node whose content width to measure
28
+ * @param referenceWidth - The reference width for percentage calculations
29
+ * @param contentStartX - The X coordinate where content area begins
30
+ * @returns Object containing the measured width and any left offset
31
+ */
32
+ measureInFlowWidth(
33
+ node: LayoutNode,
34
+ referenceWidth: number,
35
+ contentStartX: number,
36
+ ): { width: number; leftOffset: number } {
37
+ let minStart = Number.POSITIVE_INFINITY;
38
+ let maxEnd = Number.NEGATIVE_INFINITY;
39
+ let hasContent = false;
40
+
41
+ for (const child of node.children) {
42
+ if (!inFlow(child)) {
43
+ continue;
44
+ }
45
+ if (child.style.display === Display.None) {
46
+ continue;
47
+ }
48
+
49
+ const marginLeft =
50
+ child.box.usedMarginLeft !== undefined
51
+ ? child.box.usedMarginLeft
52
+ : resolveLength(child.style.marginLeft, referenceWidth, { auto: "zero" });
53
+ const marginRight =
54
+ child.box.usedMarginRight !== undefined
55
+ ? child.box.usedMarginRight
56
+ : resolveLength(child.style.marginRight, referenceWidth, { auto: "zero" });
57
+ const borderLeft = resolveLength(child.style.borderLeft, referenceWidth, { auto: "zero" });
58
+ const borderRight = resolveLength(child.style.borderRight, referenceWidth, { auto: "zero" });
59
+ const paddingLeft = resolveLength(child.style.paddingLeft, referenceWidth, { auto: "zero" });
60
+ const paddingRight = resolveLength(child.style.paddingRight, referenceWidth, { auto: "zero" });
61
+
62
+ const borderBoxWidth = child.box.borderBoxWidth || Math.max(0, child.box.contentWidth + paddingLeft + paddingRight + borderLeft + borderRight);
63
+ const marginBoxWidth = borderBoxWidth + marginLeft + marginRight;
64
+ const marginStart = child.box.x - paddingLeft - borderLeft - marginLeft;
65
+ const relativeStart = marginStart - contentStartX;
66
+ const relativeEnd = relativeStart + marginBoxWidth;
67
+
68
+ this.layoutDebug(
69
+ `[measureInFlowContentWidth] parent=${node.tagName ?? "(anonymous)"} child=${child.tagName ?? "(anonymous)"} marginStart=${marginStart} relativeStart=${relativeStart} marginBoxWidth=${marginBoxWidth} borderBoxWidth=${borderBoxWidth} child.box.x=${child.box.x} contentStartX=${contentStartX}`,
70
+ );
71
+
72
+ minStart = Math.min(minStart, relativeStart);
73
+ maxEnd = Math.max(maxEnd, relativeEnd);
74
+ hasContent = true;
75
+ }
76
+
77
+ if (!hasContent) {
78
+ return { width: 0, leftOffset: 0 };
79
+ }
80
+
81
+ if (!Number.isFinite(minStart)) {
82
+ minStart = 0;
83
+ }
84
+ if (!Number.isFinite(maxEnd)) {
85
+ maxEnd = minStart;
86
+ }
87
+
88
+ const width = Math.max(0, maxEnd - minStart);
89
+ const leftOffset = minStart > 0 ? minStart : 0;
90
+ return { width, leftOffset };
91
+ }
92
+
93
+ /**
94
+ * Shifts all in-flow children horizontally by the specified amount.
95
+ *
96
+ * This is used to adjust child positions when the parent's content width
97
+ * changes (e.g., for inline-block shrink-to-fit).
98
+ *
99
+ * @param node - The parent node whose children to shift
100
+ * @param deltaX - The horizontal offset to apply (negative shifts left)
101
+ */
102
+ shiftInFlowChildrenX(node: LayoutNode, deltaX: number): void {
103
+ if (deltaX === 0) {
104
+ return;
105
+ }
106
+ for (const child of node.children) {
107
+ if (!inFlow(child)) {
108
+ continue;
109
+ }
110
+ if (child.style.display === Display.None) {
111
+ continue;
112
+ }
113
+ // Shift the child and all its descendants
114
+ child.shift(-deltaX, 0);
115
+ }
116
+ }
117
+ }
@@ -0,0 +1,24 @@
1
+ import { LayoutNode } from "../../dom/node.js";
2
+ import { Display } from "../../css/enums.js";
3
+
4
+ /**
5
+ * Determines if a layout node has an inline-level display mode.
6
+ *
7
+ * Inline-level elements participate in inline formatting contexts
8
+ * and can be laid out on the same line with text and other inline elements.
9
+ *
10
+ * @param node - The layout node to check
11
+ * @returns true if the node has an inline-level display mode
12
+ */
13
+ export function isInlineLevel(node: LayoutNode): boolean {
14
+ switch (node.style.display) {
15
+ case Display.Inline:
16
+ case Display.InlineBlock:
17
+ case Display.InlineFlex:
18
+ case Display.InlineGrid:
19
+ case Display.InlineTable:
20
+ return true;
21
+ default:
22
+ return false;
23
+ }
24
+ }
@@ -0,0 +1,98 @@
1
+ import { LayoutNode } from "../../dom/node.js";
2
+ import { ClearMode, FloatMode } from "../../css/enums.js";
3
+ import { resolveLength } from "../../css/length.js";
4
+ import { FloatContext } from "../context/float-context.js";
5
+ import type { LayoutContext } from "../pipeline/strategy.js";
6
+
7
+ interface FloatPlacementOptions {
8
+ node: LayoutNode;
9
+ floatContext: FloatContext;
10
+ context: LayoutContext;
11
+ contentX: number;
12
+ contentWidth: number;
13
+ startY: number;
14
+ }
15
+
16
+ export function clearForBlock(node: LayoutNode, floatContext: FloatContext, yCursor: number): number {
17
+ const { clear } = node.style;
18
+ let y = yCursor;
19
+ if (clear === ClearMode.Left || clear === ClearMode.Both || clear === ClearMode.InlineStart) {
20
+ y = Math.max(y, floatContext.bottom("left"));
21
+ }
22
+ if (clear === ClearMode.Right || clear === ClearMode.Both || clear === ClearMode.InlineEnd) {
23
+ y = Math.max(y, floatContext.bottom("right"));
24
+ }
25
+ return y;
26
+ }
27
+
28
+ export function placeFloat(options: FloatPlacementOptions): number {
29
+ const { node, floatContext, context, contentX, contentWidth } = options;
30
+
31
+ context.layoutChild(node);
32
+
33
+ const marginLeft = resolveLength(node.style.marginLeft, contentWidth, { auto: "zero" });
34
+ const marginRight = resolveLength(node.style.marginRight, contentWidth, { auto: "zero" });
35
+ const marginTop = resolveLength(node.style.marginTop, contentWidth, { auto: "zero" });
36
+ const marginBottom = resolveLength(node.style.marginBottom, contentWidth, { auto: "zero" });
37
+
38
+ const borderLeft = resolveLength(node.style.borderLeft, contentWidth, { auto: "zero" });
39
+ const borderRight = resolveLength(node.style.borderRight, contentWidth, { auto: "zero" });
40
+ const borderTop = resolveLength(node.style.borderTop, contentWidth, { auto: "zero" });
41
+ const borderBottom = resolveLength(node.style.borderBottom, contentWidth, { auto: "zero" });
42
+
43
+ const paddingLeft = resolveLength(node.style.paddingLeft, contentWidth, { auto: "zero" });
44
+ const paddingRight = resolveLength(node.style.paddingRight, contentWidth, { auto: "zero" });
45
+ const paddingTop = resolveLength(node.style.paddingTop, contentWidth, { auto: "zero" });
46
+ const paddingBottom = resolveLength(node.style.paddingBottom, contentWidth, { auto: "zero" });
47
+
48
+ const borderBoxWidth = node.box.contentWidth + paddingLeft + paddingRight + borderLeft + borderRight;
49
+ const borderBoxHeight = node.box.contentHeight + paddingTop + paddingBottom + borderTop + borderBottom;
50
+
51
+ node.box.borderBoxWidth = borderBoxWidth;
52
+ node.box.borderBoxHeight = borderBoxHeight;
53
+ node.box.marginBoxWidth = borderBoxWidth + marginLeft + marginRight;
54
+ node.box.marginBoxHeight = borderBoxHeight + marginTop + marginBottom;
55
+
56
+ const outerWidth = node.box.marginBoxWidth;
57
+ const outerHeight = node.box.marginBoxHeight;
58
+
59
+ let y = options.startY;
60
+ let attempts = 0;
61
+ while (true) {
62
+ if (attempts > 1000) {
63
+ break;
64
+ }
65
+ const offsets = floatContext.inlineOffsets(y, y + outerHeight, contentWidth);
66
+ const availableWidth = Math.max(0, offsets.end - offsets.start);
67
+ if (outerWidth <= availableWidth) {
68
+ const marginBoxStart =
69
+ node.style.float === FloatMode.Left
70
+ ? contentX + offsets.start
71
+ : contentX + offsets.end - outerWidth;
72
+
73
+ const contentXPosition = marginBoxStart + marginLeft + borderLeft + paddingLeft;
74
+ const contentYPosition = y + marginTop + borderTop + paddingTop;
75
+
76
+ node.box.x = contentXPosition;
77
+ node.box.y = contentYPosition;
78
+ node.box.scrollWidth = node.box.contentWidth;
79
+ node.box.scrollHeight = node.box.contentHeight;
80
+
81
+ floatContext.register(node.style.float === FloatMode.Left ? "left" : "right", {
82
+ top: y,
83
+ bottom: y + outerHeight,
84
+ inlineStart: marginBoxStart - contentX,
85
+ inlineEnd: marginBoxStart - contentX + outerWidth,
86
+ });
87
+ return y + outerHeight;
88
+ }
89
+ const nextY = floatContext.nextUnblockedY(y, y + outerHeight);
90
+ if (nextY === null || nextY <= y) {
91
+ y += 1;
92
+ } else {
93
+ y = nextY;
94
+ }
95
+ attempts += 1;
96
+ }
97
+ return y + outerHeight;
98
+ }
@@ -0,0 +1,167 @@
1
+ /**
2
+ * Gap calculation utilities for layout strategies.
3
+ * Provides SOLID gap handling for grid, flex, and other layout modes.
4
+ *
5
+ * This module follows the Single Responsibility Principle by focusing
6
+ * solely on gap-related calculations, making it reusable across different
7
+ * layout strategies.
8
+ */
9
+
10
+ export interface GapConfig {
11
+ /** Gap between rows (in pixels) */
12
+ rowGap: number;
13
+
14
+ /** Gap between columns (in pixels) */
15
+ columnGap: number;
16
+ }
17
+
18
+ /**
19
+ * Calculate total gap space for a given number of items in one direction.
20
+ *
21
+ * Formula: gap × (itemCount - 1)
22
+ *
23
+ * @param gap - Gap value in pixels
24
+ * @param itemCount - Number of items
25
+ * @returns Total gap space consumed (0 if itemCount <= 1 or gap <= 0)
26
+ *
27
+ * @example
28
+ * calculateTotalGap(10, 3) // returns 20 (2 gaps between 3 items)
29
+ * calculateTotalGap(10, 1) // returns 0 (no gaps for single item)
30
+ */
31
+ export function calculateTotalGap(gap: number, itemCount: number): number {
32
+ if (itemCount <= 1 || gap <= 0) {
33
+ return 0;
34
+ }
35
+ return gap * (itemCount - 1);
36
+ }
37
+
38
+ /**
39
+ * Calculate available space after accounting for gaps.
40
+ *
41
+ * @param totalSpace - Total available space in pixels
42
+ * @param gap - Gap between items in pixels
43
+ * @param itemCount - Number of items
44
+ * @returns Available space for content (clamped to 0 minimum)
45
+ *
46
+ * @example
47
+ * calculateAvailableSpace(100, 10, 3) // returns 80 (100 - 20 gap)
48
+ */
49
+ export function calculateAvailableSpace(
50
+ totalSpace: number,
51
+ gap: number,
52
+ itemCount: number
53
+ ): number {
54
+ const totalGap = calculateTotalGap(gap, itemCount);
55
+ return Math.max(0, totalSpace - totalGap);
56
+ }
57
+
58
+ /**
59
+ * Calculate offsets for items with gaps in a single axis.
60
+ *
61
+ * Returns the starting position of each item, accounting for gaps.
62
+ *
63
+ * @param itemSizes - Array of item sizes in pixels
64
+ * @param gap - Gap between items in pixels
65
+ * @returns Array of offsets for each item
66
+ *
67
+ * @example
68
+ * calculateItemOffsets([50, 30, 40], 10)
69
+ * // returns [0, 60, 100] (item1 at 0, item2 at 50+10, item3 at 50+10+30+10)
70
+ */
71
+ export function calculateItemOffsets(
72
+ itemSizes: number[],
73
+ gap: number
74
+ ): number[] {
75
+ const offsets: number[] = [];
76
+ let offset = 0;
77
+
78
+ for (let i = 0; i < itemSizes.length; i++) {
79
+ offsets.push(offset);
80
+ offset += itemSizes[i];
81
+
82
+ // Add gap after all items except the last
83
+ if (i < itemSizes.length - 1) {
84
+ offset += gap;
85
+ }
86
+ }
87
+
88
+ return offsets;
89
+ }
90
+
91
+ /**
92
+ * Calculate grid track offsets with gaps.
93
+ *
94
+ * This is an alias for calculateItemOffsets with clearer naming for grid context.
95
+ *
96
+ * @param trackSizes - Array of track sizes in pixels
97
+ * @param gap - Gap between tracks in pixels
98
+ * @returns Array of offsets for each track
99
+ */
100
+ export function calculateTrackOffsets(
101
+ trackSizes: number[],
102
+ gap: number
103
+ ): number[] {
104
+ return calculateItemOffsets(trackSizes, gap);
105
+ }
106
+
107
+ /**
108
+ * Gap calculator service for layout strategies.
109
+ *
110
+ * This class provides a convenient interface for gap calculations
111
+ * with awareness of layout direction (row vs column).
112
+ *
113
+ * Follows the Dependency Inversion Principle - layout strategies
114
+ * depend on this abstraction rather than implementing gap logic themselves.
115
+ */
116
+ export class GapCalculator {
117
+ constructor(private config: GapConfig) { }
118
+
119
+ /**
120
+ * Get gap for main axis based on direction.
121
+ *
122
+ * @param isRow - True if main axis is horizontal (row), false for vertical (column)
123
+ * @returns Gap value in pixels
124
+ */
125
+ getMainAxisGap(isRow: boolean): number {
126
+ return isRow ? this.config.columnGap : this.config.rowGap;
127
+ }
128
+
129
+ /**
130
+ * Get gap for cross axis based on direction.
131
+ *
132
+ * @param isRow - True if main axis is horizontal (row), false for vertical (column)
133
+ * @returns Gap value in pixels
134
+ */
135
+ getCrossAxisGap(isRow: boolean): number {
136
+ return isRow ? this.config.rowGap : this.config.columnGap;
137
+ }
138
+
139
+ /**
140
+ * Calculate total gap for main axis.
141
+ *
142
+ * @param isRow - True if main axis is horizontal (row)
143
+ * @param itemCount - Number of items in main axis
144
+ * @returns Total gap space consumed in pixels
145
+ */
146
+ calculateMainAxisTotalGap(isRow: boolean, itemCount: number): number {
147
+ const gap = this.getMainAxisGap(isRow);
148
+ return calculateTotalGap(gap, itemCount);
149
+ }
150
+
151
+ /**
152
+ * Calculate available space in main axis after gaps.
153
+ *
154
+ * @param isRow - True if main axis is horizontal (row)
155
+ * @param totalSpace - Total available space in pixels
156
+ * @param itemCount - Number of items in main axis
157
+ * @returns Available space for content in pixels
158
+ */
159
+ calculateMainAxisAvailableSpace(
160
+ isRow: boolean,
161
+ totalSpace: number,
162
+ itemCount: number
163
+ ): number {
164
+ const gap = this.getMainAxisGap(isRow);
165
+ return calculateAvailableSpace(totalSpace, gap, itemCount);
166
+ }
167
+ }
@@ -0,0 +1,31 @@
1
+ import { FloatContext } from "../context/float-context.js";
2
+ import type { LayoutContext } from "../pipeline/strategy.js";
3
+ import type { LayoutNode } from "../../dom/node.js";
4
+ import { layoutInlineFormattingContext, type InlineLayoutResult } from "./inline-formatting.js";
5
+
6
+ export interface InlineFormattingRequest {
7
+ container: LayoutNode;
8
+ inlineNodes: LayoutNode[];
9
+ context: LayoutContext;
10
+ floatContext?: FloatContext;
11
+ contentX: number;
12
+ contentWidth: number;
13
+ startY: number;
14
+ }
15
+
16
+ export class InlineFormatter {
17
+ layout(request: InlineFormattingRequest): InlineLayoutResult {
18
+ const floatContext = request.floatContext ?? new FloatContext();
19
+ return layoutInlineFormattingContext({
20
+ container: request.container,
21
+ inlineNodes: request.inlineNodes,
22
+ context: request.context,
23
+ floatContext,
24
+ contentX: request.contentX,
25
+ contentWidth: request.contentWidth,
26
+ startY: request.startY,
27
+ });
28
+ }
29
+ }
30
+
31
+ export const defaultInlineFormatter = new InlineFormatter();
@@ -0,0 +1,9 @@
1
+ export {
2
+ layoutInlineFormattingContext,
3
+ } from "../inline/layout.js";
4
+
5
+ export type {
6
+ InlineLayoutOptions,
7
+ InlineLayoutResult,
8
+ InlineMetrics,
9
+ } from "../inline/types.js";
@@ -0,0 +1,140 @@
1
+ import { LayoutNode } from "../../dom/node.js";
2
+ import { Display } from "../../css/enums.js";
3
+ import { resolveLength } from "../../css/length.js";
4
+ import { establishesBFC, inFlow, resolveWidthBlock } from "./node-math.js";
5
+
6
+ const EPSILON = 1e-7;
7
+
8
+ function isApproximatelyZero(value: number): boolean {
9
+ return Math.abs(value) < EPSILON;
10
+ }
11
+
12
+ function isBlockLevel(node: LayoutNode): boolean {
13
+ switch (node.style.display) {
14
+ case Display.Block:
15
+ case Display.ListItem:
16
+ return true;
17
+ default:
18
+ return false;
19
+ }
20
+ }
21
+
22
+ function isMarginCollapsibleChild(node: LayoutNode): boolean {
23
+ return node.style.display !== Display.None && inFlow(node) && isBlockLevel(node) && !establishesBFC(node);
24
+ }
25
+
26
+ export function collapseMarginSet(margins: readonly number[]): number {
27
+ const positives: number[] = [];
28
+ const negatives: number[] = [];
29
+ for (const margin of margins) {
30
+ if (margin > 0) {
31
+ positives.push(margin);
32
+ } else if (margin < 0) {
33
+ negatives.push(margin);
34
+ }
35
+ }
36
+
37
+ if (negatives.length === 0) {
38
+ return positives.length > 0 ? Math.max(...positives) : 0;
39
+ }
40
+ if (positives.length === 0) {
41
+ return Math.min(...negatives);
42
+ }
43
+ return Math.max(...positives) + Math.min(...negatives);
44
+ }
45
+
46
+ export function collapsedGapBetween(
47
+ prevBottomMargin: number,
48
+ nextTopMargin: number,
49
+ parentEstablishesBfc: boolean,
50
+ ): number {
51
+ if (parentEstablishesBfc) {
52
+ return prevBottomMargin + nextTopMargin;
53
+ }
54
+ return collapseMarginSet([prevBottomMargin, nextTopMargin]);
55
+ }
56
+
57
+ export function findFirstMarginCollapsibleChild(node: LayoutNode): LayoutNode | undefined {
58
+ for (const child of node.children) {
59
+ if (!inFlow(child) || child.style.display === Display.None) {
60
+ continue;
61
+ }
62
+ if (isMarginCollapsibleChild(child)) {
63
+ return child;
64
+ }
65
+ break;
66
+ }
67
+ return undefined;
68
+ }
69
+
70
+ export function findLastMarginCollapsibleChild(node: LayoutNode): LayoutNode | undefined {
71
+ for (let i = node.children.length - 1; i >= 0; i -= 1) {
72
+ const child = node.children[i];
73
+ if (!inFlow(child) || child.style.display === Display.None) {
74
+ continue;
75
+ }
76
+ if (isMarginCollapsibleChild(child)) {
77
+ return child;
78
+ }
79
+ break;
80
+ }
81
+ return undefined;
82
+ }
83
+
84
+ export function canCollapseMarginStart(node: LayoutNode, containingBlockWidth: number): boolean {
85
+ if (!isBlockLevel(node) || establishesBFC(node)) {
86
+ return false;
87
+ }
88
+ const paddingTop = resolveLength(node.style.paddingTop, containingBlockWidth, { auto: "zero" });
89
+ const borderTop = resolveLength(node.style.borderTop, containingBlockWidth, { auto: "zero" });
90
+ if (!isApproximatelyZero(paddingTop) || !isApproximatelyZero(borderTop)) {
91
+ return false;
92
+ }
93
+ return findFirstMarginCollapsibleChild(node) !== undefined;
94
+ }
95
+
96
+ export function canCollapseMarginEnd(node: LayoutNode, containingBlockWidth: number): boolean {
97
+ if (!isBlockLevel(node) || establishesBFC(node)) {
98
+ return false;
99
+ }
100
+ const paddingBottom = resolveLength(node.style.paddingBottom, containingBlockWidth, { auto: "zero" });
101
+ const borderBottom = resolveLength(node.style.borderBottom, containingBlockWidth, { auto: "zero" });
102
+ if (!isApproximatelyZero(paddingBottom) || !isApproximatelyZero(borderBottom)) {
103
+ return false;
104
+ }
105
+ return findLastMarginCollapsibleChild(node) !== undefined;
106
+ }
107
+
108
+ export function effectiveMarginTop(node: LayoutNode, containingBlockWidth: number): number {
109
+ const ownMargin = resolveLength(node.style.marginTop, containingBlockWidth, { auto: "zero" });
110
+ if (!isBlockLevel(node)) {
111
+ return ownMargin;
112
+ }
113
+ if (!canCollapseMarginStart(node, containingBlockWidth)) {
114
+ return ownMargin;
115
+ }
116
+ const firstChild = findFirstMarginCollapsibleChild(node);
117
+ if (!firstChild) {
118
+ return ownMargin;
119
+ }
120
+ const childContainingWidth = resolveWidthBlock(node, containingBlockWidth);
121
+ const childMargin = effectiveMarginTop(firstChild, childContainingWidth);
122
+ return collapseMarginSet([ownMargin, childMargin]);
123
+ }
124
+
125
+ export function effectiveMarginBottom(node: LayoutNode, containingBlockWidth: number): number {
126
+ const ownMargin = resolveLength(node.style.marginBottom, containingBlockWidth, { auto: "zero" });
127
+ if (!isBlockLevel(node)) {
128
+ return ownMargin;
129
+ }
130
+ if (!canCollapseMarginEnd(node, containingBlockWidth)) {
131
+ return ownMargin;
132
+ }
133
+ const lastChild = findLastMarginCollapsibleChild(node);
134
+ if (!lastChild) {
135
+ return ownMargin;
136
+ }
137
+ const childContainingWidth = resolveWidthBlock(node, containingBlockWidth);
138
+ const childMargin = effectiveMarginBottom(lastChild, childContainingWidth);
139
+ return collapseMarginSet([ownMargin, childMargin]);
140
+ }