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.
- package/.eslintrc.json +30 -0
- package/CHANGELOG.md +13 -0
- package/README.md +275 -0
- package/UA_Styles_Chromium.md +93 -0
- package/_ext/woff2_conversion/brotli-decode.d.ts +139 -0
- package/_ext/woff2_conversion/brotli-encode.d.ts +129 -0
- package/_ext/woff2_conversion/brotli-port.d.ts +12 -0
- package/_ext/woff2_conversion/brotli-shared-dictionary.d.ts +25 -0
- package/_ext/woff2_conversion/brotli-types.d.ts +15 -0
- package/_ext/woff2_conversion/woff2-common.d.ts +37 -0
- package/_ext/woff2_conversion/woff2-decode.d.ts +32 -0
- package/_ext/woff2_conversion/woff2-encode.d.ts +31 -0
- package/_ext/woff2_conversion/woff2-output.d.ts +39 -0
- package/_ext/woff2_original_cpp/brotli/brotli.c +1559 -0
- package/_ext/woff2_original_cpp/brotli/brotli.md +116 -0
- package/_ext/woff2_original_cpp/brotli/decode.h +409 -0
- package/_ext/woff2_original_cpp/brotli/encode.h +505 -0
- package/_ext/woff2_original_cpp/brotli/port.h +302 -0
- package/_ext/woff2_original_cpp/brotli/shared_dictionary.h +100 -0
- package/_ext/woff2_original_cpp/brotli/types.h +83 -0
- package/_ext/woff2_original_cpp/cmake/FindBrotliDec.cmake +35 -0
- package/_ext/woff2_original_cpp/cmake/FindBrotliEnc.cmake +35 -0
- package/_ext/woff2_original_cpp/include/woff2/decode.h +36 -0
- package/_ext/woff2_original_cpp/include/woff2/encode.h +43 -0
- package/_ext/woff2_original_cpp/include/woff2/output.h +86 -0
- package/_ext/woff2_original_cpp/src/buffer.h +164 -0
- package/_ext/woff2_original_cpp/src/convert_woff2ttf_fuzzer.cc +13 -0
- package/_ext/woff2_original_cpp/src/convert_woff2ttf_fuzzer_new_entry.cc +12 -0
- package/_ext/woff2_original_cpp/src/file.h +30 -0
- package/_ext/woff2_original_cpp/src/font.cc +400 -0
- package/_ext/woff2_original_cpp/src/font.h +105 -0
- package/_ext/woff2_original_cpp/src/glyph.cc +383 -0
- package/_ext/woff2_original_cpp/src/glyph.h +71 -0
- package/_ext/woff2_original_cpp/src/normalize.cc +314 -0
- package/_ext/woff2_original_cpp/src/normalize.h +39 -0
- package/_ext/woff2_original_cpp/src/port.h +66 -0
- package/_ext/woff2_original_cpp/src/round.h +27 -0
- package/_ext/woff2_original_cpp/src/store_bytes.h +55 -0
- package/_ext/woff2_original_cpp/src/table_tags.cc +82 -0
- package/_ext/woff2_original_cpp/src/table_tags.h +30 -0
- package/_ext/woff2_original_cpp/src/transform.cc +430 -0
- package/_ext/woff2_original_cpp/src/transform.h +26 -0
- package/_ext/woff2_original_cpp/src/variable_length.cc +129 -0
- package/_ext/woff2_original_cpp/src/variable_length.h +30 -0
- package/_ext/woff2_original_cpp/src/woff2_common.cc +50 -0
- package/_ext/woff2_original_cpp/src/woff2_common.h +64 -0
- package/_ext/woff2_original_cpp/src/woff2_compress.cc +43 -0
- package/_ext/woff2_original_cpp/src/woff2_dec.cc +1398 -0
- package/_ext/woff2_original_cpp/src/woff2_decompress.cc +41 -0
- package/_ext/woff2_original_cpp/src/woff2_enc.cc +458 -0
- package/_ext/woff2_original_cpp/src/woff2_info.cc +142 -0
- package/_ext/woff2_original_cpp/src/woff2_out.cc +63 -0
- package/assets/fonts/ttf/arimo/Arimo-Bold.ttf +0 -0
- package/assets/fonts/ttf/arimo/Arimo-BoldItalic.ttf +0 -0
- package/assets/fonts/ttf/arimo/Arimo-Italic.ttf +0 -0
- package/assets/fonts/ttf/arimo/Arimo-Regular.ttf +0 -0
- package/assets/fonts/ttf/cinzeldecorative/CinzelDecorative-Black.ttf +0 -0
- package/assets/fonts/ttf/cinzeldecorative/CinzelDecorative-Bold.ttf +0 -0
- package/assets/fonts/ttf/cinzeldecorative/CinzelDecorative-Regular.ttf +0 -0
- package/assets/fonts/ttf/dejavu/DejaVuSans.ttf +0 -0
- package/assets/fonts/ttf/firecode/FiraCode-Bold.ttf +0 -0
- package/assets/fonts/ttf/firecode/FiraCode-Light.ttf +0 -0
- package/assets/fonts/ttf/firecode/FiraCode-Medium.ttf +0 -0
- package/assets/fonts/ttf/firecode/FiraCode-Regular.ttf +0 -0
- package/assets/fonts/ttf/firecode/FiraCode-SemiBold.ttf +0 -0
- package/assets/fonts/ttf/notoemoji/NotoEmoji-Bold.ttf +0 -0
- package/assets/fonts/ttf/notoemoji/NotoEmoji-Light.ttf +0 -0
- package/assets/fonts/ttf/notoemoji/NotoEmoji-Medium.ttf +0 -0
- package/assets/fonts/ttf/notoemoji/NotoEmoji-Regular.ttf +0 -0
- package/assets/fonts/ttf/notoemoji/NotoEmoji-SemiBold.ttf +0 -0
- package/assets/fonts/ttf/notosans/NotoSans-Regular.ttf +0 -0
- package/assets/fonts/ttf/roboto/Roboto-Bold.ttf +0 -0
- package/assets/fonts/ttf/roboto/Roboto-BoldItalic.ttf +0 -0
- package/assets/fonts/ttf/roboto/Roboto-Italic.ttf +0 -0
- package/assets/fonts/ttf/roboto/Roboto-Regular.ttf +0 -0
- package/assets/fonts/ttf/stixtwomath/STIXTwoMath-Regular.ttf +0 -0
- package/assets/fonts/ttf/tinos/Tinos-Bold.ttf +0 -0
- package/assets/fonts/ttf/tinos/Tinos-BoldItalic.ttf +0 -0
- package/assets/fonts/ttf/tinos/Tinos-Italic.ttf +0 -0
- package/assets/fonts/ttf/tinos/Tinos-Regular.ttf +0 -0
- package/assets/fonts/woff/lato/lato-latin-400-italic.woff +0 -0
- package/assets/fonts/woff/lato/lato-latin-400-normal.woff +0 -0
- package/assets/fonts/woff/lato/lato-latin-700-italic.woff +0 -0
- package/assets/fonts/woff/lato/lato-latin-700-normal.woff +0 -0
- package/assets/fonts/woff2/caveat/Caveat-Bold.woff2 +0 -0
- package/assets/fonts/woff2/caveat/Caveat-Regular.woff2 +0 -0
- package/assets/fonts/woff2/lato/lato-latin-400-italic.woff2 +0 -0
- package/assets/fonts/woff2/lato/lato-latin-400-normal.woff2 +0 -0
- package/assets/fonts/woff2/lato/lato-latin-700-italic.woff2 +0 -0
- package/assets/fonts/woff2/lato/lato-latin-700-normal.woff2 +0 -0
- package/docs/AGENTS.md +288 -0
- package/docs/BACKGROUND-REPEAT-IMPLEMENTATION.md +127 -0
- package/docs/BACKGROUND-REPEAT-REFERENCE.md +127 -0
- package/docs/BACKGROUND-REPEAT-SPACE-ROUND.md +164 -0
- package/docs/css-properties-support.md +256 -0
- package/docs/src_modules_table.md +172 -0
- package/docs/text-overlap-fix.md +85 -0
- package/docs/text-overlap-investigation.md +27 -0
- package/eslint.config.js +36 -0
- package/glyph_measure.htm +1458 -0
- package/package.json +50 -0
- package/playground/browser-entry.ts +2 -0
- package/playground/exports/background-text-debug.pdf +0 -0
- package/playground/public/app.js +875 -0
- package/playground/public/assets/1.webp +0 -0
- package/playground/public/examples/accents-test.html +24 -0
- package/playground/public/examples/advanced-selectors-demo.html +118 -0
- package/playground/public/examples/background-advanced-showcase.html +82 -0
- package/playground/public/examples/background-clip-text.html +36 -0
- package/playground/public/examples/background-origin-showcase.html +137 -0
- package/playground/public/examples/background-position-showcase.html +83 -0
- package/playground/public/examples/background-repeat-showcase.html +83 -0
- package/playground/public/examples/background-repeat-space-round.html +348 -0
- package/playground/public/examples/background-size-showcase.html +82 -0
- package/playground/public/examples/background-text-debug.html +18 -0
- package/playground/public/examples/baseline-test.html +24 -0
- package/playground/public/examples/bold-showcase.html +150 -0
- package/playground/public/examples/bold-strike-example.html +12 -0
- package/playground/public/examples/border-collapse-test.html +23 -0
- package/playground/public/examples/centered-shadow-div.html +72 -0
- package/playground/public/examples/css-variables.html +50 -0
- package/playground/public/examples/debug-accents.html +11 -0
- package/playground/public/examples/debug-text-overlap.html +46 -0
- package/playground/public/examples/flex-gap-column.html +130 -0
- package/playground/public/examples/flex-gap-row.html +137 -0
- package/playground/public/examples/flex-padding-test.html +29 -0
- package/playground/public/examples/flexbox-text-test.html +193 -0
- package/playground/public/examples/fonts-demo.html +126 -0
- package/playground/public/examples/footer-example.html +4 -0
- package/playground/public/examples/gradient-text.html +54 -0
- package/playground/public/examples/grid-gap-demo.html +156 -0
- package/playground/public/examples/header-example.html +4 -0
- package/playground/public/examples/header-footer-example.html +27 -0
- package/playground/public/examples/image-showcase.html +33 -0
- package/playground/public/examples/justify-text.html +22 -0
- package/playground/public/examples/linear-gradient-example.html +38 -0
- package/playground/public/examples/lorem-span.html +14 -0
- package/playground/public/examples/margin-block-showcase.html +21 -0
- package/playground/public/examples/margin-inline-showcase.html +21 -0
- package/playground/public/examples/monthly-summary.html +95 -0
- package/playground/public/examples/multi-page-lorem.html +190 -0
- package/playground/public/examples/opacity-debug.html +39 -0
- package/playground/public/examples/opacity-example.html +70 -0
- package/playground/public/examples/png-image-example.html +13 -0
- package/playground/public/examples/red-rectangle.html +18 -0
- package/playground/public/examples/repro.html +24 -0
- package/playground/public/examples/rounded-borders-test.html +24 -0
- package/playground/public/examples/simple-list.html +89 -0
- package/playground/public/examples/simple-svg.html +37 -0
- package/playground/public/examples/simple-table.html +52 -0
- package/playground/public/examples/skew-div.html +138 -0
- package/playground/public/examples/skew-text.html +21 -0
- package/playground/public/examples/starter-report.css +51 -0
- package/playground/public/examples/starter-report.html +23 -0
- package/playground/public/examples/svg-aspect-ratio-showcase.html +116 -0
- package/playground/public/examples/svg-gradients-linear.html +28 -0
- package/playground/public/examples/svg-gradients-radial.html +29 -0
- package/playground/public/examples/svg-gradients-showcase.html +66 -0
- package/playground/public/examples/svg-image-path-test.html +43 -0
- package/playground/public/examples/svg-images-clipping.html +27 -0
- package/playground/public/examples/svg-path-gallery.html +118 -0
- package/playground/public/examples/svg-radial-transform-demo.html +78 -0
- package/playground/public/examples/svg-transform-stack.html +103 -0
- package/playground/public/examples/svg-transforms-demo.html +127 -0
- package/playground/public/examples/table-merge-test.html +34 -0
- package/playground/public/examples/text-decoration-showcase.html +138 -0
- package/playground/public/examples/text-indent-showcase.html +137 -0
- package/playground/public/examples/text-shadow-example.html +29 -0
- package/playground/public/examples/very-complex-css.html +293 -0
- package/playground/public/examples/webp-example.html +13 -0
- package/playground/public/examples/z-index-demo.html +93 -0
- package/playground/public/examples.json +240 -0
- package/playground/public/images/dice.png +0 -0
- package/playground/public/images/duck.jpg +0 -0
- package/playground/public/index.html +149 -0
- package/playground/public/mode.js +1 -0
- package/playground/public/styles.css +382 -0
- package/playground/public/tmp-h2-debug.html +33 -0
- package/playground/public/tmp-italic-debug.html +32 -0
- package/playground/public/vendor/codemirror/codemirror.min.css +1 -0
- package/playground/public/vendor/codemirror/codemirror.min.js +1 -0
- package/playground/public/vendor/codemirror/css.min.js +1 -0
- package/playground/public/vendor/codemirror/darcula.min.css +1 -0
- package/playground/public/vendor/codemirror/htmlmixed.min.js +1 -0
- package/playground/public/vendor/codemirror/javascript.min.js +1 -0
- package/playground/public/vendor/codemirror/xml.min.js +1 -0
- package/playground/public/vendor/pagyra-playground-browser.js +165966 -0
- package/playground/public/vendor/pagyra-playground-browser.js.map +7 -0
- package/playground/server.d.ts +1 -0
- package/playground/server.js +68 -0
- package/playground/server.ts +128 -0
- package/scripts/browser-build.ts +101 -0
- package/scripts/build-browser-bundle.ts +52 -0
- package/scripts/glyph-comparison/simulate.ts +744 -0
- package/scripts/playground-browser-server.ts +57 -0
- package/scripts/probe-roboto.ts +6 -0
- package/scripts/render-playground-example.ts +121 -0
- package/scripts/run-glyph-atlas-tuner-runner.mjs +113 -0
- package/scripts/run-glyph-atlas-tuner.ts +141 -0
- package/scripts/top-ts-files.ps1 +39 -0
- package/scripts/top-ts-files.sh +37 -0
- package/scripts/woff2_info.ps1 +132 -0
- package/src/browser-entry.ts +14 -0
- package/src/compression/adler32.ts +45 -0
- package/src/compression/brotli/brotli.ts +463 -0
- package/src/compression/brotli/index.ts +15 -0
- package/src/compression/brotli/transform.ts +184 -0
- package/src/compression/brotli/types.ts +58 -0
- package/src/compression/brotli/utils.ts +157 -0
- package/src/compression/brotli/vendor/bit_reader.js +124 -0
- package/src/compression/brotli/vendor/context.js +250 -0
- package/src/compression/brotli/vendor/decode.d.ts +2 -0
- package/src/compression/brotli/vendor/decode.js +938 -0
- package/src/compression/brotli/vendor/dictionary-data.js +9469 -0
- package/src/compression/brotli/vendor/dictionary.js +36 -0
- package/src/compression/brotli/vendor/huffman.js +123 -0
- package/src/compression/brotli/vendor/package.json +3 -0
- package/src/compression/brotli/vendor/prefix.js +60 -0
- package/src/compression/brotli/vendor/streams.js +31 -0
- package/src/compression/brotli/vendor/transform.js +247 -0
- package/src/compression/brotli/vendor-decode.d.ts +4 -0
- package/src/compression/brotli/woff2-glyf-transform.ts +623 -0
- package/src/compression/decompress.ts +16 -0
- package/src/compression/deflate.ts +295 -0
- package/src/compression/index.ts +4 -0
- package/src/compression/types.ts +26 -0
- package/src/compression/utils.ts +107 -0
- package/src/core.ts +18 -0
- package/src/css/apply-declarations.ts +86 -0
- package/src/css/background-types.ts +65 -0
- package/src/css/browser-defaults.ts +16 -0
- package/src/css/clip-path-types.ts +13 -0
- package/src/css/compute-style.ts +494 -0
- package/src/css/css-unit-resolver.ts +65 -0
- package/src/css/custom-properties.ts +215 -0
- package/src/css/enums.ts +127 -0
- package/src/css/font-face-parser.ts +233 -0
- package/src/css/font-weight.ts +65 -0
- package/src/css/inline-style-parser.ts +27 -0
- package/src/css/layout-property-resolver.ts +75 -0
- package/src/css/length.ts +141 -0
- package/src/css/line-height.ts +96 -0
- package/src/css/named-colors.ts +150 -0
- package/src/css/parsers/background-parser-extended.ts +111 -0
- package/src/css/parsers/background-parser.ts +456 -0
- package/src/css/parsers/border-block-parser.ts +26 -0
- package/src/css/parsers/border-inline-parser.ts +26 -0
- package/src/css/parsers/border-parser-extended.ts +256 -0
- package/src/css/parsers/border-parser.ts +175 -0
- package/src/css/parsers/box-shadow-parser.ts +106 -0
- package/src/css/parsers/clip-path-parser.ts +92 -0
- package/src/css/parsers/color-parser.ts +14 -0
- package/src/css/parsers/dimension-parser.ts +117 -0
- package/src/css/parsers/display-flex-parser.ts +59 -0
- package/src/css/parsers/flex-parser.ts +144 -0
- package/src/css/parsers/font-parser.ts +40 -0
- package/src/css/parsers/gradient-parser.ts +366 -0
- package/src/css/parsers/grid-parser-extended.ts +55 -0
- package/src/css/parsers/grid-parser.ts +218 -0
- package/src/css/parsers/length-parser.ts +95 -0
- package/src/css/parsers/list-style-parser.ts +39 -0
- package/src/css/parsers/margin-block-parser.ts +12 -0
- package/src/css/parsers/margin-inline-parser.ts +12 -0
- package/src/css/parsers/margin-parser.ts +30 -0
- package/src/css/parsers/opacity-parser.ts +32 -0
- package/src/css/parsers/overflow-wrap-parser.ts +38 -0
- package/src/css/parsers/padding-block-parser.ts +12 -0
- package/src/css/parsers/padding-inline-parser.ts +12 -0
- package/src/css/parsers/padding-parser.ts +30 -0
- package/src/css/parsers/position-parser.ts +75 -0
- package/src/css/parsers/register-parsers.ts +302 -0
- package/src/css/parsers/registry.ts +18 -0
- package/src/css/parsers/text-parser-extended.ts +144 -0
- package/src/css/parsers/text-parser.ts +25 -0
- package/src/css/parsers/text-shadow-parser.ts +94 -0
- package/src/css/properties/box-model.ts +82 -0
- package/src/css/properties/flexbox.ts +44 -0
- package/src/css/properties/gap.ts +14 -0
- package/src/css/properties/grid.ts +94 -0
- package/src/css/properties/layout.ts +59 -0
- package/src/css/properties/misc.ts +44 -0
- package/src/css/properties/typography.ts +71 -0
- package/src/css/properties/visual.ts +68 -0
- package/src/css/selectors/matcher.ts +219 -0
- package/src/css/selectors/parser.ts +163 -0
- package/src/css/selectors/simple-key.ts +31 -0
- package/src/css/selectors/specificity.ts +41 -0
- package/src/css/selectors/types.ts +31 -0
- package/src/css/shorthands/border-shorthand.ts +68 -0
- package/src/css/shorthands/box-shorthand.ts +33 -0
- package/src/css/style-inheritance.ts +50 -0
- package/src/css/style.ts +402 -0
- package/src/css/ua-defaults/base-defaults.ts +266 -0
- package/src/css/ua-defaults/browser-defaults.ts +134 -0
- package/src/css/ua-defaults/element-defaults.ts +374 -0
- package/src/css/ua-defaults/types.ts +43 -0
- package/src/css/unit-conversion.ts +24 -0
- package/src/css/utils.ts +108 -0
- package/src/css/viewport.ts +17 -0
- package/src/debug/audit.ts +20 -0
- package/src/debug/ids.ts +13 -0
- package/src/debug/log.js +28 -0
- package/src/debug/log.ts +52 -0
- package/src/debug/tree.ts +57 -0
- package/src/dom/node.ts +133 -0
- package/src/environment/browser-environment.ts +78 -0
- package/src/environment/environment.ts +35 -0
- package/src/environment/global.ts +13 -0
- package/src/environment/node-environment.browser.ts +28 -0
- package/src/environment/node-environment.ts +64 -0
- package/src/fonts/detector.ts +28 -0
- package/src/fonts/engines/ttf-engine.ts +28 -0
- package/src/fonts/engines/woff-engine.ts +38 -0
- package/src/fonts/engines/woff2-engine.ts +41 -0
- package/src/fonts/extractors/metrics-extractor.ts +362 -0
- package/src/fonts/font-registry-resolver.ts +132 -0
- package/src/fonts/index.ts +3 -0
- package/src/fonts/orchestrator.ts +92 -0
- package/src/fonts/parsers/base-parser.ts +23 -0
- package/src/fonts/types.ts +85 -0
- package/src/fonts/utils/ttf-reconstructor.ts +120 -0
- package/src/fonts/woff/decoder.ts +105 -0
- package/src/fonts/woff2/buffer.ts +106 -0
- package/src/fonts/woff2/decoder.ts +981 -0
- package/src/geometry/box.ts +48 -0
- package/src/geometry/matrix.ts +59 -0
- package/src/html/css/parse-css.ts +85 -0
- package/src/html/dom-converter.ts +433 -0
- package/src/html/image-converter.ts +200 -0
- package/src/html-to-pdf.ts +410 -0
- package/src/image/base-decoder.ts +149 -0
- package/src/image/image-service.ts +188 -0
- package/src/image/jpeg-decoder.ts +73 -0
- package/src/image/png-decoder.ts +550 -0
- package/src/image/types.ts +20 -0
- package/src/image/webp-decoder.ts +242 -0
- package/src/image/webp-huffman.ts +218 -0
- package/src/image/webp-riff-parser.ts +54 -0
- package/src/image/webp-vp8l-decoder.ts +199 -0
- package/src/index.ts +35 -0
- package/src/layout/context/float-context.ts +62 -0
- package/src/layout/context/layout-environment.ts +29 -0
- package/src/layout/debug.ts +18 -0
- package/src/layout/inline/bounding-box-calculator.ts +132 -0
- package/src/layout/inline/font-baseline-calculator.ts +76 -0
- package/src/layout/inline/inline-utils.ts +94 -0
- package/src/layout/inline/layout.ts +285 -0
- package/src/layout/inline/line_breaker.ts +109 -0
- package/src/layout/inline/measurement.ts +144 -0
- package/src/layout/inline/run-placer.ts +139 -0
- package/src/layout/inline/text-alignment.ts +70 -0
- package/src/layout/inline/tokenizer.ts +195 -0
- package/src/layout/inline/types.ts +76 -0
- package/src/layout/pipeline/context-factory.ts +16 -0
- package/src/layout/pipeline/default-engine.ts +24 -0
- package/src/layout/pipeline/engine.ts +59 -0
- package/src/layout/pipeline/layout-tree.ts +13 -0
- package/src/layout/pipeline/out-of-flow-manager.ts +73 -0
- package/src/layout/pipeline/strategy.ts +12 -0
- package/src/layout/pipeline/text-metrics-initializer.ts +13 -0
- package/src/layout/strategies/block.ts +236 -0
- package/src/layout/strategies/display-none.ts +14 -0
- package/src/layout/strategies/fallback.ts +15 -0
- package/src/layout/strategies/flex.ts +477 -0
- package/src/layout/strategies/fragmentation.ts +17 -0
- package/src/layout/strategies/grid.ts +247 -0
- package/src/layout/strategies/image.ts +342 -0
- package/src/layout/strategies/inline.ts +128 -0
- package/src/layout/strategies/table.ts +595 -0
- package/src/layout/table/cell_layout.ts +31 -0
- package/src/layout/table/diagnostics.ts +19 -0
- package/src/layout/text-run.ts +42 -0
- package/src/layout/utils/content-measurer.ts +117 -0
- package/src/layout/utils/display-utils.ts +24 -0
- package/src/layout/utils/floats.ts +98 -0
- package/src/layout/utils/gap-calculator.ts +167 -0
- package/src/layout/utils/inline-formatter.ts +31 -0
- package/src/layout/utils/inline-formatting.ts +9 -0
- package/src/layout/utils/margin.ts +140 -0
- package/src/layout/utils/node-math.ts +237 -0
- package/src/layout/utils/overflow.ts +14 -0
- package/src/layout/utils/sizing.ts +12 -0
- package/src/layout/utils/text-metrics.ts +361 -0
- package/src/logging/debug.ts +58 -0
- package/src/pdf/font/base14/widths-courier-bold.ts +159 -0
- package/src/pdf/font/base14/widths-courier.ts +159 -0
- package/src/pdf/font/base14/widths-helvetica-bold.ts +158 -0
- package/src/pdf/font/base14/widths-helvetica.ts +158 -0
- package/src/pdf/font/base14/widths-times-bold.ts +158 -0
- package/src/pdf/font/base14/widths-times-roman.ts +158 -0
- package/src/pdf/font/base14/widths-types.ts +25 -0
- package/src/pdf/font/base14-widths.ts +32 -0
- package/src/pdf/font/blur.ts +81 -0
- package/src/pdf/font/builtin-fonts.browser.ts +262 -0
- package/src/pdf/font/builtin-fonts.ts +126 -0
- package/src/pdf/font/composite-glyph-parser.ts +242 -0
- package/src/pdf/font/embedder.ts +395 -0
- package/src/pdf/font/font-config.ts +190 -0
- package/src/pdf/font/font-registry.ts +263 -0
- package/src/pdf/font/font-subset.ts +258 -0
- package/src/pdf/font/glyph-atlas-maxrects.ts +305 -0
- package/src/pdf/font/glyph-atlas-tuner.ts +98 -0
- package/src/pdf/font/glyph-atlas.ts +226 -0
- package/src/pdf/font/glyph-cache.ts +127 -0
- package/src/pdf/font/loca-reader.ts +109 -0
- package/src/pdf/font/managers/font-resource-manager.ts +73 -0
- package/src/pdf/font/managers/subset-resource-manager.ts +164 -0
- package/src/pdf/font/rasterizer.ts +270 -0
- package/src/pdf/font/resolvers/base-font-mapper.ts +77 -0
- package/src/pdf/font/resolvers/family-resolver.ts +33 -0
- package/src/pdf/font/resolvers/weight-style-applicator.ts +63 -0
- package/src/pdf/font/simple-glyph-parser.ts +289 -0
- package/src/pdf/font/to-unicode.ts +109 -0
- package/src/pdf/font/transformation-matrix.ts +136 -0
- package/src/pdf/font/ttf-cmap.ts +180 -0
- package/src/pdf/font/ttf-global-metrics.ts +58 -0
- package/src/pdf/font/ttf-glyf.ts +125 -0
- package/src/pdf/font/ttf-glyph-metrics.ts +43 -0
- package/src/pdf/font/ttf-lite.ts +269 -0
- package/src/pdf/font/ttf-table-parser.ts +132 -0
- package/src/pdf/font/ttf-table-provider.ts +61 -0
- package/src/pdf/font/widths.ts +79 -0
- package/src/pdf/font-subset/font-registry.ts +127 -0
- package/src/pdf/header-footer-layout.ts +153 -0
- package/src/pdf/header-footer-painter.ts +209 -0
- package/src/pdf/header-footer-renderer.ts +357 -0
- package/src/pdf/header-footer-tokens.ts +55 -0
- package/src/pdf/header-footer.ts +25 -0
- package/src/pdf/layout-tree-builder.ts +261 -0
- package/src/pdf/page-painter.ts +241 -0
- package/src/pdf/pagination.ts +155 -0
- package/src/pdf/primitives/pdf-builder.ts +378 -0
- package/src/pdf/primitives/pdf-bytes.ts +40 -0
- package/src/pdf/primitives/pdf-document.ts +108 -0
- package/src/pdf/primitives/pdf-reference-manager.ts +47 -0
- package/src/pdf/primitives/pdf-resource-registries.ts +255 -0
- package/src/pdf/primitives/pdf-serializers.ts +194 -0
- package/src/pdf/primitives/pdf-types.ts +73 -0
- package/src/pdf/render.ts +210 -0
- package/src/pdf/renderer/box-painter.ts +236 -0
- package/src/pdf/renderer/page-paint.ts +102 -0
- package/src/pdf/renderer/paint-box-shadows.ts +218 -0
- package/src/pdf/renderer/radius.ts +58 -0
- package/src/pdf/renderers/graphics-state-manager.ts +40 -0
- package/src/pdf/renderers/image-renderer.ts +127 -0
- package/src/pdf/renderers/radius-utils.ts +80 -0
- package/src/pdf/renderers/rectangle-renderer.ts +129 -0
- package/src/pdf/renderers/rounded-rect-path.ts +120 -0
- package/src/pdf/renderers/shape-renderer.ts +563 -0
- package/src/pdf/renderers/shape-utils.ts +194 -0
- package/src/pdf/renderers/text-decoration-renderer.ts +313 -0
- package/src/pdf/renderers/text-encoder.ts +41 -0
- package/src/pdf/renderers/text-font-resolver.ts +75 -0
- package/src/pdf/renderers/text-renderer-utils.ts +28 -0
- package/src/pdf/renderers/text-renderer.ts +391 -0
- package/src/pdf/renderers/text-shadow-renderer.ts +300 -0
- package/src/pdf/shading/gradient-service.ts +525 -0
- package/src/pdf/shading/index.ts +1 -0
- package/src/pdf/stacking/build-stacking-contexts.ts +93 -0
- package/src/pdf/stacking/resolve-paint-order.ts +157 -0
- package/src/pdf/stacking/types.ts +40 -0
- package/src/pdf/svg/aspect-ratio.ts +81 -0
- package/src/pdf/svg/coordinate-mapper.ts +81 -0
- package/src/pdf/svg/geometry-builder.ts +45 -0
- package/src/pdf/svg/render-svg.ts +296 -0
- package/src/pdf/svg/shape-renderer.ts +463 -0
- package/src/pdf/svg/style-computer.ts +246 -0
- package/src/pdf/transform-adapter.ts +26 -0
- package/src/pdf/types.ts +377 -0
- package/src/pdf/utils/background-layer-resolver.ts +439 -0
- package/src/pdf/utils/background-tiles.ts +192 -0
- package/src/pdf/utils/border-dashes.ts +109 -0
- package/src/pdf/utils/border-radius-utils.ts +86 -0
- package/src/pdf/utils/box-dimensions-utils.ts +47 -0
- package/src/pdf/utils/clip-path-resolver.ts +50 -0
- package/src/pdf/utils/clipping-path-builder.ts +190 -0
- package/src/pdf/utils/color-utils.ts +102 -0
- package/src/pdf/utils/coordinate-transformer.ts +30 -0
- package/src/pdf/utils/drop-shadow-raster.ts +233 -0
- package/src/pdf/utils/encoding.ts +98 -0
- package/src/pdf/utils/glyph-atlas-registrar.ts +13 -0
- package/src/pdf/utils/glyph-run-renderer.ts +129 -0
- package/src/pdf/utils/image-command-partitioner.ts +28 -0
- package/src/pdf/utils/image-matrix-builder.ts +31 -0
- package/src/pdf/utils/image-utils.ts +26 -0
- package/src/pdf/utils/list-utils.ts +194 -0
- package/src/pdf/utils/node-text-run-factory.ts +202 -0
- package/src/pdf/utils/page-resource-registrar.ts +46 -0
- package/src/pdf/utils/result-combiner.ts +102 -0
- package/src/pdf/utils/shadow-utils.ts +127 -0
- package/src/pdf/utils/text-alignment-resolver.ts +76 -0
- package/src/pdf/utils/text-decoration-utils.ts +64 -0
- package/src/pdf/utils/text-layout-adjuster.ts +185 -0
- package/src/pdf/utils/text-utils.ts +193 -0
- package/src/pdf/utils/transform-scope-manager.ts +69 -0
- package/src/render/offset.ts +170 -0
- package/src/shim/empty.ts +2 -0
- package/src/shim/fs-empty.ts +5 -0
- package/src/shim/url-empty.ts +7 -0
- package/src/shim/zlib-empty.ts +9 -0
- package/src/style/shorthands/index.ts +19 -0
- package/src/style/ua/defaults.ts +69 -0
- package/src/svg/index.ts +4 -0
- package/src/svg/parser-registry.ts +71 -0
- package/src/svg/parser.ts +486 -0
- package/src/svg/path-data.ts +515 -0
- package/src/svg/types.ts +194 -0
- package/src/text/line-breaker.ts +321 -0
- package/src/text/text-transform.ts +43 -0
- package/src/text/text.ts +33 -0
- package/src/transform/css-parser.ts +95 -0
- package/src/types/fonts.ts +62 -0
- package/src/types/public.ts +19 -0
- package/src/units/page-utils.ts +58 -0
- package/src/units/units.ts +50 -0
- package/src/utils/base64.ts +24 -0
- package/test-output.txt +79 -0
- package/tests/css/background-parser.spec.ts +14 -0
- package/tests/css/clip-path-parser.spec.ts +66 -0
- package/tests/environment/path-resolution.spec.ts +104 -0
- package/tests/helpers/ai-layout-diagnostics.ts +141 -0
- package/tests/helpers/render-utils.ts +52 -0
- package/tests/helpers/text-geometry.ts +56 -0
- package/tests/layout/custom-properties.test.ts +38 -0
- package/tests/layout/gap-calculator.spec.ts +196 -0
- package/tests/layout/inline-background-alignment.spec.ts +93 -0
- package/tests/layout/inline-fragments.spec.ts +26 -0
- package/tests/layout/run-placer-baseline.spec.ts +108 -0
- package/tests/pdf/alignments.spec.ts +26 -0
- package/tests/pdf/background-clip.spec.ts +57 -0
- package/tests/pdf/background-repeat-space-round.spec.ts +35 -0
- package/tests/pdf/background-repeat.spec.ts +137 -0
- package/tests/pdf/border-radius.spec.ts +151 -0
- package/tests/pdf/clip-path.spec.ts +92 -0
- package/tests/pdf/radial-gradient.spec.ts +50 -0
- package/tests/pdf/svg-stroke-dash.spec.ts +81 -0
- package/tests/pdf/text-transform-matrix.spec.ts +43 -0
- package/tsconfig.json +17 -0
- package/types/fonts.js +10 -0
- package/vitest.config.ts +9 -0
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MaxRects glyph atlas packer (separate module).
|
|
3
|
+
*
|
|
4
|
+
* Implements MaxRects (Best Short Side Fit) packing with basic free-rect
|
|
5
|
+
* splitting and pruning. This lives alongside the existing shelf packer so
|
|
6
|
+
* we can switch to it safely and run tests incrementally.
|
|
7
|
+
*
|
|
8
|
+
* API mirrors glyph-atlas.ts: GlyphAtlasMaxRects with pack/getPages/getPlacement.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export interface AtlasPlacement {
|
|
12
|
+
readonly pageIndex: number;
|
|
13
|
+
readonly x: number;
|
|
14
|
+
readonly y: number;
|
|
15
|
+
readonly width: number;
|
|
16
|
+
readonly height: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface AtlasPage {
|
|
20
|
+
readonly width: number;
|
|
21
|
+
readonly height: number;
|
|
22
|
+
readonly data: Uint8Array; // RGBA
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
type Key = string;
|
|
26
|
+
|
|
27
|
+
const DEFAULT_PAGE_SIZE = 2048;
|
|
28
|
+
const DEFAULT_PADDING = 3;
|
|
29
|
+
|
|
30
|
+
class Rect {
|
|
31
|
+
x: number;
|
|
32
|
+
y: number;
|
|
33
|
+
width: number;
|
|
34
|
+
height: number;
|
|
35
|
+
constructor(x: number, y: number, w: number, h: number) {
|
|
36
|
+
this.x = x; this.y = y; this.width = w; this.height = h;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function fnv1a32(buf: Uint8Array): number {
|
|
41
|
+
let h = 0x811c9dc5;
|
|
42
|
+
for (let i = 0; i < buf.length; i++) {
|
|
43
|
+
h ^= buf[i];
|
|
44
|
+
h = Math.imul(h, 0x01000193) >>> 0;
|
|
45
|
+
}
|
|
46
|
+
return h >>> 0;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export class GlyphAtlasMaxRects {
|
|
50
|
+
private pageSize: number;
|
|
51
|
+
private padding: number;
|
|
52
|
+
private pages: {
|
|
53
|
+
width: number;
|
|
54
|
+
height: number;
|
|
55
|
+
data: Uint8Array;
|
|
56
|
+
freeRects: Rect[];
|
|
57
|
+
usedArea: number;
|
|
58
|
+
lastUsed: number;
|
|
59
|
+
}[] = [];
|
|
60
|
+
private placements = new Map<Key, AtlasPlacement>();
|
|
61
|
+
private hashIndex = new Map<number, AtlasPlacement[]>();
|
|
62
|
+
|
|
63
|
+
// Eviction / memory control (LRU)
|
|
64
|
+
private maxPages = 8;
|
|
65
|
+
setMaxPages(n: number) {
|
|
66
|
+
if (!Number.isFinite(n) || n < 1) return;
|
|
67
|
+
this.maxPages = Math.max(1, Math.floor(n));
|
|
68
|
+
this.evictIfNeeded();
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
constructor(pageSize = DEFAULT_PAGE_SIZE, padding = DEFAULT_PADDING) {
|
|
72
|
+
this.pageSize = Math.max(128, Math.floor(pageSize));
|
|
73
|
+
this.padding = Math.max(0, Math.floor(padding));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
setPageSize(px: number) {
|
|
77
|
+
if (!Number.isFinite(px) || px <= 0) return;
|
|
78
|
+
this.pageSize = Math.max(128, Math.floor(px));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
setPadding(p: number) {
|
|
82
|
+
this.padding = Math.max(0, Math.floor(p));
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
clear() {
|
|
86
|
+
this.pages = [];
|
|
87
|
+
this.placements.clear();
|
|
88
|
+
this.hashIndex.clear();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
getPages(): AtlasPage[] {
|
|
92
|
+
return this.pages.map(p => ({ width: p.width, height: p.height, data: p.data.slice() }));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
has(key: Key) { return this.placements.has(key); }
|
|
96
|
+
getPlacement(key: Key): AtlasPlacement | null { return this.placements.get(key) ?? null; }
|
|
97
|
+
stats() { return { pages: this.pages.length, placements: this.placements.size }; }
|
|
98
|
+
|
|
99
|
+
pack(key: Key, mask: Uint8ClampedArray, w: number, h: number): AtlasPlacement | null {
|
|
100
|
+
if (this.placements.has(key)) return this.placements.get(key)!;
|
|
101
|
+
if (!mask || mask.length !== w * h) return null;
|
|
102
|
+
|
|
103
|
+
const rw = w + this.padding * 2;
|
|
104
|
+
const rh = h + this.padding * 2;
|
|
105
|
+
if (rw <= 0 || rh <= 0) return null;
|
|
106
|
+
|
|
107
|
+
// dedupe
|
|
108
|
+
const hash = fnv1a32(new Uint8Array(mask.buffer, mask.byteOffset, mask.byteLength));
|
|
109
|
+
const candList = this.hashIndex.get(hash);
|
|
110
|
+
if (candList) {
|
|
111
|
+
for (const cand of candList) {
|
|
112
|
+
const page = this.pages[cand.pageIndex];
|
|
113
|
+
if (!page) continue;
|
|
114
|
+
let equal = true;
|
|
115
|
+
for (let row = 0; row < h && equal; row++) {
|
|
116
|
+
const srcOff = row * w;
|
|
117
|
+
const dstRow = (cand.y + row) * page.width;
|
|
118
|
+
for (let col = 0; col < w; col++) {
|
|
119
|
+
const di = (dstRow + (cand.x + col)) * 4 + 3;
|
|
120
|
+
if (mask[srcOff + col] !== page.data[di]) { equal = false; break; }
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
if (equal) { this.placements.set(key, cand); return cand; }
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// try existing pages
|
|
128
|
+
for (let pi = 0; pi < this.pages.length; pi++) {
|
|
129
|
+
const page = this.pages[pi];
|
|
130
|
+
const node = this.findPosition(page.freeRects, rw, rh);
|
|
131
|
+
if (node) {
|
|
132
|
+
const dstX = node.x + this.padding;
|
|
133
|
+
const dstY = node.y + this.padding;
|
|
134
|
+
this.blitToPage(page.data, page.width, page.height, dstX, dstY, mask, w, h);
|
|
135
|
+
page.usedArea += rw * rh;
|
|
136
|
+
page.lastUsed = Date.now();
|
|
137
|
+
this.splitFreeRectangles(node, page.freeRects);
|
|
138
|
+
this.pruneFreeList(page.freeRects);
|
|
139
|
+
const placement: AtlasPlacement = { pageIndex: pi, x: dstX, y: dstY, width: w, height: h };
|
|
140
|
+
this.placements.set(key, placement);
|
|
141
|
+
const list = this.hashIndex.get(hash) ?? [];
|
|
142
|
+
list.push(placement);
|
|
143
|
+
this.hashIndex.set(hash, list);
|
|
144
|
+
return placement;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// create new page
|
|
149
|
+
const pageW = this.pageSize, pageH = this.pageSize;
|
|
150
|
+
if (rw > pageW || rh > pageH) return null;
|
|
151
|
+
const data = new Uint8Array(pageW * pageH * 4);
|
|
152
|
+
const freeRects: Rect[] = [new Rect(0, 0, pageW, pageH)];
|
|
153
|
+
const page = { width: pageW, height: pageH, data, freeRects, usedArea: 0, lastUsed: Date.now() };
|
|
154
|
+
this.pages.push(page);
|
|
155
|
+
// enforce max pages after adding
|
|
156
|
+
this.evictIfNeeded();
|
|
157
|
+
|
|
158
|
+
const node = this.findPosition(page.freeRects, rw, rh);
|
|
159
|
+
if (!node) return null;
|
|
160
|
+
const dstX = node.x + this.padding;
|
|
161
|
+
const dstY = node.y + this.padding;
|
|
162
|
+
this.blitToPage(page.data, page.width, page.height, dstX, dstY, mask, w, h);
|
|
163
|
+
page.usedArea += rw * rh;
|
|
164
|
+
page.lastUsed = Date.now();
|
|
165
|
+
this.splitFreeRectangles(node, page.freeRects);
|
|
166
|
+
this.pruneFreeList(page.freeRects);
|
|
167
|
+
const placement: AtlasPlacement = { pageIndex: this.pages.length - 1, x: dstX, y: dstY, width: w, height: h };
|
|
168
|
+
this.placements.set(key, placement);
|
|
169
|
+
const list = this.hashIndex.get(hash) ?? [];
|
|
170
|
+
list.push(placement);
|
|
171
|
+
this.hashIndex.set(hash, list);
|
|
172
|
+
this.evictIfNeeded();
|
|
173
|
+
return placement;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Best Short Side Fit
|
|
177
|
+
private findPosition(freeRects: Rect[], width: number, height: number): Rect | null {
|
|
178
|
+
let bestRect: Rect | null = null;
|
|
179
|
+
let bestShort = Infinity;
|
|
180
|
+
let bestLong = Infinity;
|
|
181
|
+
for (const r of freeRects) {
|
|
182
|
+
if (r.width >= width && r.height >= height) {
|
|
183
|
+
const leftoverW = r.width - width;
|
|
184
|
+
const leftoverH = r.height - height;
|
|
185
|
+
const shortSide = Math.min(leftoverW, leftoverH);
|
|
186
|
+
const longSide = Math.max(leftoverW, leftoverH);
|
|
187
|
+
if (shortSide < bestShort || (shortSide === bestShort && longSide < bestLong)) {
|
|
188
|
+
bestShort = shortSide; bestLong = longSide;
|
|
189
|
+
bestRect = new Rect(r.x, r.y, width, height);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
// rotated
|
|
193
|
+
if (r.width >= height && r.height >= width) {
|
|
194
|
+
const leftoverW = r.width - height;
|
|
195
|
+
const leftoverH = r.height - width;
|
|
196
|
+
const shortSide = Math.min(leftoverW, leftoverH);
|
|
197
|
+
const longSide = Math.max(leftoverW, leftoverH);
|
|
198
|
+
if (shortSide < bestShort || (shortSide === bestShort && longSide < bestLong)) {
|
|
199
|
+
bestShort = shortSide; bestLong = longSide;
|
|
200
|
+
bestRect = new Rect(r.x, r.y, height, width);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
return bestRect;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
private splitFreeRectangles(placed: Rect, freeRects: Rect[]) {
|
|
208
|
+
const toAdd: Rect[] = [];
|
|
209
|
+
for (let i = freeRects.length - 1; i >= 0; i--) {
|
|
210
|
+
const r = freeRects[i];
|
|
211
|
+
if (!this.intersect(r, placed)) continue;
|
|
212
|
+
freeRects.splice(i, 1);
|
|
213
|
+
if (placed.x > r.x && placed.x < r.x + r.width) toAdd.push(new Rect(r.x, r.y, placed.x - r.x, r.height));
|
|
214
|
+
if (placed.x + placed.width < r.x + r.width) toAdd.push(new Rect(placed.x + placed.width, r.y, (r.x + r.width) - (placed.x + placed.width), r.height));
|
|
215
|
+
if (placed.y > r.y && placed.y < r.y + r.height) toAdd.push(new Rect(r.x, r.y, r.width, placed.y - r.y));
|
|
216
|
+
if (placed.y + placed.height < r.y + r.height) toAdd.push(new Rect(r.x, placed.y + placed.height, r.width, (r.y + r.height) - (placed.y + placed.height)));
|
|
217
|
+
}
|
|
218
|
+
for (const nr of toAdd) if (nr.width > 0 && nr.height > 0) freeRects.push(nr);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
private pruneFreeList(freeRects: Rect[]) {
|
|
222
|
+
for (let i = 0; i < freeRects.length; i++) {
|
|
223
|
+
for (let j = i + 1; j < freeRects.length; j++) {
|
|
224
|
+
const a = freeRects[i], b = freeRects[j];
|
|
225
|
+
if (this.isContained(a, b)) { freeRects.splice(i, 1); i--; break; }
|
|
226
|
+
if (this.isContained(b, a)) { freeRects.splice(j, 1); j--; }
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
private intersect(a: Rect, b: Rect) {
|
|
232
|
+
return !(b.x >= a.x + a.width || b.x + b.width <= a.x || b.y >= a.y + a.height || b.y + b.height <= a.y);
|
|
233
|
+
}
|
|
234
|
+
private isContained(a: Rect, b: Rect) {
|
|
235
|
+
return a.x >= b.x && a.y >= b.y && (a.x + a.width) <= (b.x + b.width) && (a.y + a.height) <= (b.y + b.height);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
private evictIfNeeded() {
|
|
239
|
+
while (this.pages.length > this.maxPages) {
|
|
240
|
+
this.evictLeastRecentlyUsedPage();
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
private evictLeastRecentlyUsedPage() {
|
|
245
|
+
if (this.pages.length === 0) return;
|
|
246
|
+
let oldest = 0;
|
|
247
|
+
let oldestTime = Infinity;
|
|
248
|
+
for (let i = 0; i < this.pages.length; i++) {
|
|
249
|
+
const p = this.pages[i];
|
|
250
|
+
if (p.lastUsed < oldestTime) {
|
|
251
|
+
oldestTime = p.lastUsed;
|
|
252
|
+
oldest = i;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
this.evictPage(oldest);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
private evictPage(idx: number) {
|
|
259
|
+
if (idx < 0 || idx >= this.pages.length) return;
|
|
260
|
+
// Remove page data
|
|
261
|
+
this.pages.splice(idx, 1);
|
|
262
|
+
// Remove placements that pointed to this page and adjust pageIndex for later pages
|
|
263
|
+
for (const [k, placement] of Array.from(this.placements.entries())) {
|
|
264
|
+
if (placement.pageIndex === idx) {
|
|
265
|
+
this.placements.delete(k);
|
|
266
|
+
} else if (placement.pageIndex > idx) {
|
|
267
|
+
this.placements.set(k, { ...placement, pageIndex: placement.pageIndex - 1 });
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
// Rebuild hashIndex: remove entries referencing removed page and adjust indices
|
|
271
|
+
const newHash = new Map<number, AtlasPlacement[]>();
|
|
272
|
+
for (const [h, list] of this.hashIndex.entries()) {
|
|
273
|
+
const newList: AtlasPlacement[] = [];
|
|
274
|
+
for (const p of list) {
|
|
275
|
+
if (p.pageIndex === idx) continue;
|
|
276
|
+
if (p.pageIndex > idx) {
|
|
277
|
+
newList.push({ ...p, pageIndex: p.pageIndex - 1 });
|
|
278
|
+
} else {
|
|
279
|
+
newList.push(p);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
if (newList.length > 0) newHash.set(h, newList);
|
|
283
|
+
}
|
|
284
|
+
this.hashIndex = newHash;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
private blitToPage(pageData: Uint8Array, pageW: number, _pageH: number, dstX: number, dstY: number, mask: Uint8ClampedArray, w: number, h: number) {
|
|
288
|
+
for (let row = 0; row < h; row++) {
|
|
289
|
+
const srcOff = row * w;
|
|
290
|
+
const dstRow = (dstY + row) * pageW;
|
|
291
|
+
let dstCol = dstX;
|
|
292
|
+
for (let col = 0; col < w; col++) {
|
|
293
|
+
const a = mask[srcOff + col];
|
|
294
|
+
const di = (dstRow + dstCol) * 4;
|
|
295
|
+
pageData[di] = 0;
|
|
296
|
+
pageData[di + 1] = 0;
|
|
297
|
+
pageData[di + 2] = 0;
|
|
298
|
+
pageData[di + 3] = a;
|
|
299
|
+
dstCol++;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
export const globalGlyphAtlasMaxRects = new GlyphAtlasMaxRects(DEFAULT_PAGE_SIZE, DEFAULT_PADDING);
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple heuristic tuner for glyph atlas parameters.
|
|
3
|
+
*
|
|
4
|
+
* Exports pickAtlasSettingsFromSamples(samples)
|
|
5
|
+
* - samples: Array of { width:number, height:number } representing a sample
|
|
6
|
+
* of glyph mask sizes (in px) for a font/size combination.
|
|
7
|
+
*
|
|
8
|
+
* Heuristic:
|
|
9
|
+
* - Try candidate page sizes (1024, 2048, 4096)
|
|
10
|
+
* - Try padding values (0..3)
|
|
11
|
+
* - For each (pageSize, padding) compute:
|
|
12
|
+
* * If any glyph (w+2p > pageSize or h+2p > pageSize) -> invalid
|
|
13
|
+
* * totalPaddedArea = sum((w+2p)*(h+2p))
|
|
14
|
+
* * estimatedPages = ceil(totalPaddedArea / (pageSize*pageSize))
|
|
15
|
+
* * wastedRatio = estimatedPages * pageSize*pageSize / totalPaddedArea
|
|
16
|
+
* - Prefer configurations with smallest estimatedPages; tie-break by smallest wastedRatio,
|
|
17
|
+
* then smaller pageSize and smaller padding.
|
|
18
|
+
*
|
|
19
|
+
* This is fast and conservative. It doesn't run an actual packer; it's only a
|
|
20
|
+
* lightweight selector to choose sensible defaults for pageSize and padding.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
export interface AtlasSettings {
|
|
24
|
+
pageSize: number;
|
|
25
|
+
padding: number;
|
|
26
|
+
estimatedPages: number;
|
|
27
|
+
wastedRatio: number;
|
|
28
|
+
valid: boolean;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function pickAtlasSettingsFromSamples(
|
|
32
|
+
samples: Array<{ width: number; height: number }>,
|
|
33
|
+
opts?: { candidates?: number[]; paddings?: number[] }
|
|
34
|
+
): AtlasSettings {
|
|
35
|
+
const candidates = opts?.candidates ?? [1024, 2048, 4096];
|
|
36
|
+
const paddings = opts?.paddings ?? [0, 1, 2, 3];
|
|
37
|
+
|
|
38
|
+
// sanitize samples
|
|
39
|
+
const filtered = (samples || []).map(s => ({ width: Math.max(0, Math.floor(s.width)), height: Math.max(0, Math.floor(s.height)) }));
|
|
40
|
+
if (filtered.length === 0) {
|
|
41
|
+
// default safe
|
|
42
|
+
return { pageSize: 2048, padding: 1, estimatedPages: 1, wastedRatio: 1, valid: true };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
let best: AtlasSettings | null = null;
|
|
46
|
+
|
|
47
|
+
for (const pageSize of candidates) {
|
|
48
|
+
for (const padding of paddings) {
|
|
49
|
+
let invalid = false;
|
|
50
|
+
let totalPaddedArea = 0;
|
|
51
|
+
for (const s of filtered) {
|
|
52
|
+
const pw = s.width + padding * 2;
|
|
53
|
+
const ph = s.height + padding * 2;
|
|
54
|
+
if (pw > pageSize || ph > pageSize) {
|
|
55
|
+
invalid = true;
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
totalPaddedArea += pw * ph;
|
|
59
|
+
}
|
|
60
|
+
if (invalid) continue;
|
|
61
|
+
const pageArea = pageSize * pageSize;
|
|
62
|
+
const estimatedPages = Math.max(1, Math.ceil(totalPaddedArea / pageArea));
|
|
63
|
+
const wastedRatio = (estimatedPages * pageArea) / Math.max(1, totalPaddedArea);
|
|
64
|
+
|
|
65
|
+
const candidate: AtlasSettings = {
|
|
66
|
+
pageSize,
|
|
67
|
+
padding,
|
|
68
|
+
estimatedPages,
|
|
69
|
+
wastedRatio,
|
|
70
|
+
valid: true,
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
if (!best) {
|
|
74
|
+
best = candidate;
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// prefer fewer pages, then lower wastedRatio, then smaller pageSize, then smaller padding
|
|
79
|
+
if (candidate.estimatedPages < best.estimatedPages) {
|
|
80
|
+
best = candidate;
|
|
81
|
+
} else if (candidate.estimatedPages === best.estimatedPages) {
|
|
82
|
+
if (candidate.wastedRatio < best.wastedRatio - 1e-9) {
|
|
83
|
+
best = candidate;
|
|
84
|
+
} else if (Math.abs(candidate.wastedRatio - best.wastedRatio) < 1e-9) {
|
|
85
|
+
if (candidate.pageSize < best.pageSize) best = candidate;
|
|
86
|
+
else if (candidate.pageSize === best.pageSize && candidate.padding < best.padding) best = candidate;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (!best) {
|
|
93
|
+
// fallback conservative
|
|
94
|
+
return { pageSize: 2048, padding: 1, estimatedPages: 1, wastedRatio: 1, valid: false };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return best;
|
|
98
|
+
}
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple shelf-based glyph atlas packer.
|
|
3
|
+
*
|
|
4
|
+
* Purpose:
|
|
5
|
+
* - Pack many small glyph alpha masks into one or more RGBA atlas pages.
|
|
6
|
+
* - Provide placement metadata so caller can later extract or reference the region.
|
|
7
|
+
*
|
|
8
|
+
* Notes:
|
|
9
|
+
* - This implementation focuses on correctness and simplicity (shelf packer).
|
|
10
|
+
* - Atlas pages are stored as RGBA Uint8Array buffers where R=G=B=0 and A=mask.
|
|
11
|
+
* - Does NOT yet integrate automatically with PDF image registration;
|
|
12
|
+
* that will be done in the rendering path in a follow-up step.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
export interface AtlasPlacement {
|
|
16
|
+
readonly pageIndex: number;
|
|
17
|
+
readonly x: number;
|
|
18
|
+
readonly y: number;
|
|
19
|
+
readonly width: number;
|
|
20
|
+
readonly height: number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface AtlasPage {
|
|
24
|
+
readonly width: number;
|
|
25
|
+
readonly height: number;
|
|
26
|
+
readonly data: Uint8Array; // RGBA
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
type Key = string;
|
|
30
|
+
|
|
31
|
+
const DEFAULT_PAGE_SIZE = 2048; // square atlas page default
|
|
32
|
+
// Increase base padding to reduce risk of blur bleeding between packed glyphs.
|
|
33
|
+
// We still add extraPadding from the caller (e.g. glyph-cache) for blur radius.
|
|
34
|
+
const PADDING = 2; // padding around glyphs to avoid bleeding
|
|
35
|
+
|
|
36
|
+
class Shelf {
|
|
37
|
+
y: number;
|
|
38
|
+
height: number;
|
|
39
|
+
xCursor: number;
|
|
40
|
+
remaining: number;
|
|
41
|
+
constructor(y: number, height: number, pageWidth: number) {
|
|
42
|
+
this.y = y;
|
|
43
|
+
this.height = height;
|
|
44
|
+
this.xCursor = 0;
|
|
45
|
+
this.remaining = pageWidth;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* AtlasManager
|
|
51
|
+
*/
|
|
52
|
+
export class GlyphAtlas {
|
|
53
|
+
private pageSize = DEFAULT_PAGE_SIZE;
|
|
54
|
+
private pages: { width: number; height: number; data: Uint8Array; shelves: Shelf[]; used: number; lastUsed: number }[] = [];
|
|
55
|
+
private placements = new Map<Key, AtlasPlacement>();
|
|
56
|
+
// LRU eviction control for shelf atlas
|
|
57
|
+
private maxPages = 8;
|
|
58
|
+
setMaxPages(n: number) {
|
|
59
|
+
if (!Number.isFinite(n) || n < 1) return;
|
|
60
|
+
this.maxPages = Math.max(1, Math.floor(n));
|
|
61
|
+
this.evictIfNeeded();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
constructor(pageSize = DEFAULT_PAGE_SIZE) {
|
|
65
|
+
this.pageSize = pageSize;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
clear() {
|
|
69
|
+
this.pages = [];
|
|
70
|
+
this.placements.clear();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
getPages(): AtlasPage[] {
|
|
74
|
+
return this.pages.map((p) => ({ width: p.width, height: p.height, data: p.data.slice() }));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
has(key: Key): boolean {
|
|
78
|
+
return this.placements.has(key);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
getPlacement(key: Key): AtlasPlacement | null {
|
|
82
|
+
return this.placements.get(key) ?? null;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Pack an alpha mask into the atlas.
|
|
87
|
+
* - key: unique identifier for the glyph (e.g., fontUid|gid|size|ss)
|
|
88
|
+
* - mask: Uint8ClampedArray length = w*h
|
|
89
|
+
* - w,h: dimensions
|
|
90
|
+
* Returns placement or null on failure.
|
|
91
|
+
*/
|
|
92
|
+
pack(key: Key, mask: Uint8ClampedArray, w: number, h: number, extraPadding = 0): AtlasPlacement | null {
|
|
93
|
+
if (this.placements.has(key)) {
|
|
94
|
+
return this.placements.get(key)!;
|
|
95
|
+
}
|
|
96
|
+
const pw = this.pageSize;
|
|
97
|
+
const ph = this.pageSize;
|
|
98
|
+
const pad = PADDING + Math.max(0, Math.floor(extraPadding));
|
|
99
|
+
const rw = w + pad * 2;
|
|
100
|
+
const rh = h + pad * 2;
|
|
101
|
+
|
|
102
|
+
// Try existing pages
|
|
103
|
+
for (let pi = 0; pi < this.pages.length; pi++) {
|
|
104
|
+
const page = this.pages[pi];
|
|
105
|
+
// try to fit in existing shelves
|
|
106
|
+
for (const shelf of page.shelves) {
|
|
107
|
+
if (rh <= shelf.height && rw <= shelf.remaining) {
|
|
108
|
+
// place it
|
|
109
|
+
const x = shelf.xCursor + pad;
|
|
110
|
+
const y = shelf.y + pad;
|
|
111
|
+
this.blitToPage(page.data, page.width, page.height, x, y, mask, w, h);
|
|
112
|
+
shelf.xCursor += rw;
|
|
113
|
+
shelf.remaining -= rw;
|
|
114
|
+
page.used += rw * rh;
|
|
115
|
+
page.lastUsed = Date.now();
|
|
116
|
+
const placement: AtlasPlacement = { pageIndex: pi, x, y, width: w, height: h };
|
|
117
|
+
this.placements.set(key, placement);
|
|
118
|
+
return placement;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// If no shelf fits, try to create a new shelf at bottom if space allows
|
|
123
|
+
const usedHeight = page.shelves.reduce((s, sh) => s + sh.height, 0);
|
|
124
|
+
if (usedHeight + rh <= page.height) {
|
|
125
|
+
const shelfY = usedHeight;
|
|
126
|
+
const shelf = new Shelf(shelfY, rh, page.width);
|
|
127
|
+
page.shelves.push(shelf);
|
|
128
|
+
// place at start
|
|
129
|
+
const x = shelf.xCursor + pad;
|
|
130
|
+
const y = shelf.y + pad;
|
|
131
|
+
this.blitToPage(page.data, page.width, page.height, x, y, mask, w, h);
|
|
132
|
+
shelf.xCursor += rw;
|
|
133
|
+
shelf.remaining -= rw;
|
|
134
|
+
page.used += rw * rh;
|
|
135
|
+
page.lastUsed = Date.now();
|
|
136
|
+
const placement: AtlasPlacement = { pageIndex: pi, x, y, width: w, height: h };
|
|
137
|
+
this.placements.set(key, placement);
|
|
138
|
+
return placement;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// No existing page fits: create a new page
|
|
143
|
+
const pageW = pw;
|
|
144
|
+
const pageH = ph;
|
|
145
|
+
const pageData = new Uint8Array(pageW * pageH * 4); // RGBA
|
|
146
|
+
// default rgba zeros
|
|
147
|
+
const newPage = { width: pageW, height: pageH, data: pageData, shelves: [] as Shelf[], used: 0, lastUsed: Date.now() };
|
|
148
|
+
// create first shelf
|
|
149
|
+
if (rh > pageH || rw > pageW) {
|
|
150
|
+
// too large for page
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
const shelf = new Shelf(0, rh, pageW);
|
|
154
|
+
newPage.shelves.push(shelf);
|
|
155
|
+
this.pages.push(newPage);
|
|
156
|
+
// enforce max pages after adding page
|
|
157
|
+
this.evictIfNeeded();
|
|
158
|
+
const x = shelf.xCursor + pad;
|
|
159
|
+
const y = shelf.y + pad;
|
|
160
|
+
this.blitToPage(newPage.data, newPage.width, newPage.height, x, y, mask, w, h);
|
|
161
|
+
shelf.xCursor += rw;
|
|
162
|
+
shelf.remaining -= rw;
|
|
163
|
+
newPage.used += rw * rh;
|
|
164
|
+
newPage.lastUsed = Date.now();
|
|
165
|
+
const placement: AtlasPlacement = { pageIndex: this.pages.length - 1, x, y, width: w, height: h };
|
|
166
|
+
this.placements.set(key, placement);
|
|
167
|
+
return placement;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
private evictIfNeeded() {
|
|
171
|
+
while (this.pages.length > this.maxPages) {
|
|
172
|
+
this.evictLeastRecentlyUsedPage();
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
private evictLeastRecentlyUsedPage() {
|
|
177
|
+
if (this.pages.length === 0) return;
|
|
178
|
+
let oldest = 0;
|
|
179
|
+
let oldestTime = Infinity;
|
|
180
|
+
for (let i = 0; i < this.pages.length; i++) {
|
|
181
|
+
const p = this.pages[i];
|
|
182
|
+
if (p.lastUsed < oldestTime) {
|
|
183
|
+
oldestTime = p.lastUsed;
|
|
184
|
+
oldest = i;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
this.evictPage(oldest);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
private evictPage(idx: number) {
|
|
191
|
+
if (idx < 0 || idx >= this.pages.length) return;
|
|
192
|
+
// Remove page data
|
|
193
|
+
this.pages.splice(idx, 1);
|
|
194
|
+
// Remove placements that pointed to this page and adjust pageIndex for later pages
|
|
195
|
+
for (const [k, placement] of Array.from(this.placements.entries())) {
|
|
196
|
+
if (placement.pageIndex === idx) {
|
|
197
|
+
this.placements.delete(k);
|
|
198
|
+
} else if (placement.pageIndex > idx) {
|
|
199
|
+
this.placements.set(k, { ...placement, pageIndex: placement.pageIndex - 1 });
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
// NOTE: shelf atlas does not maintain a hashIndex for dedupe.
|
|
203
|
+
// Eviction only updates placements above; nothing more to adjust here.
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
private blitToPage(pageData: Uint8Array, pageW: number, _pageH: number, dstX: number, dstY: number, mask: Uint8ClampedArray, w: number, h: number) {
|
|
207
|
+
// pageData is RGBA; we write R=G=B=0, A = mask
|
|
208
|
+
for (let row = 0; row < h; row++) {
|
|
209
|
+
const srcOff = row * w;
|
|
210
|
+
const dstRow = (dstY + row) * pageW;
|
|
211
|
+
let dstCol = dstX;
|
|
212
|
+
for (let col = 0; col < w; col++) {
|
|
213
|
+
const a = mask[srcOff + col];
|
|
214
|
+
const di = (dstRow + dstCol) * 4;
|
|
215
|
+
pageData[di] = 0;
|
|
216
|
+
pageData[di + 1] = 0;
|
|
217
|
+
pageData[di + 2] = 0;
|
|
218
|
+
pageData[di + 3] = a;
|
|
219
|
+
dstCol++;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/** Singleton atlas for now (can be replaced with per-font/per-size atlases later) */
|
|
226
|
+
export const globalGlyphAtlas = new GlyphAtlas(DEFAULT_PAGE_SIZE);
|