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