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,395 @@
|
|
|
1
|
+
import type { PdfObjectRef, PdfDocument } from "../primitives/pdf-document.js";
|
|
2
|
+
import { parseTtfBuffer } from "./ttf-lite.js";
|
|
3
|
+
import type { FontFaceDef, FontConfig, TtfFontMetrics } from "../../types/fonts.js";
|
|
4
|
+
import { log } from "../../logging/debug.js";
|
|
5
|
+
import { normalizeFontWeight } from "../../css/font-weight.js";
|
|
6
|
+
import { detectFontFormat } from "../../fonts/detector.js";
|
|
7
|
+
import { reconstructTtf } from "../../fonts/utils/ttf-reconstructor.js";
|
|
8
|
+
import { computeWidths } from "./widths.js";
|
|
9
|
+
import { createToUnicodeCMapText } from "./to-unicode.js";
|
|
10
|
+
import { BASE_FONT_ALIASES, GENERIC_FAMILIES } from "./font-config.js";
|
|
11
|
+
|
|
12
|
+
export { computeWidths } from "./widths.js";
|
|
13
|
+
export { createToUnicodeCMapText } from "./to-unicode.js";
|
|
14
|
+
|
|
15
|
+
const TYPICAL_STEM_V = 80;
|
|
16
|
+
|
|
17
|
+
export interface EmbeddedFont {
|
|
18
|
+
readonly resourceName: string;
|
|
19
|
+
readonly ref: PdfObjectRef;
|
|
20
|
+
readonly baseFont: string;
|
|
21
|
+
readonly metrics: TtfFontMetrics;
|
|
22
|
+
readonly subset: Uint8Array;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface FontDescriptor {
|
|
26
|
+
readonly Type: "/FontDescriptor";
|
|
27
|
+
readonly FontName: string;
|
|
28
|
+
readonly Flags: number;
|
|
29
|
+
readonly FontBBox: readonly [number, number, number, number];
|
|
30
|
+
readonly ItalicAngle: number;
|
|
31
|
+
readonly Ascent: number;
|
|
32
|
+
readonly Descent: number;
|
|
33
|
+
readonly CapHeight: number;
|
|
34
|
+
readonly XHeight: number;
|
|
35
|
+
readonly StemV: number;
|
|
36
|
+
readonly FontFile2: PdfObjectRef;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
interface FontDictionary {
|
|
40
|
+
readonly Type: "/Font";
|
|
41
|
+
readonly Subtype: "/Type0";
|
|
42
|
+
readonly BaseFont: string;
|
|
43
|
+
readonly Encoding: "/Identity-H";
|
|
44
|
+
readonly DescendantFonts: readonly [PdfObjectRef];
|
|
45
|
+
readonly ToUnicode: PdfObjectRef;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
interface CIDFontDictionary {
|
|
49
|
+
readonly Type: "/Font";
|
|
50
|
+
readonly Subtype: "/CIDFontType2";
|
|
51
|
+
readonly BaseFont: string;
|
|
52
|
+
readonly CIDSystemInfo: {
|
|
53
|
+
readonly Registry: string; // emitted as literal strings "(Adobe)"
|
|
54
|
+
readonly Ordering: string; // emitted as literal strings "(Identity)"
|
|
55
|
+
readonly Supplement: 0;
|
|
56
|
+
};
|
|
57
|
+
readonly FontDescriptor: PdfObjectRef;
|
|
58
|
+
readonly DW?: number;
|
|
59
|
+
readonly W: readonly (number | readonly number[])[];
|
|
60
|
+
readonly CIDToGIDMap: PdfObjectRef | string;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export class FontEmbedder {
|
|
64
|
+
private embeddedFonts = new Map<string, EmbeddedFont>();
|
|
65
|
+
private faceMetrics = new Map<string, TtfFontMetrics>();
|
|
66
|
+
|
|
67
|
+
constructor(private readonly config: FontConfig, private readonly doc: PdfDocument) { }
|
|
68
|
+
|
|
69
|
+
async initialize(): Promise<void> {
|
|
70
|
+
for (const face of this.config.fontFaceDefs) {
|
|
71
|
+
if (!face.data) {
|
|
72
|
+
log("font", "error", `Missing data for font ${face.name}`);
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
try {
|
|
76
|
+
let fontData = new Uint8Array(face.data);
|
|
77
|
+
const format = detectFontFormat(fontData);
|
|
78
|
+
let ttfBuffer: ArrayBuffer;
|
|
79
|
+
if (format === "woff") {
|
|
80
|
+
const { decodeWoff } = await import("../../fonts/woff/decoder.js");
|
|
81
|
+
const decoded = await decodeWoff(fontData);
|
|
82
|
+
ttfBuffer = reconstructTtf(decoded);
|
|
83
|
+
fontData = new Uint8Array(ttfBuffer);
|
|
84
|
+
(face as any).data = ttfBuffer;
|
|
85
|
+
} else if (format === "woff2") {
|
|
86
|
+
const { decodeWoff2 } = await import("../../fonts/woff2/decoder.js");
|
|
87
|
+
const decoded = await decodeWoff2(fontData);
|
|
88
|
+
// Copy into a fresh ArrayBuffer to avoid SharedArrayBuffer unions
|
|
89
|
+
const ttfCopy = decoded.ttfBuffer.slice();
|
|
90
|
+
ttfBuffer = ttfCopy.buffer;
|
|
91
|
+
fontData = new Uint8Array(ttfBuffer);
|
|
92
|
+
(face as any).data = ttfBuffer;
|
|
93
|
+
} else {
|
|
94
|
+
ttfBuffer = face.data!;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const metrics = parseTtfBuffer(ttfBuffer);
|
|
98
|
+
this.faceMetrics.set(face.name, metrics);
|
|
99
|
+
} catch (error) {
|
|
100
|
+
log("font", "error", `Failed to load font ${face.name}`, { error: error instanceof Error ? error.message : String(error) });
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
ensureFont(familyStack: string[], fontWeight?: number, fontStyle?: string): EmbeddedFont | null {
|
|
106
|
+
const targetWeight = normalizeFontWeight(fontWeight);
|
|
107
|
+
const wantsItalic = isItalic(fontStyle);
|
|
108
|
+
|
|
109
|
+
for (const family of familyStack) {
|
|
110
|
+
const normalizedFamily = family.toLowerCase().trim();
|
|
111
|
+
const candidates = this.config.fontFaceDefs.filter((f) => {
|
|
112
|
+
return (f.family || "").toLowerCase().trim() === normalizedFamily;
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
if (candidates.length === 0) {
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const face = pickFaceByWeight(candidates, targetWeight, wantsItalic);
|
|
120
|
+
if (!face) {
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Use normalized face name as key to avoid accidental mismatches due to spacing/case
|
|
125
|
+
const faceKey = (face.name || "").toLowerCase().trim();
|
|
126
|
+
const existing = this.embeddedFonts.get(face.name) ?? this.embeddedFonts.get(faceKey);
|
|
127
|
+
if (existing) return existing;
|
|
128
|
+
|
|
129
|
+
const embedded = this.embedFont(face);
|
|
130
|
+
if (embedded) {
|
|
131
|
+
// store with both canonical and normalized keys for resilient lookups
|
|
132
|
+
this.embeddedFonts.set(face.name, embedded);
|
|
133
|
+
this.embeddedFonts.set(faceKey, embedded);
|
|
134
|
+
return embedded;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
private embedFont(face: FontFaceDef): EmbeddedFont | null {
|
|
142
|
+
const metrics = this.faceMetrics.get(face.name);
|
|
143
|
+
if (!metrics) return null;
|
|
144
|
+
const unitsPerEm = metrics.metrics.unitsPerEm;
|
|
145
|
+
const scaleTo1000 = (v: number) => Math.round((v / unitsPerEm) * 1000);
|
|
146
|
+
|
|
147
|
+
let fontBBox: [number, number, number, number];
|
|
148
|
+
if (metrics.headBBox) {
|
|
149
|
+
const hb = metrics.headBBox;
|
|
150
|
+
fontBBox = [scaleTo1000(hb[0]), scaleTo1000(hb[1]), scaleTo1000(hb[2]), scaleTo1000(hb[3])];
|
|
151
|
+
} else {
|
|
152
|
+
// Fallback if headBBox is missing
|
|
153
|
+
fontBBox = [-1000, -1000, 1000, 1000];
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Register the font file stream
|
|
157
|
+
// We need the full font data here. In the initialize method we updated face.data
|
|
158
|
+
// but here we need to access it.
|
|
159
|
+
// Since we can't easily change the interface, we'll assume face.data is the source.
|
|
160
|
+
// If it was converted to TTF in initialize, face.data (casted) holds the TTF buffer.
|
|
161
|
+
const fullFontData = new Uint8Array(face.data!);
|
|
162
|
+
// FontFile2 streams require only /Length (PdfDocument adds it); avoid Type1-specific Length1 headers.
|
|
163
|
+
const fontFileRef = this.doc.registerStream(fullFontData, {});
|
|
164
|
+
|
|
165
|
+
const fontDescriptor: FontDescriptor = {
|
|
166
|
+
Type: "/FontDescriptor",
|
|
167
|
+
FontName: `/${face.name}`,
|
|
168
|
+
Flags: computePdfFlagsFromFace(face),
|
|
169
|
+
FontBBox: fontBBox,
|
|
170
|
+
ItalicAngle: face.style === "italic" ? -12 : 0,
|
|
171
|
+
Ascent: scaleTo1000(metrics.metrics.ascender),
|
|
172
|
+
Descent: scaleTo1000(metrics.metrics.descender),
|
|
173
|
+
CapHeight: scaleTo1000(metrics.metrics.capHeight),
|
|
174
|
+
XHeight: scaleTo1000(metrics.metrics.xHeight),
|
|
175
|
+
StemV: TYPICAL_STEM_V,
|
|
176
|
+
FontFile2: fontFileRef
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
const fontDescriptorRef = this.doc.register(fontDescriptor);
|
|
180
|
+
|
|
181
|
+
// Compute DW and compressed W
|
|
182
|
+
const { DW, W } = computeWidths(metrics);
|
|
183
|
+
|
|
184
|
+
// Create CID font dictionary (include DW)
|
|
185
|
+
// CID fonts must declare string-valued CIDSystemInfo entries per PDF spec.
|
|
186
|
+
const cidFontDict: CIDFontDictionary = {
|
|
187
|
+
Type: "/Font",
|
|
188
|
+
Subtype: "/CIDFontType2",
|
|
189
|
+
BaseFont: `/${face.name}`,
|
|
190
|
+
CIDSystemInfo: {
|
|
191
|
+
Registry: "(Adobe)",
|
|
192
|
+
Ordering: "(Identity)",
|
|
193
|
+
Supplement: 0
|
|
194
|
+
},
|
|
195
|
+
FontDescriptor: fontDescriptorRef,
|
|
196
|
+
DW,
|
|
197
|
+
W,
|
|
198
|
+
// Rely on built-in Identity CIDToGID mapping to keep font dictionaries simple/compatible.
|
|
199
|
+
CIDToGIDMap: "/Identity"
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
const cidFontRef = this.doc.register(cidFontDict);
|
|
203
|
+
|
|
204
|
+
// Create Unicode mapping (ToUnicode CMap)
|
|
205
|
+
const toUnicodeRef = this.createToUnicodeCMap(metrics);
|
|
206
|
+
|
|
207
|
+
// Create Type0 font dictionary
|
|
208
|
+
// Per PDF spec, Type0 BaseFont should include the CMap suffix (e.g. "-Identity-H")
|
|
209
|
+
// to make the composite font name unambiguous for parsers/renderers.
|
|
210
|
+
const type0Font: FontDictionary = {
|
|
211
|
+
Type: "/Font",
|
|
212
|
+
Subtype: "/Type0",
|
|
213
|
+
BaseFont: `/${face.name}-Identity-H`,
|
|
214
|
+
Encoding: "/Identity-H",
|
|
215
|
+
DescendantFonts: [cidFontRef],
|
|
216
|
+
ToUnicode: toUnicodeRef
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
const fontRef = this.doc.register(type0Font);
|
|
220
|
+
|
|
221
|
+
return {
|
|
222
|
+
resourceName: `F${this.embeddedFonts.size + 1}`,
|
|
223
|
+
ref: fontRef,
|
|
224
|
+
baseFont: face.name,
|
|
225
|
+
metrics,
|
|
226
|
+
subset: fullFontData
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
private createToUnicodeCMap(metrics: TtfFontMetrics, _uniqueUnicodes: number[] = []): PdfObjectRef {
|
|
233
|
+
// Build inverse mapping gid -> unicode (pick first unicode when multiple map to same gid)
|
|
234
|
+
const unicodeMap = metrics.cmap["unicodeMap"] as Map<number, number>;
|
|
235
|
+
log("font", "debug", "createToUnicodeCMap - unicodeMap size", { size: unicodeMap.size });
|
|
236
|
+
|
|
237
|
+
// Sample first few entries for debugging
|
|
238
|
+
const samples: Array<{ unicode: number, char: string, gid: number }> = [];
|
|
239
|
+
let count = 0;
|
|
240
|
+
for (const [unicode, gid] of unicodeMap.entries()) {
|
|
241
|
+
if (count < 10) {
|
|
242
|
+
samples.push({ unicode, char: String.fromCodePoint(unicode), gid });
|
|
243
|
+
}
|
|
244
|
+
count++;
|
|
245
|
+
}
|
|
246
|
+
log("font", "debug", "createToUnicodeCMap - sample entries", { samples });
|
|
247
|
+
|
|
248
|
+
const gidToUni = new Map<number, number>();
|
|
249
|
+
for (const [unicode, gid] of unicodeMap.entries()) {
|
|
250
|
+
if (!gidToUni.has(gid)) gidToUni.set(gid, unicode);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Debug: log sample gid->unicode mappings
|
|
254
|
+
const gidSamples: Array<{ gid: number, unicode: number, char: string }> = [];
|
|
255
|
+
let gidCount = 0;
|
|
256
|
+
for (const [gid, unicode] of gidToUni.entries()) {
|
|
257
|
+
if (gidCount < 20) {
|
|
258
|
+
gidSamples.push({ gid, unicode, char: String.fromCodePoint(unicode) });
|
|
259
|
+
}
|
|
260
|
+
gidCount++;
|
|
261
|
+
}
|
|
262
|
+
log("font", "debug", "createToUnicodeCMap - gid->unicode sample", { samples: gidSamples });
|
|
263
|
+
|
|
264
|
+
const entries = Array.from(gidToUni.entries())
|
|
265
|
+
.map(([gid, unicode]) => ({ gid, unicode }))
|
|
266
|
+
.sort((a, b) => a.gid - b.gid);
|
|
267
|
+
|
|
268
|
+
const cmapText = createToUnicodeCMapText(entries);
|
|
269
|
+
return this.doc.registerStream(new TextEncoder().encode(cmapText), {});
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Return parsed TTF metrics for a loaded face by name or family, or null if not available.
|
|
275
|
+
* Exposed to allow rendering code to access outlines/metrics for embedding masks.
|
|
276
|
+
*
|
|
277
|
+
* The lookup tries:
|
|
278
|
+
* 1. Exact match by face name (e.g., "Tinos-Bold")
|
|
279
|
+
* 2. Match by family name (e.g., "Tinos") with weight/style matching
|
|
280
|
+
* 3. Case-insensitive match by family name
|
|
281
|
+
* 4. If input contains comma-separated font stack (e.g., "Tinos, serif"), tries each font
|
|
282
|
+
* 5. Uses BASE_FONT_ALIASES to resolve CSS font names to embedded fonts (e.g., "Times New Roman" → "Tinos")
|
|
283
|
+
*
|
|
284
|
+
* @param faceName - Font family name or font stack
|
|
285
|
+
* @param fontWeight - Optional font weight (100-900), defaults to 400
|
|
286
|
+
* @param fontStyle - Optional font style ('normal' | 'italic' | 'oblique'), defaults to 'normal'
|
|
287
|
+
*/
|
|
288
|
+
public getMetrics(faceName: string, fontWeight: number = 400, fontStyle: string = "normal"): TtfFontMetrics | null {
|
|
289
|
+
// Try exact match first
|
|
290
|
+
const exact = this.faceMetrics.get(faceName);
|
|
291
|
+
if (exact) return exact;
|
|
292
|
+
|
|
293
|
+
const wantsItalic = fontStyle === "italic" || fontStyle === "oblique";
|
|
294
|
+
|
|
295
|
+
// Handle font-family stacks like "'Times New Roman', Times, serif"
|
|
296
|
+
const fontStack = faceName.split(",").map(f => f.trim().replace(/^["']|["']$/g, ""));
|
|
297
|
+
|
|
298
|
+
for (const fontName of fontStack) {
|
|
299
|
+
const normalizedQuery = fontName.toLowerCase().trim();
|
|
300
|
+
|
|
301
|
+
// Try exact match with this font name
|
|
302
|
+
const exactMatch = this.faceMetrics.get(fontName);
|
|
303
|
+
if (exactMatch) return exactMatch;
|
|
304
|
+
|
|
305
|
+
// Check if this is an alias that maps to an embedded font
|
|
306
|
+
const aliasedFont = BASE_FONT_ALIASES.get(normalizedQuery) || GENERIC_FAMILIES.get(normalizedQuery);
|
|
307
|
+
const targetFamily = aliasedFont ? aliasedFont.toLowerCase() : normalizedQuery;
|
|
308
|
+
|
|
309
|
+
// Collect all faces matching the family
|
|
310
|
+
const matchingFaces: FontFaceDef[] = [];
|
|
311
|
+
for (const face of this.config.fontFaceDefs) {
|
|
312
|
+
const faceFamily = (face.family || "").toLowerCase().trim();
|
|
313
|
+
if (faceFamily === targetFamily) {
|
|
314
|
+
matchingFaces.push(face);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// Pick the best face by weight and style
|
|
319
|
+
if (matchingFaces.length > 0) {
|
|
320
|
+
const bestFace = pickFaceByWeight(matchingFaces, fontWeight, wantsItalic);
|
|
321
|
+
if (bestFace) {
|
|
322
|
+
const metrics = this.faceMetrics.get(bestFace.name);
|
|
323
|
+
if (metrics) return metrics;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
return null;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
function pickFaceByWeight(faces: FontFaceDef[], requestedWeight: number, wantsItalic: boolean): FontFaceDef | null {
|
|
333
|
+
if (faces.length === 0) {
|
|
334
|
+
return null;
|
|
335
|
+
}
|
|
336
|
+
// Prefer faces that match the requested style; if none do, fall back to any style.
|
|
337
|
+
const styleFiltered = faces.filter((face) => isItalic(face.style) === wantsItalic);
|
|
338
|
+
const pool = styleFiltered.length > 0 ? styleFiltered : faces;
|
|
339
|
+
|
|
340
|
+
let bestFace = pool[0];
|
|
341
|
+
let bestDiff = Math.abs(normalizeFontWeight(bestFace.weight) - requestedWeight);
|
|
342
|
+
for (const face of pool) {
|
|
343
|
+
const normalized = normalizeFontWeight(face.weight);
|
|
344
|
+
const diff = Math.abs(normalized - requestedWeight);
|
|
345
|
+
if (diff < bestDiff) {
|
|
346
|
+
bestDiff = diff;
|
|
347
|
+
bestFace = face;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
return bestFace;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
function isItalic(style: string | undefined): boolean {
|
|
354
|
+
if (!style) return false;
|
|
355
|
+
const s = style.toLowerCase();
|
|
356
|
+
return s === "italic" || s === "oblique";
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// Compute PDF Flags based on font family/style heuristics.
|
|
360
|
+
// See AGENTS.md notes for bits: Symbolic vs Nonsymbolic, Serif, Italic, etc.
|
|
361
|
+
function computePdfFlagsFromFace(face: FontFaceDef): number {
|
|
362
|
+
let flags = 0;
|
|
363
|
+
const family = (face.family || "").toLowerCase().trim();
|
|
364
|
+
const name = (face.name || "").toLowerCase().trim();
|
|
365
|
+
const style = (face.style || "").toLowerCase();
|
|
366
|
+
|
|
367
|
+
const isItalic = /italic|oblique/i.test(style);
|
|
368
|
+
const isSerif = /serif/i.test(family) || /serif/i.test(name);
|
|
369
|
+
const isSymbol = /symbol|dingbat|dingbats|zapfdingbats/i.test(family) || /symbol|dingbat|dingbats/i.test(name);
|
|
370
|
+
|
|
371
|
+
if (isSymbol) flags |= 1 << 2; // Symbolic
|
|
372
|
+
else flags |= 1 << 5; // Nonsymbolic
|
|
373
|
+
|
|
374
|
+
if (isSerif) flags |= 1 << 1; // Serif
|
|
375
|
+
if (isItalic) flags |= 1 << 6; // Italic
|
|
376
|
+
|
|
377
|
+
return flags;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
export async function getEmbeddedFont(name: "NotoSans-Regular" | "DejaVuSans", doc: PdfDocument, config: FontConfig): Promise<EmbeddedFont | null> {
|
|
381
|
+
const embedder = new FontEmbedder(config, doc);
|
|
382
|
+
const face = config.fontFaceDefs.find(f => f.name === name);
|
|
383
|
+
if (!face) return null;
|
|
384
|
+
|
|
385
|
+
const existing = embedder['embeddedFonts'].get(name);
|
|
386
|
+
if (existing) return existing;
|
|
387
|
+
|
|
388
|
+
// @ts-ignore: TypeScript complains about private method, but this is intentional for testing
|
|
389
|
+
const embedded = embedder['embedFont'](face);
|
|
390
|
+
if (embedded) {
|
|
391
|
+
embedder['embeddedFonts'].set(name, embedded);
|
|
392
|
+
return embedded;
|
|
393
|
+
}
|
|
394
|
+
return null;
|
|
395
|
+
}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Font configuration and mapping data for PDF font resolution.
|
|
3
|
+
*
|
|
4
|
+
* This module centralizes all font alias mappings, generic family mappings,
|
|
5
|
+
* and Base14 font variant configurations used throughout the font registry system.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Base14 variant types supported by PDF standard fonts.
|
|
10
|
+
*/
|
|
11
|
+
export type Base14Variant = "normal" | "italic" | "bold" | "boldItalic";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Base14 font family names.
|
|
15
|
+
*/
|
|
16
|
+
export type Base14Family = "Helvetica" | "Times-Roman" | "Courier";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Variant configuration for a Base14 font family.
|
|
20
|
+
*/
|
|
21
|
+
export interface Base14Variants {
|
|
22
|
+
normal: string;
|
|
23
|
+
italic: string;
|
|
24
|
+
bold: string;
|
|
25
|
+
boldItalic: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Maps common font family names to embedded font aliases.
|
|
30
|
+
*
|
|
31
|
+
* This enables fallback to built-in fonts when requested fonts aren't available.
|
|
32
|
+
* For example, "Arial" → "Arimo", "Times New Roman" → "Tinos"
|
|
33
|
+
*/
|
|
34
|
+
export const BASE_FONT_ALIASES: ReadonlyMap<string, string> = new Map([
|
|
35
|
+
// Sans/UI families
|
|
36
|
+
["helvetica", "Lato"],
|
|
37
|
+
["arial", "Arimo"],
|
|
38
|
+
["arial black", "Arimo"],
|
|
39
|
+
["segoe ui", "Roboto"],
|
|
40
|
+
["open sans", "Lato"],
|
|
41
|
+
["calibri", "Roboto"],
|
|
42
|
+
["roboto", "Roboto"],
|
|
43
|
+
["arimo", "Arimo"],
|
|
44
|
+
["lato", "Lato"],
|
|
45
|
+
["noto sans", "Noto Sans"],
|
|
46
|
+
["notosans-regular", "Noto Sans"],
|
|
47
|
+
["dejavu sans", "DejaVu Sans"],
|
|
48
|
+
|
|
49
|
+
// Serif
|
|
50
|
+
["times", "Tinos"],
|
|
51
|
+
["times-roman", "Tinos"],
|
|
52
|
+
["times new roman", "Tinos"],
|
|
53
|
+
["georgia", "Tinos"],
|
|
54
|
+
["garamond", "Tinos"],
|
|
55
|
+
["baskerville", "Tinos"],
|
|
56
|
+
|
|
57
|
+
// Mono
|
|
58
|
+
["courier", "Fira Code"],
|
|
59
|
+
["courier new", "Fira Code"],
|
|
60
|
+
["consolas", "Fira Code"],
|
|
61
|
+
["menlo", "Fira Code"],
|
|
62
|
+
["monaco", "Fira Code"],
|
|
63
|
+
["source code pro", "Fira Code"],
|
|
64
|
+
|
|
65
|
+
// Decorative / cursive
|
|
66
|
+
["comic sans", "Caveat"],
|
|
67
|
+
["comic sans ms", "Caveat"],
|
|
68
|
+
["fantasy", "Cinzel Decorative"],
|
|
69
|
+
["cursive", "Caveat"],
|
|
70
|
+
|
|
71
|
+
// Emoji / math / symbols
|
|
72
|
+
["noto emoji", "Noto Emoji"],
|
|
73
|
+
["noto color emoji", "Noto Emoji"],
|
|
74
|
+
["segoe ui emoji", "Noto Emoji"],
|
|
75
|
+
["apple color emoji", "Noto Emoji"],
|
|
76
|
+
["twemoji", "Noto Emoji"],
|
|
77
|
+
["cambria math", "STIX Two Math"],
|
|
78
|
+
["stix two math", "STIX Two Math"],
|
|
79
|
+
["math", "STIX Two Math"],
|
|
80
|
+
["symbol", "Symbol"],
|
|
81
|
+
["zapfdingbats", "ZapfDingbats"],
|
|
82
|
+
]);
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Maps CSS generic font families to specific embedded fonts.
|
|
86
|
+
*
|
|
87
|
+
* Used when no specific font family is requested or as a final fallback.
|
|
88
|
+
*/
|
|
89
|
+
export const GENERIC_FAMILIES: ReadonlyMap<string, string> = new Map([
|
|
90
|
+
["serif", "Tinos"],
|
|
91
|
+
["sans-serif", "Lato"],
|
|
92
|
+
["monospace", "Fira Code"],
|
|
93
|
+
["system-ui", "Roboto"],
|
|
94
|
+
["cursive", "Caveat"],
|
|
95
|
+
["fantasy", "Cinzel Decorative"],
|
|
96
|
+
["emoji", "Noto Emoji"],
|
|
97
|
+
["math", "STIX Two Math"],
|
|
98
|
+
]);
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Maps common font family names to Base14 standard font fallbacks.
|
|
102
|
+
*
|
|
103
|
+
* Used when font embedding is disabled or not available.
|
|
104
|
+
*/
|
|
105
|
+
export const BASE14_FALLBACKS: ReadonlyMap<string, string> = new Map([
|
|
106
|
+
["times", "Times-Roman"],
|
|
107
|
+
["times-roman", "Times-Roman"],
|
|
108
|
+
["times new roman", "Times-Roman"],
|
|
109
|
+
["georgia", "Times-Roman"],
|
|
110
|
+
["garamond", "Times-Roman"],
|
|
111
|
+
["baskerville", "Times-Roman"],
|
|
112
|
+
["serif", "Times-Roman"],
|
|
113
|
+
["helvetica", "Helvetica"],
|
|
114
|
+
["arial", "Helvetica"],
|
|
115
|
+
["segoe ui", "Helvetica"],
|
|
116
|
+
["open sans", "Helvetica"],
|
|
117
|
+
["calibri", "Helvetica"],
|
|
118
|
+
["sans-serif", "Helvetica"],
|
|
119
|
+
["system-ui", "Helvetica"],
|
|
120
|
+
["courier", "Courier"],
|
|
121
|
+
["courier new", "Courier"],
|
|
122
|
+
["monospace", "Courier"],
|
|
123
|
+
]);
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Base14 font family variants mapping.
|
|
127
|
+
*
|
|
128
|
+
* Defines the standard variant names for each Base14 font family.
|
|
129
|
+
* Used to apply weight/style transformations to Base14 fonts.
|
|
130
|
+
*/
|
|
131
|
+
export const BASE14_FAMILY_VARIANTS: Record<Base14Family, Base14Variants> = {
|
|
132
|
+
"Helvetica": {
|
|
133
|
+
normal: "Helvetica",
|
|
134
|
+
italic: "Helvetica-Oblique",
|
|
135
|
+
bold: "Helvetica-Bold",
|
|
136
|
+
boldItalic: "Helvetica-BoldOblique",
|
|
137
|
+
},
|
|
138
|
+
"Times-Roman": {
|
|
139
|
+
normal: "Times-Roman",
|
|
140
|
+
italic: "Times-Italic",
|
|
141
|
+
bold: "Times-Bold",
|
|
142
|
+
boldItalic: "Times-BoldItalic",
|
|
143
|
+
},
|
|
144
|
+
"Courier": {
|
|
145
|
+
normal: "Courier",
|
|
146
|
+
italic: "Courier-Oblique",
|
|
147
|
+
bold: "Courier-Bold",
|
|
148
|
+
boldItalic: "Courier-BoldOblique",
|
|
149
|
+
},
|
|
150
|
+
} as const;
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Reverse lookup map: Base14 variant name → family + variant type.
|
|
154
|
+
*
|
|
155
|
+
* Built automatically from BASE14_FAMILY_VARIANTS.
|
|
156
|
+
* Used to detect which Base14 family a font belongs to.
|
|
157
|
+
*/
|
|
158
|
+
export const BASE14_VARIANT_LOOKUP: ReadonlyMap<string, { family: Base14Family; variant: Base14Variant }> = (() => {
|
|
159
|
+
const lookup = new Map<string, { family: Base14Family; variant: Base14Variant }>();
|
|
160
|
+
|
|
161
|
+
for (const [family, variants] of Object.entries(BASE14_FAMILY_VARIANTS) as Array<[Base14Family, Record<Base14Variant, string>]>) {
|
|
162
|
+
for (const [variant, name] of Object.entries(variants) as Array<[Base14Variant, string]>) {
|
|
163
|
+
lookup.set(name.toLowerCase(), { family, variant });
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return lookup;
|
|
168
|
+
})();
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Detect the Base14 family for a given font name.
|
|
172
|
+
*
|
|
173
|
+
* @param baseFont - Font name to check
|
|
174
|
+
* @returns Base14 family name if found, null otherwise
|
|
175
|
+
*/
|
|
176
|
+
export function detectBase14Family(baseFont: string): Base14Family | null {
|
|
177
|
+
const entry = BASE14_VARIANT_LOOKUP.get(baseFont.toLowerCase());
|
|
178
|
+
return entry ? entry.family : null;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Classify the variant type of a Base14 font.
|
|
183
|
+
*
|
|
184
|
+
* @param baseFont - Font name to check
|
|
185
|
+
* @returns Variant type (normal, italic, bold, boldItalic)
|
|
186
|
+
*/
|
|
187
|
+
export function classifyBase14Variant(baseFont: string): Base14Variant {
|
|
188
|
+
const entry = BASE14_VARIANT_LOOKUP.get(baseFont.toLowerCase());
|
|
189
|
+
return entry ? entry.variant : "normal";
|
|
190
|
+
}
|