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,262 @@
|
|
|
1
|
+
import { log } from "../../logging/debug.js";
|
|
2
|
+
import type { Environment } from "../../environment/environment.js";
|
|
3
|
+
import { BrowserEnvironment } from "../../environment/browser-environment.js";
|
|
4
|
+
import type { FontConfig, FontFaceDef } from "../../types/fonts.js";
|
|
5
|
+
|
|
6
|
+
type FontSource = "assets" | "google";
|
|
7
|
+
|
|
8
|
+
type BuiltinFace = Omit<FontFaceDef, "src" | "data"> & { file: string; google?: string };
|
|
9
|
+
|
|
10
|
+
const BUILTIN_FACES: BuiltinFace[] = [
|
|
11
|
+
// Sans: primary and UI-friendly
|
|
12
|
+
{ name: "Lato-Regular", family: "Lato", weight: 400, style: "normal", file: "woff2/lato/lato-latin-400-normal.woff2", google: "Lato" },
|
|
13
|
+
{ name: "Lato-Bold", family: "Lato", weight: 700, style: "normal", file: "woff2/lato/lato-latin-700-normal.woff2", google: "Lato" },
|
|
14
|
+
{ name: "Lato-Italic", family: "Lato", weight: 400, style: "italic", file: "woff2/lato/lato-latin-400-italic.woff2", google: "Lato" },
|
|
15
|
+
{ name: "Lato-BoldItalic", family: "Lato", weight: 700, style: "italic", file: "woff2/lato/lato-latin-700-italic.woff2", google: "Lato" },
|
|
16
|
+
{ name: "Roboto-Regular", family: "Roboto", weight: 400, style: "normal", file: "ttf/roboto/Roboto-Regular.ttf", google: "Roboto" },
|
|
17
|
+
{ name: "Roboto-Bold", family: "Roboto", weight: 700, style: "normal", file: "ttf/roboto/Roboto-Bold.ttf", google: "Roboto" },
|
|
18
|
+
{ name: "Roboto-Italic", family: "Roboto", weight: 400, style: "italic", file: "ttf/roboto/Roboto-Italic.ttf", google: "Roboto" },
|
|
19
|
+
{ name: "Roboto-BoldItalic", family: "Roboto", weight: 700, style: "italic", file: "ttf/roboto/Roboto-BoldItalic.ttf", google: "Roboto" },
|
|
20
|
+
{ name: "Arimo-Regular", family: "Arimo", weight: 400, style: "normal", file: "ttf/arimo/Arimo-Regular.ttf", google: "Arimo" },
|
|
21
|
+
{ name: "Arimo-Bold", family: "Arimo", weight: 700, style: "normal", file: "ttf/arimo/Arimo-Bold.ttf", google: "Arimo" },
|
|
22
|
+
{ name: "Arimo-Italic", family: "Arimo", weight: 400, style: "italic", file: "ttf/arimo/Arimo-Italic.ttf", google: "Arimo" },
|
|
23
|
+
{ name: "Arimo-BoldItalic", family: "Arimo", weight: 700, style: "italic", file: "ttf/arimo/Arimo-BoldItalic.ttf", google: "Arimo" },
|
|
24
|
+
{ name: "NotoSans-Regular", family: "Noto Sans", weight: 400, style: "normal", file: "ttf/notosans/NotoSans-Regular.ttf", google: "Noto Sans" },
|
|
25
|
+
{ name: "DejaVuSans-Regular", family: "DejaVu Sans", weight: 400, style: "normal", file: "ttf/dejavu/DejaVuSans.ttf" },
|
|
26
|
+
|
|
27
|
+
// Serif / display
|
|
28
|
+
{ name: "Tinos-Regular", family: "Tinos", weight: 400, style: "normal", file: "ttf/tinos/Tinos-Regular.ttf", google: "Tinos" },
|
|
29
|
+
{ name: "Tinos-Bold", family: "Tinos", weight: 700, style: "normal", file: "ttf/tinos/Tinos-Bold.ttf", google: "Tinos" },
|
|
30
|
+
{ name: "Tinos-Italic", family: "Tinos", weight: 400, style: "italic", file: "ttf/tinos/Tinos-Italic.ttf", google: "Tinos" },
|
|
31
|
+
{ name: "Tinos-BoldItalic", family: "Tinos", weight: 700, style: "italic", file: "ttf/tinos/Tinos-BoldItalic.ttf", google: "Tinos" },
|
|
32
|
+
{ name: "CinzelDecorative-Regular", family: "Cinzel Decorative", weight: 400, style: "normal", file: "ttf/cinzeldecorative/CinzelDecorative-Regular.ttf", google: "Cinzel Decorative" },
|
|
33
|
+
{ name: "CinzelDecorative-Bold", family: "Cinzel Decorative", weight: 700, style: "normal", file: "ttf/cinzeldecorative/CinzelDecorative-Bold.ttf", google: "Cinzel Decorative" },
|
|
34
|
+
{ name: "CinzelDecorative-Black", family: "Cinzel Decorative", weight: 900, style: "normal", file: "ttf/cinzeldecorative/CinzelDecorative-Black.ttf", google: "Cinzel Decorative" },
|
|
35
|
+
{ name: "Caveat-Regular", family: "Caveat", weight: 400, style: "normal", file: "woff2/caveat/Caveat-Regular.woff2", google: "Caveat" },
|
|
36
|
+
{ name: "Caveat-Bold", family: "Caveat", weight: 700, style: "normal", file: "woff2/caveat/Caveat-Bold.woff2", google: "Caveat" },
|
|
37
|
+
|
|
38
|
+
// Monospace
|
|
39
|
+
{ name: "FiraCode-Light", family: "Fira Code", weight: 300, style: "normal", file: "ttf/firecode/FiraCode-Light.ttf", google: "Fira Code" },
|
|
40
|
+
{ name: "FiraCode-Regular", family: "Fira Code", weight: 400, style: "normal", file: "ttf/firecode/FiraCode-Regular.ttf", google: "Fira Code" },
|
|
41
|
+
{ name: "FiraCode-Medium", family: "Fira Code", weight: 500, style: "normal", file: "ttf/firecode/FiraCode-Medium.ttf", google: "Fira Code" },
|
|
42
|
+
{ name: "FiraCode-SemiBold", family: "Fira Code", weight: 600, style: "normal", file: "ttf/firecode/FiraCode-SemiBold.ttf", google: "Fira Code" },
|
|
43
|
+
{ name: "FiraCode-Bold", family: "Fira Code", weight: 700, style: "normal", file: "ttf/firecode/FiraCode-Bold.ttf", google: "Fira Code" },
|
|
44
|
+
|
|
45
|
+
// Emoji
|
|
46
|
+
{ name: "NotoEmoji-Light", family: "Noto Emoji", weight: 300, style: "normal", file: "ttf/notoemoji/NotoEmoji-Light.ttf" },
|
|
47
|
+
{ name: "NotoEmoji-Regular", family: "Noto Emoji", weight: 400, style: "normal", file: "ttf/notoemoji/NotoEmoji-Regular.ttf" },
|
|
48
|
+
{ name: "NotoEmoji-Medium", family: "Noto Emoji", weight: 500, style: "normal", file: "ttf/notoemoji/NotoEmoji-Medium.ttf" },
|
|
49
|
+
{ name: "NotoEmoji-SemiBold", family: "Noto Emoji", weight: 600, style: "normal", file: "ttf/notoemoji/NotoEmoji-SemiBold.ttf" },
|
|
50
|
+
{ name: "NotoEmoji-Bold", family: "Noto Emoji", weight: 700, style: "normal", file: "ttf/notoemoji/NotoEmoji-Bold.ttf" },
|
|
51
|
+
|
|
52
|
+
// Math
|
|
53
|
+
{ name: "STIXTwoMath-Regular", family: "STIX Two Math", weight: 400, style: "normal", file: "ttf/stixtwomath/STIXTwoMath-Regular.ttf" },
|
|
54
|
+
];
|
|
55
|
+
|
|
56
|
+
const DEFAULT_STACK: string[] = [
|
|
57
|
+
"Lato",
|
|
58
|
+
"Roboto",
|
|
59
|
+
"Arimo",
|
|
60
|
+
"Noto Sans",
|
|
61
|
+
"DejaVu Sans",
|
|
62
|
+
"Tinos",
|
|
63
|
+
"Fira Code",
|
|
64
|
+
"Caveat",
|
|
65
|
+
"Cinzel Decorative",
|
|
66
|
+
"Noto Emoji",
|
|
67
|
+
"STIX Two Math",
|
|
68
|
+
];
|
|
69
|
+
|
|
70
|
+
let cachedConfig: FontConfig | null | undefined;
|
|
71
|
+
let cachedSource: FontSource | null = null;
|
|
72
|
+
let loading: Promise<FontConfig | null> | null = null;
|
|
73
|
+
|
|
74
|
+
export async function loadBuiltinFontConfig(environment: Environment = new BrowserEnvironment()): Promise<FontConfig | null> {
|
|
75
|
+
const source = resolveFontSource();
|
|
76
|
+
if (cachedConfig !== undefined && cachedSource === source) {
|
|
77
|
+
return cachedConfig;
|
|
78
|
+
}
|
|
79
|
+
if (loading && cachedSource === source) {
|
|
80
|
+
return loading;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
cachedSource = source;
|
|
84
|
+
|
|
85
|
+
loading = (async () => {
|
|
86
|
+
// Try the selected source; if google fails and was explicitly selected, fall back to assets.
|
|
87
|
+
if (source === "google") {
|
|
88
|
+
const googleFaces = await loadFromGoogleFonts(environment);
|
|
89
|
+
if (googleFaces.length) {
|
|
90
|
+
cachedConfig = { fontFaceDefs: googleFaces, defaultStack: DEFAULT_STACK };
|
|
91
|
+
return cachedConfig;
|
|
92
|
+
}
|
|
93
|
+
log("font", "warn", "Google Fonts requested but none loaded; falling back to bundled assets");
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const assetFaces = await loadFromAssets(environment);
|
|
97
|
+
cachedConfig = assetFaces.length ? { fontFaceDefs: assetFaces, defaultStack: DEFAULT_STACK } : null;
|
|
98
|
+
return cachedConfig;
|
|
99
|
+
})();
|
|
100
|
+
|
|
101
|
+
try {
|
|
102
|
+
return await loading;
|
|
103
|
+
} finally {
|
|
104
|
+
loading = null;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function computeBaseUrl(): string {
|
|
109
|
+
const globalBase = (globalThis as any).__PAGYRA_FONT_BASE;
|
|
110
|
+
if (typeof globalBase === "string" && globalBase.trim().length > 0) {
|
|
111
|
+
return ensureTrailingSlash(globalBase.trim());
|
|
112
|
+
}
|
|
113
|
+
if (typeof window !== "undefined" && window.location) {
|
|
114
|
+
const origin = window.location.origin.endsWith("/") ? window.location.origin : `${window.location.origin}/`;
|
|
115
|
+
return `${origin}assets/fonts/`;
|
|
116
|
+
}
|
|
117
|
+
try {
|
|
118
|
+
return new URL("../../assets/fonts/", import.meta.url).toString();
|
|
119
|
+
} catch {
|
|
120
|
+
return "/assets/fonts/";
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function ensureTrailingSlash(input: string): string {
|
|
125
|
+
return input.endsWith("/") ? input : `${input}/`;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function resolveFontSource(): FontSource {
|
|
129
|
+
const globalSource = (globalThis as any).__PAGYRA_FONT_SOURCE;
|
|
130
|
+
const legacyGoogle = (globalThis as any).__PAGYRA_USE_GOOGLE_FONTS;
|
|
131
|
+
if (typeof globalSource === "string") {
|
|
132
|
+
const normalized = globalSource.toLowerCase();
|
|
133
|
+
if (normalized === "google") return "google";
|
|
134
|
+
if (normalized === "assets") return "assets";
|
|
135
|
+
}
|
|
136
|
+
if (legacyGoogle === true) {
|
|
137
|
+
return "google";
|
|
138
|
+
}
|
|
139
|
+
return "assets";
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
async function loadFromAssets(environment: Environment): Promise<FontFaceDef[]> {
|
|
143
|
+
const baseUrl = computeBaseUrl();
|
|
144
|
+
const faces: FontFaceDef[] = [];
|
|
145
|
+
for (const face of BUILTIN_FACES) {
|
|
146
|
+
const url = new URL(face.file, baseUrl).toString();
|
|
147
|
+
try {
|
|
148
|
+
log("font", "debug", "Loading browser font file", { url });
|
|
149
|
+
const buffer = await environment.loader.load(url);
|
|
150
|
+
faces.push({
|
|
151
|
+
name: face.name,
|
|
152
|
+
family: face.family,
|
|
153
|
+
weight: face.weight,
|
|
154
|
+
style: face.style,
|
|
155
|
+
src: url,
|
|
156
|
+
data: buffer,
|
|
157
|
+
});
|
|
158
|
+
} catch (error) {
|
|
159
|
+
log("font", "warn", "Failed to load font file", { url, error });
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return faces;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
async function loadFromGoogleFonts(environment: Environment): Promise<FontFaceDef[]> {
|
|
166
|
+
const requestable = BUILTIN_FACES.filter((f) => !!f.google);
|
|
167
|
+
if (!requestable.length) return [];
|
|
168
|
+
|
|
169
|
+
const cssUrl = buildGoogleCssUrl(requestable);
|
|
170
|
+
if (!cssUrl) return [];
|
|
171
|
+
|
|
172
|
+
try {
|
|
173
|
+
log("font", "debug", "Fetching Google Fonts CSS", { url: cssUrl });
|
|
174
|
+
const cssBuffer = await environment.loader.load(cssUrl);
|
|
175
|
+
const cssText = new TextDecoder().decode(cssBuffer);
|
|
176
|
+
const faceUrls = parseGoogleCss(cssText);
|
|
177
|
+
|
|
178
|
+
const faces: FontFaceDef[] = [];
|
|
179
|
+
for (const face of requestable) {
|
|
180
|
+
const key = faceKey(face.family, face.weight, face.style);
|
|
181
|
+
const url = faceUrls.get(key);
|
|
182
|
+
if (!url) {
|
|
183
|
+
log("font", "warn", "Google Fonts entry missing", { family: face.family, weight: face.weight, style: face.style });
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
try {
|
|
187
|
+
log("font", "debug", "Downloading Google font file", { url, family: face.family, weight: face.weight, style: face.style });
|
|
188
|
+
const buffer = await environment.loader.load(url);
|
|
189
|
+
faces.push({
|
|
190
|
+
name: face.name,
|
|
191
|
+
family: face.family,
|
|
192
|
+
weight: face.weight,
|
|
193
|
+
style: face.style,
|
|
194
|
+
src: url,
|
|
195
|
+
data: buffer,
|
|
196
|
+
});
|
|
197
|
+
} catch (error) {
|
|
198
|
+
log("font", "warn", "Failed to download Google font", { url, family: face.family, weight: face.weight, style: face.style, error });
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return faces;
|
|
202
|
+
} catch (error) {
|
|
203
|
+
log("font", "warn", "Failed to load Google Fonts CSS", { url: cssUrl, error });
|
|
204
|
+
return [];
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function buildGoogleCssUrl(faces: BuiltinFace[]): string {
|
|
209
|
+
const byFamily = new Map<string, Set<string>>();
|
|
210
|
+
for (const face of faces) {
|
|
211
|
+
if (!face.google) continue;
|
|
212
|
+
const key = face.google;
|
|
213
|
+
let set = byFamily.get(key);
|
|
214
|
+
if (!set) {
|
|
215
|
+
set = new Set();
|
|
216
|
+
byFamily.set(key, set);
|
|
217
|
+
}
|
|
218
|
+
const ital = face.style === "italic" ? 1 : 0;
|
|
219
|
+
set.add(`${ital},${face.weight}`);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const families: string[] = [];
|
|
223
|
+
for (const [family, combos] of byFamily) {
|
|
224
|
+
const comboList = Array.from(combos).sort();
|
|
225
|
+
const param = `family=${encodeGoogleFamily(family)}:ital,wght@${comboList.join(";")}`;
|
|
226
|
+
families.push(param);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (!families.length) return "";
|
|
230
|
+
return `https://fonts.googleapis.com/css2?${families.join("&")}&display=swap`;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function encodeGoogleFamily(family: string): string {
|
|
234
|
+
return encodeURIComponent(family.replace(/\s+/g, "+"));
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function parseGoogleCss(cssText: string): Map<string, string> {
|
|
238
|
+
const result = new Map<string, string>();
|
|
239
|
+
const blockRegex = /@font-face\s*{[^}]*}/g;
|
|
240
|
+
for (const blockMatch of cssText.matchAll(blockRegex)) {
|
|
241
|
+
const block = blockMatch[0];
|
|
242
|
+
const familyMatch = /font-family:\s*['"]?([^'";]+)['"]?/i.exec(block);
|
|
243
|
+
const weightMatch = /font-weight:\s*([0-9]+)/i.exec(block);
|
|
244
|
+
const styleMatch = /font-style:\s*(italic|normal)/i.exec(block);
|
|
245
|
+
const srcMatch = /src:\s*[^;]*url\(([^)]+)\)/i.exec(block);
|
|
246
|
+
if (!familyMatch || !weightMatch || !styleMatch || !srcMatch) continue;
|
|
247
|
+
|
|
248
|
+
const family = familyMatch[1].trim();
|
|
249
|
+
const weight = Number.parseInt(weightMatch[1], 10);
|
|
250
|
+
const style = styleMatch[1].toLowerCase() === "italic" ? "italic" : "normal";
|
|
251
|
+
const url = srcMatch[1].replace(/['"]/g, "").trim();
|
|
252
|
+
const key = faceKey(family, weight, style);
|
|
253
|
+
if (!result.has(key)) {
|
|
254
|
+
result.set(key, url);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
return result;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function faceKey(family: string, weight: number, style: string): string {
|
|
261
|
+
return `${family.toLowerCase()}@${weight}@${style.toLowerCase()}`;
|
|
262
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import * as path from "node:path";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
import { log, type LogLevel } from "../../logging/debug.js";
|
|
4
|
+
import type { FontConfig, FontFaceDef } from "../../types/fonts.js";
|
|
5
|
+
import type { Environment } from "../../environment/environment.js";
|
|
6
|
+
import { NodeEnvironment } from "../../environment/node-environment.js";
|
|
7
|
+
|
|
8
|
+
type BuiltinFace = Omit<FontFaceDef, "src" | "data"> & { file: string };
|
|
9
|
+
|
|
10
|
+
const BUILTIN_FACES: BuiltinFace[] = [
|
|
11
|
+
// Sans: primary and UI-friendly
|
|
12
|
+
{ name: "Lato-Regular", family: "Lato", weight: 400, style: "normal", file: "woff2/lato/lato-latin-400-normal.woff2" },
|
|
13
|
+
{ name: "Lato-Bold", family: "Lato", weight: 700, style: "normal", file: "woff2/lato/lato-latin-700-normal.woff2" },
|
|
14
|
+
{ name: "Lato-Italic", family: "Lato", weight: 400, style: "italic", file: "woff2/lato/lato-latin-400-italic.woff2" },
|
|
15
|
+
{ name: "Lato-BoldItalic", family: "Lato", weight: 700, style: "italic", file: "woff2/lato/lato-latin-700-italic.woff2" },
|
|
16
|
+
{ name: "Roboto-Regular", family: "Roboto", weight: 400, style: "normal", file: "ttf/roboto/Roboto-Regular.ttf" },
|
|
17
|
+
{ name: "Roboto-Bold", family: "Roboto", weight: 700, style: "normal", file: "ttf/roboto/Roboto-Bold.ttf" },
|
|
18
|
+
{ name: "Roboto-Italic", family: "Roboto", weight: 400, style: "italic", file: "ttf/roboto/Roboto-Italic.ttf" },
|
|
19
|
+
{ name: "Roboto-BoldItalic", family: "Roboto", weight: 700, style: "italic", file: "ttf/roboto/Roboto-BoldItalic.ttf" },
|
|
20
|
+
{ name: "Arimo-Regular", family: "Arimo", weight: 400, style: "normal", file: "ttf/arimo/Arimo-Regular.ttf" },
|
|
21
|
+
{ name: "Arimo-Bold", family: "Arimo", weight: 700, style: "normal", file: "ttf/arimo/Arimo-Bold.ttf" },
|
|
22
|
+
{ name: "Arimo-Italic", family: "Arimo", weight: 400, style: "italic", file: "ttf/arimo/Arimo-Italic.ttf" },
|
|
23
|
+
{ name: "Arimo-BoldItalic", family: "Arimo", weight: 700, style: "italic", file: "ttf/arimo/Arimo-BoldItalic.ttf" },
|
|
24
|
+
{ name: "NotoSans-Regular", family: "Noto Sans", weight: 400, style: "normal", file: "ttf/notosans/NotoSans-Regular.ttf" },
|
|
25
|
+
{ name: "DejaVuSans-Regular", family: "DejaVu Sans", weight: 400, style: "normal", file: "ttf/dejavu/DejaVuSans.ttf" },
|
|
26
|
+
|
|
27
|
+
// Serif / display
|
|
28
|
+
{ name: "Tinos-Regular", family: "Tinos", weight: 400, style: "normal", file: "ttf/tinos/Tinos-Regular.ttf" },
|
|
29
|
+
{ name: "Tinos-Bold", family: "Tinos", weight: 700, style: "normal", file: "ttf/tinos/Tinos-Bold.ttf" },
|
|
30
|
+
{ name: "Tinos-Italic", family: "Tinos", weight: 400, style: "italic", file: "ttf/tinos/Tinos-Italic.ttf" },
|
|
31
|
+
{ name: "Tinos-BoldItalic", family: "Tinos", weight: 700, style: "italic", file: "ttf/tinos/Tinos-BoldItalic.ttf" },
|
|
32
|
+
{ name: "CinzelDecorative-Regular", family: "Cinzel Decorative", weight: 400, style: "normal", file: "ttf/cinzeldecorative/CinzelDecorative-Regular.ttf" },
|
|
33
|
+
{ name: "CinzelDecorative-Bold", family: "Cinzel Decorative", weight: 700, style: "normal", file: "ttf/cinzeldecorative/CinzelDecorative-Bold.ttf" },
|
|
34
|
+
{ name: "CinzelDecorative-Black", family: "Cinzel Decorative", weight: 900, style: "normal", file: "ttf/cinzeldecorative/CinzelDecorative-Black.ttf" },
|
|
35
|
+
{ name: "Caveat-Regular", family: "Caveat", weight: 400, style: "normal", file: "woff2/caveat/Caveat-Regular.woff2" },
|
|
36
|
+
{ name: "Caveat-Bold", family: "Caveat", weight: 700, style: "normal", file: "woff2/caveat/Caveat-Bold.woff2" },
|
|
37
|
+
|
|
38
|
+
// Monospace
|
|
39
|
+
{ name: "FiraCode-Light", family: "Fira Code", weight: 300, style: "normal", file: "ttf/firecode/FiraCode-Light.ttf" },
|
|
40
|
+
{ name: "FiraCode-Regular", family: "Fira Code", weight: 400, style: "normal", file: "ttf/firecode/FiraCode-Regular.ttf" },
|
|
41
|
+
{ name: "FiraCode-Medium", family: "Fira Code", weight: 500, style: "normal", file: "ttf/firecode/FiraCode-Medium.ttf" },
|
|
42
|
+
{ name: "FiraCode-SemiBold", family: "Fira Code", weight: 600, style: "normal", file: "ttf/firecode/FiraCode-SemiBold.ttf" },
|
|
43
|
+
{ name: "FiraCode-Bold", family: "Fira Code", weight: 700, style: "normal", file: "ttf/firecode/FiraCode-Bold.ttf" },
|
|
44
|
+
|
|
45
|
+
// Emoji
|
|
46
|
+
{ name: "NotoEmoji-Light", family: "Noto Emoji", weight: 300, style: "normal", file: "ttf/notoemoji/NotoEmoji-Light.ttf" },
|
|
47
|
+
{ name: "NotoEmoji-Regular", family: "Noto Emoji", weight: 400, style: "normal", file: "ttf/notoemoji/NotoEmoji-Regular.ttf" },
|
|
48
|
+
{ name: "NotoEmoji-Medium", family: "Noto Emoji", weight: 500, style: "normal", file: "ttf/notoemoji/NotoEmoji-Medium.ttf" },
|
|
49
|
+
{ name: "NotoEmoji-SemiBold", family: "Noto Emoji", weight: 600, style: "normal", file: "ttf/notoemoji/NotoEmoji-SemiBold.ttf" },
|
|
50
|
+
{ name: "NotoEmoji-Bold", family: "Noto Emoji", weight: 700, style: "normal", file: "ttf/notoemoji/NotoEmoji-Bold.ttf" },
|
|
51
|
+
|
|
52
|
+
// Math
|
|
53
|
+
{ name: "STIXTwoMath-Regular", family: "STIX Two Math", weight: 400, style: "normal", file: "ttf/stixtwomath/STIXTwoMath-Regular.ttf" },
|
|
54
|
+
];
|
|
55
|
+
|
|
56
|
+
let cachedConfig: FontConfig | null | undefined;
|
|
57
|
+
let loading: Promise<FontConfig | null> | null = null;
|
|
58
|
+
|
|
59
|
+
export async function loadBuiltinFontConfig(environment: Environment = new NodeEnvironment()): Promise<FontConfig | null> {
|
|
60
|
+
if (loading) {
|
|
61
|
+
return loading;
|
|
62
|
+
}
|
|
63
|
+
if (!isNodeRuntime()) {
|
|
64
|
+
cachedConfig = null;
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
loading = (async () => {
|
|
69
|
+
try {
|
|
70
|
+
const baseDir = resolveFontsDir();
|
|
71
|
+
log('font', 'debug', "Builtin font baseDir:", baseDir);
|
|
72
|
+
const faces: FontFaceDef[] = [];
|
|
73
|
+
for (const face of BUILTIN_FACES) {
|
|
74
|
+
const filePath = path.join(baseDir, face.file);
|
|
75
|
+
log('font', 'debug', "Loading font file:", filePath);
|
|
76
|
+
try {
|
|
77
|
+
const buffer = await environment.loader.load(filePath);
|
|
78
|
+
faces.push({
|
|
79
|
+
name: face.name,
|
|
80
|
+
family: face.family,
|
|
81
|
+
weight: face.weight,
|
|
82
|
+
style: face.style,
|
|
83
|
+
src: filePath,
|
|
84
|
+
data: buffer,
|
|
85
|
+
});
|
|
86
|
+
} catch (err) {
|
|
87
|
+
log('font', 'warn', `Failed to load font file: ${filePath}`, err);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
cachedConfig = {
|
|
91
|
+
fontFaceDefs: faces,
|
|
92
|
+
defaultStack: [
|
|
93
|
+
"Lato",
|
|
94
|
+
"Roboto",
|
|
95
|
+
"Arimo",
|
|
96
|
+
"Noto Sans",
|
|
97
|
+
"DejaVu Sans",
|
|
98
|
+
"Tinos",
|
|
99
|
+
"Fira Code",
|
|
100
|
+
"Caveat",
|
|
101
|
+
"Cinzel Decorative",
|
|
102
|
+
"Noto Emoji",
|
|
103
|
+
"STIX Two Math",
|
|
104
|
+
],
|
|
105
|
+
};
|
|
106
|
+
return cachedConfig;
|
|
107
|
+
} catch (error) {
|
|
108
|
+
log("font", 'warn', "Unable to load builtin font config", { error });
|
|
109
|
+
cachedConfig = null;
|
|
110
|
+
return null;
|
|
111
|
+
} finally {
|
|
112
|
+
loading = null;
|
|
113
|
+
}
|
|
114
|
+
})();
|
|
115
|
+
|
|
116
|
+
return loading;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function resolveFontsDir(): string {
|
|
120
|
+
const here = fileURLToPath(import.meta.url);
|
|
121
|
+
return path.resolve(path.dirname(here), "../../../assets/fonts");
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function isNodeRuntime(): boolean {
|
|
125
|
+
return typeof process !== "undefined" && !!process.versions?.node;
|
|
126
|
+
}
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import type { GlyphOutlineCmd } from "../../types/fonts.js";
|
|
2
|
+
import type { DataViewReader } from "./ttf-table-provider.js";
|
|
3
|
+
import { TransformationMatrix } from "./transformation-matrix.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Provider interface for retrieving glyph outlines.
|
|
7
|
+
* Used to resolve component glyph references in composite glyphs.
|
|
8
|
+
*/
|
|
9
|
+
export interface GlyphOutlineProvider {
|
|
10
|
+
/**
|
|
11
|
+
* Gets the outline for a glyph.
|
|
12
|
+
* @param glyphId - The glyph ID
|
|
13
|
+
* @param depth - Current recursion depth (for preventing infinite recursion)
|
|
14
|
+
* @returns Glyph outline commands, or null if not available
|
|
15
|
+
*/
|
|
16
|
+
getOutline(glyphId: number, depth?: number): GlyphOutlineCmd[] | null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Component transformation and reference data.
|
|
21
|
+
*/
|
|
22
|
+
interface ComponentData {
|
|
23
|
+
glyphIndex: number;
|
|
24
|
+
flags: number;
|
|
25
|
+
transform: TransformationMatrix;
|
|
26
|
+
hasMoreComponents: boolean;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Parses composite (component-based) TrueType glyphs.
|
|
31
|
+
*
|
|
32
|
+
* Composite glyphs are built from one or more simple glyphs (components),
|
|
33
|
+
* each with its own transformation (translation, scale, rotation).
|
|
34
|
+
*/
|
|
35
|
+
export class CompositeGlyphParser {
|
|
36
|
+
private readonly recursionLimit = 8;
|
|
37
|
+
private readonly visited: Set<number> = new Set();
|
|
38
|
+
|
|
39
|
+
// Component flags
|
|
40
|
+
private static readonly ARG_1_AND_2_ARE_WORDS = 0x0001;
|
|
41
|
+
private static readonly WE_HAVE_A_SCALE = 0x0008;
|
|
42
|
+
private static readonly MORE_COMPONENTS = 0x0020;
|
|
43
|
+
private static readonly WE_HAVE_AN_X_AND_Y_SCALE = 0x0040;
|
|
44
|
+
private static readonly WE_HAVE_A_TWO_BY_TWO = 0x0080;
|
|
45
|
+
|
|
46
|
+
constructor(private readonly reader: DataViewReader) { }
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Parses a composite glyph.
|
|
50
|
+
* @param view - DataView containing the composite glyph data
|
|
51
|
+
* @param provider - Provider for resolving component glyph references
|
|
52
|
+
* @param depth - Current recursion depth
|
|
53
|
+
* @returns Array of glyph outline commands, or null if parsing fails
|
|
54
|
+
*/
|
|
55
|
+
parse(
|
|
56
|
+
view: DataView,
|
|
57
|
+
provider: GlyphOutlineProvider,
|
|
58
|
+
depth: number = 0
|
|
59
|
+
): GlyphOutlineCmd[] | null {
|
|
60
|
+
if (depth > this.recursionLimit) return null;
|
|
61
|
+
|
|
62
|
+
// Composite glyph component records start at offset 10 (after header)
|
|
63
|
+
const cmds = this.parseComponentRecords(view, 10, provider, depth);
|
|
64
|
+
|
|
65
|
+
// Clear visited set for next parse
|
|
66
|
+
this.visited.clear();
|
|
67
|
+
|
|
68
|
+
return cmds;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Parses component records from a composite glyph.
|
|
73
|
+
* @param view - DataView containing the glyph data
|
|
74
|
+
* @param startOffset - Starting offset for component records
|
|
75
|
+
* @param provider - Provider for resolving component references
|
|
76
|
+
* @param depth - Current recursion depth
|
|
77
|
+
* @returns Combined outline commands from all components, or null if parsing fails
|
|
78
|
+
*/
|
|
79
|
+
private parseComponentRecords(
|
|
80
|
+
view: DataView,
|
|
81
|
+
startOffset: number,
|
|
82
|
+
provider: GlyphOutlineProvider,
|
|
83
|
+
depth: number
|
|
84
|
+
): GlyphOutlineCmd[] | null {
|
|
85
|
+
if (depth > this.recursionLimit) return null;
|
|
86
|
+
|
|
87
|
+
let offset = startOffset;
|
|
88
|
+
const allCmds: GlyphOutlineCmd[] = [];
|
|
89
|
+
|
|
90
|
+
while (true) {
|
|
91
|
+
const component = this.readComponentData(view, offset);
|
|
92
|
+
if (!component) return null;
|
|
93
|
+
|
|
94
|
+
offset = this.calculateNextOffset(view, offset, component.flags);
|
|
95
|
+
|
|
96
|
+
// Prevent infinite recursion cycles
|
|
97
|
+
if (this.visited.has(component.glyphIndex)) {
|
|
98
|
+
// Skip this component to avoid cycle
|
|
99
|
+
} else {
|
|
100
|
+
this.visited.add(component.glyphIndex);
|
|
101
|
+
|
|
102
|
+
// Get component's outline
|
|
103
|
+
const componentCmds = provider.getOutline(component.glyphIndex, depth + 1);
|
|
104
|
+
if (componentCmds && componentCmds.length > 0) {
|
|
105
|
+
// Transform the component's outline
|
|
106
|
+
const transformedCmds = component.transform.applyToCommands(componentCmds);
|
|
107
|
+
|
|
108
|
+
// Ensure proper moveTo at start if needed
|
|
109
|
+
if (transformedCmds.length > 0 && transformedCmds[0].type !== "moveTo") {
|
|
110
|
+
const firstCoord = this.findFirstCoordinate(transformedCmds);
|
|
111
|
+
if (firstCoord) {
|
|
112
|
+
allCmds.push({ type: "moveTo", x: firstCoord.x, y: firstCoord.y });
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
allCmds.push(...transformedCmds);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Check if there are more components
|
|
121
|
+
if (!component.hasMoreComponents) break;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return allCmds.length > 0 ? allCmds : null;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Reads a single component's data from the view.
|
|
129
|
+
* @param view - DataView containing the glyph data
|
|
130
|
+
* @param offset - Starting offset for this component
|
|
131
|
+
* @returns Component data or null if reading fails
|
|
132
|
+
*/
|
|
133
|
+
private readComponentData(view: DataView, offset: number): ComponentData | null {
|
|
134
|
+
if (offset + 4 > view.byteLength) return null;
|
|
135
|
+
|
|
136
|
+
const flags = this.reader.getUint16(view, offset);
|
|
137
|
+
const glyphIndex = this.reader.getUint16(view, offset + 2);
|
|
138
|
+
let p = offset + 4;
|
|
139
|
+
|
|
140
|
+
// Read arguments (translation or matching points)
|
|
141
|
+
let arg1 = 0;
|
|
142
|
+
let arg2 = 0;
|
|
143
|
+
|
|
144
|
+
if (flags & CompositeGlyphParser.ARG_1_AND_2_ARE_WORDS) {
|
|
145
|
+
// 16-bit signed arguments
|
|
146
|
+
if (p + 4 > view.byteLength) return null;
|
|
147
|
+
arg1 = this.reader.getInt16(view, p);
|
|
148
|
+
arg2 = this.reader.getInt16(view, p + 2);
|
|
149
|
+
p += 4;
|
|
150
|
+
} else {
|
|
151
|
+
// 8-bit signed arguments
|
|
152
|
+
if (p + 2 > view.byteLength) return null;
|
|
153
|
+
arg1 = this.reader.getInt8(view, p);
|
|
154
|
+
arg2 = this.reader.getInt8(view, p + 1);
|
|
155
|
+
p += 2;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Read transformation matrix
|
|
159
|
+
let mxx = 1, mxy = 0, myx = 0, myy = 1;
|
|
160
|
+
|
|
161
|
+
if (flags & CompositeGlyphParser.WE_HAVE_A_SCALE) {
|
|
162
|
+
// Uniform scale
|
|
163
|
+
if (p + 2 > view.byteLength) return null;
|
|
164
|
+
const scale = this.reader.getInt16(view, p) / (1 << 14);
|
|
165
|
+
mxx = scale;
|
|
166
|
+
myy = scale;
|
|
167
|
+
} else if (flags & CompositeGlyphParser.WE_HAVE_AN_X_AND_Y_SCALE) {
|
|
168
|
+
// Non-uniform scale
|
|
169
|
+
if (p + 4 > view.byteLength) return null;
|
|
170
|
+
const sx = this.reader.getInt16(view, p) / (1 << 14);
|
|
171
|
+
const sy = this.reader.getInt16(view, p + 2) / (1 << 14);
|
|
172
|
+
mxx = sx;
|
|
173
|
+
myy = sy;
|
|
174
|
+
} else if (flags & CompositeGlyphParser.WE_HAVE_A_TWO_BY_TWO) {
|
|
175
|
+
// Full 2x2 matrix
|
|
176
|
+
if (p + 8 > view.byteLength) return null;
|
|
177
|
+
mxx = this.reader.getInt16(view, p) / (1 << 14);
|
|
178
|
+
mxy = this.reader.getInt16(view, p + 2) / (1 << 14);
|
|
179
|
+
myx = this.reader.getInt16(view, p + 4) / (1 << 14);
|
|
180
|
+
myy = this.reader.getInt16(view, p + 6) / (1 << 14);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Treat args as translation (simplified - spec allows matching points too)
|
|
184
|
+
const transform = TransformationMatrix.fromComponents(arg1, arg2, mxx, mxy, myx, myy);
|
|
185
|
+
|
|
186
|
+
return {
|
|
187
|
+
glyphIndex,
|
|
188
|
+
flags,
|
|
189
|
+
transform,
|
|
190
|
+
hasMoreComponents: !!(flags & CompositeGlyphParser.MORE_COMPONENTS)
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Calculates the next offset after reading a component record.
|
|
196
|
+
* @param view - DataView containing the glyph data
|
|
197
|
+
* @param offset - Current offset
|
|
198
|
+
* @param flags - Component flags
|
|
199
|
+
* @returns Next offset
|
|
200
|
+
*/
|
|
201
|
+
private calculateNextOffset(view: DataView, offset: number, flags: number): number {
|
|
202
|
+
let p = offset + 4; // flags + glyphIndex
|
|
203
|
+
|
|
204
|
+
// Skip arguments
|
|
205
|
+
if (flags & CompositeGlyphParser.ARG_1_AND_2_ARE_WORDS) {
|
|
206
|
+
p += 4;
|
|
207
|
+
} else {
|
|
208
|
+
p += 2;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Skip transformation data
|
|
212
|
+
if (flags & CompositeGlyphParser.WE_HAVE_A_SCALE) {
|
|
213
|
+
p += 2;
|
|
214
|
+
} else if (flags & CompositeGlyphParser.WE_HAVE_AN_X_AND_Y_SCALE) {
|
|
215
|
+
p += 4;
|
|
216
|
+
} else if (flags & CompositeGlyphParser.WE_HAVE_A_TWO_BY_TWO) {
|
|
217
|
+
p += 8;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return p;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Finds the first coordinate in a list of commands.
|
|
225
|
+
* @param cmds - Array of glyph outline commands
|
|
226
|
+
* @returns First coordinate found, or null
|
|
227
|
+
*/
|
|
228
|
+
private findFirstCoordinate(cmds: GlyphOutlineCmd[]): { x: number; y: number } | null {
|
|
229
|
+
for (const c of cmds) {
|
|
230
|
+
if (c.type === "moveTo" || c.type === "lineTo") {
|
|
231
|
+
return { x: c.x, y: c.y };
|
|
232
|
+
}
|
|
233
|
+
if (c.type === "quadTo") {
|
|
234
|
+
return { x: c.x, y: c.y };
|
|
235
|
+
}
|
|
236
|
+
if (c.type === "cubicTo") {
|
|
237
|
+
return { x: c.x, y: c.y };
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
return null;
|
|
241
|
+
}
|
|
242
|
+
}
|