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,76 @@
|
|
|
1
|
+
import { Display, JustifyContent } from "../../css/enums.js";
|
|
2
|
+
import type { LayoutNode } from "../../dom/node.js";
|
|
3
|
+
|
|
4
|
+
export function resolveTextAlign(node: LayoutNode): string | undefined {
|
|
5
|
+
let current: LayoutNode | null = node;
|
|
6
|
+
while (current) {
|
|
7
|
+
const value = current.style.textAlign;
|
|
8
|
+
if (value && value !== "start" && value !== "auto") {
|
|
9
|
+
return value;
|
|
10
|
+
}
|
|
11
|
+
current = current.parent;
|
|
12
|
+
}
|
|
13
|
+
return undefined;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function resolveFallbackStartX(node: LayoutNode, advanceWidth: number, textAlign?: string): number {
|
|
17
|
+
let startX = node.box.x;
|
|
18
|
+
if (advanceWidth > 0 && Number.isFinite(node.box.contentWidth) && node.box.contentWidth > 0) {
|
|
19
|
+
const slack = Math.max(node.box.contentWidth - advanceWidth, 0);
|
|
20
|
+
if (textAlign === "center") {
|
|
21
|
+
startX = node.box.x + slack / 2;
|
|
22
|
+
} else if (textAlign === "right" || textAlign === "end") {
|
|
23
|
+
startX = node.box.x + slack;
|
|
24
|
+
} else if (textAlign === "left" || textAlign === "start") {
|
|
25
|
+
startX = node.box.x;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const flexStart = resolveFlexSingleChildStart(node, advanceWidth);
|
|
30
|
+
if (flexStart !== null) {
|
|
31
|
+
startX = flexStart;
|
|
32
|
+
}
|
|
33
|
+
return startX;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function resolveFlexSingleChildStart(node: LayoutNode, advanceWidth: number): number | null {
|
|
37
|
+
if (!node.parent || advanceWidth <= 0) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
const parent = node.parent;
|
|
41
|
+
if (parent.style.display !== Display.Flex) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
if (parent.children.length !== 1) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
const direction = parent.style.flexDirection ?? "row";
|
|
48
|
+
if (direction !== "row" && direction !== "row-reverse") {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
const justify = parent.style.justifyContent ?? JustifyContent.FlexStart;
|
|
52
|
+
const parentWidth = Number.isFinite(parent.box.contentWidth) ? parent.box.contentWidth : 0;
|
|
53
|
+
if (!(parentWidth > 0)) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
const constrainedWidth = advanceWidth > parentWidth ? parentWidth : advanceWidth;
|
|
57
|
+
const slack = Math.max(parentWidth - constrainedWidth, 0);
|
|
58
|
+
const parentStart = parent.box.x;
|
|
59
|
+
const parentEnd = parentStart + parentWidth;
|
|
60
|
+
const isReverse = direction === "row-reverse";
|
|
61
|
+
|
|
62
|
+
switch (justify) {
|
|
63
|
+
case JustifyContent.Center:
|
|
64
|
+
return parentStart + slack / 2;
|
|
65
|
+
case JustifyContent.FlexEnd:
|
|
66
|
+
case JustifyContent.End:
|
|
67
|
+
case JustifyContent.Right:
|
|
68
|
+
return isReverse ? parentStart : parentEnd - constrainedWidth;
|
|
69
|
+
case JustifyContent.FlexStart:
|
|
70
|
+
case JustifyContent.Start:
|
|
71
|
+
case JustifyContent.Left:
|
|
72
|
+
return isReverse ? parentEnd - constrainedWidth : parentStart;
|
|
73
|
+
default:
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { ComputedStyle } from "../../css/style.js";
|
|
2
|
+
import type { Decorations, RGBA } from "../types.js";
|
|
3
|
+
import { parseColor } from "./color-utils.js";
|
|
4
|
+
|
|
5
|
+
export function resolveDecorations(style: ComputedStyle): Decorations | undefined {
|
|
6
|
+
const value = style.textDecorationLine?.trim().toLowerCase();
|
|
7
|
+
if (!value || value === "none") {
|
|
8
|
+
return undefined;
|
|
9
|
+
}
|
|
10
|
+
const tokens = value.split(/\s+/);
|
|
11
|
+
const decoration: Decorations = {};
|
|
12
|
+
for (const token of tokens) {
|
|
13
|
+
switch (token) {
|
|
14
|
+
case "underline":
|
|
15
|
+
decoration.underline = true;
|
|
16
|
+
break;
|
|
17
|
+
case "overline":
|
|
18
|
+
decoration.overline = true;
|
|
19
|
+
break;
|
|
20
|
+
case "line-through":
|
|
21
|
+
decoration.lineThrough = true;
|
|
22
|
+
break;
|
|
23
|
+
default:
|
|
24
|
+
break;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
if (!(decoration.underline || decoration.overline || decoration.lineThrough)) {
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const styleKeyword = style.textDecorationStyle?.trim().toLowerCase();
|
|
32
|
+
switch (styleKeyword) {
|
|
33
|
+
case "solid":
|
|
34
|
+
case "double":
|
|
35
|
+
case "dotted":
|
|
36
|
+
case "dashed":
|
|
37
|
+
case "wavy":
|
|
38
|
+
decoration.style = styleKeyword;
|
|
39
|
+
break;
|
|
40
|
+
default:
|
|
41
|
+
decoration.style = "solid";
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const color = resolveDecorationColor(style);
|
|
46
|
+
if (color) {
|
|
47
|
+
decoration.color = color;
|
|
48
|
+
}
|
|
49
|
+
return decoration;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function resolveDecorationColor(style: ComputedStyle): RGBA | undefined {
|
|
53
|
+
const raw = style.textDecorationColor;
|
|
54
|
+
if (raw) {
|
|
55
|
+
if (raw.toLowerCase() === "currentcolor") {
|
|
56
|
+
return parseColor(style.color);
|
|
57
|
+
}
|
|
58
|
+
const parsed = parseColor(raw);
|
|
59
|
+
if (parsed) {
|
|
60
|
+
return parsed;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return parseColor(style.color);
|
|
64
|
+
}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import type { RenderBox, Run } from "../types.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Global pass to fix justified layout in the render tree.
|
|
5
|
+
*
|
|
6
|
+
* For each RenderBox with textAlign: "justify":
|
|
7
|
+
* - collect ALL runs from that box and its descendants
|
|
8
|
+
* - group runs by lineIndex
|
|
9
|
+
import type { RenderBox, Run } from "../types.js";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Global pass to fix justified layout in the render tree.
|
|
13
|
+
*
|
|
14
|
+
* For each RenderBox with textAlign: "justify":
|
|
15
|
+
* - collect ALL runs from that box and its descendants
|
|
16
|
+
* - group runs by lineIndex
|
|
17
|
+
* - sort each group by lineMatrix.e (X)
|
|
18
|
+
* -for each non-final line:
|
|
19
|
+
* * shift later runs by the cumulative expansion
|
|
20
|
+
* from previous runs: spacesInRun * wordSpacing
|
|
21
|
+
*/
|
|
22
|
+
export function applyTextLayoutAdjustments(root: RenderBox): void {
|
|
23
|
+
traverse(root, (box) => {
|
|
24
|
+
// Only process boxes with justify alignment
|
|
25
|
+
if (box.textAlign !== "justify") return true; // Continue searching
|
|
26
|
+
|
|
27
|
+
// Collect ALL runs from this box and its descendants
|
|
28
|
+
const allRuns: Run[] = [];
|
|
29
|
+
collectRunsFromTree(box, allRuns);
|
|
30
|
+
|
|
31
|
+
const candidates = allRuns.filter(
|
|
32
|
+
(run) => typeof run.lineIndex === "number",
|
|
33
|
+
);
|
|
34
|
+
if (candidates.length === 0) return false; // Stop descending
|
|
35
|
+
|
|
36
|
+
const groups = groupRunsByLineIndex(candidates);
|
|
37
|
+
for (const lineRuns of groups) {
|
|
38
|
+
adjustLinePositions(lineRuns);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return false; // Stop descending into this justified box
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function groupRunsByLineIndex(runs: Run[]): Run[][] {
|
|
46
|
+
const map = new Map<number, Run[]>();
|
|
47
|
+
|
|
48
|
+
for (const run of runs) {
|
|
49
|
+
const idx = run.lineIndex as number;
|
|
50
|
+
let group = map.get(idx);
|
|
51
|
+
if (!group) {
|
|
52
|
+
group = [];
|
|
53
|
+
map.set(idx, group);
|
|
54
|
+
}
|
|
55
|
+
group.push(run);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
for (const group of map.values()) {
|
|
59
|
+
group.sort((a, b) => {
|
|
60
|
+
const ax = a.lineMatrix?.e ?? 0;
|
|
61
|
+
const bx = b.lineMatrix?.e ?? 0;
|
|
62
|
+
return ax - bx;
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return Array.from(map.values());
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Adjust positions on a single visual line.
|
|
71
|
+
*
|
|
72
|
+
* - Skip last lines (isLastLine === true).
|
|
73
|
+
* - Skip lines with no spaces.
|
|
74
|
+
* - Walk from left to right:
|
|
75
|
+
* * shift run.x by cumulativeOffset
|
|
76
|
+
* * cumulativeOffset += spacesInRun * wordSpacing
|
|
77
|
+
*/
|
|
78
|
+
function adjustLinePositions(runs: Run[]): void {
|
|
79
|
+
if (runs.length === 0) return;
|
|
80
|
+
|
|
81
|
+
// Find first non-whitespace run to identify leading spaces
|
|
82
|
+
let firstContentIndex = 0;
|
|
83
|
+
while (
|
|
84
|
+
firstContentIndex < runs.length &&
|
|
85
|
+
/^\s+$/.test(runs[firstContentIndex].text)
|
|
86
|
+
) {
|
|
87
|
+
firstContentIndex++;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Calculate width to redistribute from leading spaces
|
|
91
|
+
let widthLost = 0;
|
|
92
|
+
for (let i = 0; i < firstContentIndex; i++) {
|
|
93
|
+
widthLost += runs[i].advanceWidth ?? 0;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Count remaining spaces
|
|
97
|
+
let remainingSpaces = 0;
|
|
98
|
+
for (let i = firstContentIndex; i < runs.length; i++) {
|
|
99
|
+
remainingSpaces += runs[i].spacesInRun ?? 0;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const isLastLine = runs[0].isLastLine ?? false;
|
|
103
|
+
|
|
104
|
+
// Redistribute width if not last line and we have spaces to distribute to
|
|
105
|
+
if (!isLastLine && widthLost > 0 && remainingSpaces > 0) {
|
|
106
|
+
const adjustment = widthLost / remainingSpaces;
|
|
107
|
+
for (let i = firstContentIndex; i < runs.length; i++) {
|
|
108
|
+
const run = runs[i];
|
|
109
|
+
if ((run.spacesInRun ?? 0) > 0) {
|
|
110
|
+
run.wordSpacing = (run.wordSpacing ?? 0) + adjustment;
|
|
111
|
+
// Update advanceWidth to reflect the new spacing
|
|
112
|
+
run.advanceWidth =
|
|
113
|
+
(run.advanceWidth ?? 0) + (run.spacesInRun! * adjustment);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Capture original positions and base widths to detect existing gaps
|
|
119
|
+
const originals = runs.map((run) => ({
|
|
120
|
+
x: run.lineMatrix?.e ?? 0,
|
|
121
|
+
baseWidth:
|
|
122
|
+
(run.advanceWidth ?? 0) -
|
|
123
|
+
(run.spacesInRun ?? 0) * (run.wordSpacing ?? 0),
|
|
124
|
+
fontSize: run.fontSize,
|
|
125
|
+
}));
|
|
126
|
+
|
|
127
|
+
let currentX = originals[0].x;
|
|
128
|
+
|
|
129
|
+
for (let i = 0; i < runs.length; i++) {
|
|
130
|
+
const run = runs[i];
|
|
131
|
+
|
|
132
|
+
// If it's a leading whitespace run, treat as zero width and skip
|
|
133
|
+
if (i < firstContentIndex) {
|
|
134
|
+
if (run.lineMatrix) {
|
|
135
|
+
run.lineMatrix.e = currentX;
|
|
136
|
+
}
|
|
137
|
+
// Set width to 0 to avoid overlaps in tests and rendering
|
|
138
|
+
run.advanceWidth = 0;
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (run.lineMatrix) {
|
|
143
|
+
run.lineMatrix.e = currentX;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const advanceWidth = run.advanceWidth ?? 0;
|
|
147
|
+
currentX += advanceWidth;
|
|
148
|
+
|
|
149
|
+
// If there's a significant gap between this run and the next (e.g. an image), preserve it.
|
|
150
|
+
// Otherwise, assume it's a text gap/overlap and close it (using our calculated advanceWidth).
|
|
151
|
+
if (i < runs.length - 1) {
|
|
152
|
+
const nextOriginal = originals[i + 1];
|
|
153
|
+
const currOriginal = originals[i];
|
|
154
|
+
|
|
155
|
+
const gap = nextOriginal.x - (currOriginal.x + currOriginal.baseWidth);
|
|
156
|
+
// Use the larger font size as a threshold for "is this a text gap?"
|
|
157
|
+
const threshold = Math.max(currOriginal.fontSize, nextOriginal.fontSize);
|
|
158
|
+
|
|
159
|
+
// Preserve gap if it's large, OR if the current run has 0 width (and wasn't stripped)
|
|
160
|
+
// This handles cases where layout engine produced 0-width runs but placed them with spacing.
|
|
161
|
+
const isStripped = i < firstContentIndex;
|
|
162
|
+
if (gap > threshold || (!isStripped && advanceWidth === 0 && gap > 0)) {
|
|
163
|
+
currentX += gap;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function collectRunsFromTree(box: RenderBox, accumulator: Run[]): void {
|
|
170
|
+
if (box.textRuns) {
|
|
171
|
+
accumulator.push(...box.textRuns);
|
|
172
|
+
}
|
|
173
|
+
for (const child of box.children) {
|
|
174
|
+
collectRunsFromTree(child, accumulator);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function traverse(box: RenderBox, visitor: (box: RenderBox) => boolean): void {
|
|
179
|
+
const continueTraversal = visitor(box);
|
|
180
|
+
if (continueTraversal) {
|
|
181
|
+
for (const child of box.children) {
|
|
182
|
+
traverse(child, visitor);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { resolvedLineHeight } from "../../css/style.js";
|
|
2
|
+
import type { ComputedStyle } from "../../css/style.js";
|
|
3
|
+
import { estimateLineWidth } from "../../layout/utils/text-metrics.js";
|
|
4
|
+
import type { LayoutNode } from "../../dom/node.js";
|
|
5
|
+
import type { Run, Decorations, RGBA } from "../types.js";
|
|
6
|
+
import { applyTextTransform } from "../../text/text-transform.js";
|
|
7
|
+
import { resolveTextShadows } from "../../pdf/utils/shadow-utils.js";
|
|
8
|
+
import { resolveTextAlign, resolveFallbackStartX } from "./text-alignment-resolver.js";
|
|
9
|
+
import { resolveDecorations } from "./text-decoration-utils.js";
|
|
10
|
+
|
|
11
|
+
export function createTextRuns(node: LayoutNode, color: RGBA | undefined, inheritedDecorations?: Decorations): Run[] {
|
|
12
|
+
const runs: Run[] = [];
|
|
13
|
+
const defaultColor = color ?? { r: 0, g: 0, b: 0, a: 1 };
|
|
14
|
+
const effectiveTextAlign = resolveTextAlign(node) ?? node.style.textAlign;
|
|
15
|
+
const decoration = inheritedDecorations ?? resolveDecorations(node.style);
|
|
16
|
+
const fontFamily = node.style.fontFamily ?? "sans-serif";
|
|
17
|
+
const fontSize = node.style.fontSize;
|
|
18
|
+
const fontWeight = node.style.fontWeight;
|
|
19
|
+
const fontStyle = node.style.fontStyle;
|
|
20
|
+
const fontVariant = node.style.fontVariant;
|
|
21
|
+
const letterSpacing = node.style.letterSpacing;
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
// Se o layout calculou inlineRuns (novo builder), use-os.
|
|
25
|
+
if (node.inlineRuns && node.inlineRuns.length > 0) {
|
|
26
|
+
for (const inlineRun of node.inlineRuns) {
|
|
27
|
+
const justifyLine =
|
|
28
|
+
effectiveTextAlign === "justify" && !inlineRun.isLastLine;
|
|
29
|
+
|
|
30
|
+
let wordSpacing: number | undefined;
|
|
31
|
+
const targetWidth =
|
|
32
|
+
inlineRun.targetWidth ?? inlineRun.lineWidth ?? inlineRun.width;
|
|
33
|
+
const lineWidth = inlineRun.lineWidth ?? inlineRun.width;
|
|
34
|
+
|
|
35
|
+
if (justifyLine && inlineRun.spaceCount > 0) {
|
|
36
|
+
const slack = Math.max(targetWidth - lineWidth, 0);
|
|
37
|
+
if (slack > 0) {
|
|
38
|
+
wordSpacing = slack / inlineRun.spaceCount;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Count spaces *inside this run's text*
|
|
43
|
+
const spacesInRun =
|
|
44
|
+
inlineRun.text.split("").reduce(
|
|
45
|
+
(count, ch) => (ch === " " ? count + 1 : count),
|
|
46
|
+
0,
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
const extraWidth =
|
|
50
|
+
spacesInRun > 0 && wordSpacing !== undefined
|
|
51
|
+
? spacesInRun * wordSpacing
|
|
52
|
+
: 0;
|
|
53
|
+
|
|
54
|
+
// Run-local visual width (base width + justified expansion)
|
|
55
|
+
const advanceWidth = inlineRun.width + extraWidth;
|
|
56
|
+
|
|
57
|
+
const resolvedShadows = resolveTextShadows(node, defaultColor);
|
|
58
|
+
const baseLineMatrix = {
|
|
59
|
+
a: 1,
|
|
60
|
+
b: 0,
|
|
61
|
+
c: 0,
|
|
62
|
+
d: 1,
|
|
63
|
+
e: inlineRun.startX,
|
|
64
|
+
f: inlineRun.baseline,
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
runs.push({
|
|
68
|
+
text: inlineRun.text.normalize("NFC"),
|
|
69
|
+
fontFamily,
|
|
70
|
+
fontSize,
|
|
71
|
+
fontWeight,
|
|
72
|
+
fontStyle,
|
|
73
|
+
fontVariant,
|
|
74
|
+
fill: defaultColor,
|
|
75
|
+
letterSpacing,
|
|
76
|
+
lineMatrix: baseLineMatrix,
|
|
77
|
+
wordSpacing,
|
|
78
|
+
decorations: decoration ? { ...decoration } : undefined,
|
|
79
|
+
advanceWidth,
|
|
80
|
+
textShadows: resolvedShadows,
|
|
81
|
+
|
|
82
|
+
// New metadata
|
|
83
|
+
lineIndex: inlineRun.lineIndex,
|
|
84
|
+
isLastLine: inlineRun.isLastLine,
|
|
85
|
+
spacesInRun,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
return runs;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Se o layout calculou caixas de linha, use-as (caminho leg legado).
|
|
92
|
+
if (node.lineBoxes && node.lineBoxes.length > 0) {
|
|
93
|
+
const lineHeight = resolvedLineHeight(node.style);
|
|
94
|
+
// For table cells, we need the parent cell's contentWidth, not the text node's width
|
|
95
|
+
const parentIsTableCell = node.parent && (node.parent.tagName === 'td' || node.parent.tagName === 'th');
|
|
96
|
+
const contentWidth = parentIsTableCell
|
|
97
|
+
? Math.max(node.parent!.box.contentWidth, 0)
|
|
98
|
+
: Math.max(node.box.contentWidth, 0);
|
|
99
|
+
// Alignment logic
|
|
100
|
+
let alignY = node.box.y;
|
|
101
|
+
// Vertical alignment
|
|
102
|
+
let totalTextHeight = node.lineBoxes.length * lineHeight;
|
|
103
|
+
if (node.style.verticalAlign === "middle") {
|
|
104
|
+
alignY = node.box.y + (node.box.contentHeight - totalTextHeight) / 2;
|
|
105
|
+
} else if (node.style.verticalAlign === "bottom") {
|
|
106
|
+
alignY = node.box.y + (node.box.contentHeight - totalTextHeight);
|
|
107
|
+
}
|
|
108
|
+
const justify = effectiveTextAlign === "justify";
|
|
109
|
+
for (let i = 0; i < node.lineBoxes.length; i++) {
|
|
110
|
+
const line = node.lineBoxes[i];
|
|
111
|
+
const normalizedText = line.text.normalize("NFC");
|
|
112
|
+
const baseWidth = line.width ?? estimateLineWidth(normalizedText, node.style);
|
|
113
|
+
const lineYOffset = i * lineHeight;
|
|
114
|
+
const baseline = alignY + lineYOffset + fontSize;
|
|
115
|
+
let wordSpacing: number | undefined;
|
|
116
|
+
if (justify && i < node.lineBoxes.length - 1) {
|
|
117
|
+
const gapCount = line.spaceCount ?? 0;
|
|
118
|
+
if (gapCount > 0) {
|
|
119
|
+
const targetWidth = line.targetWidth ?? contentWidth ?? baseWidth;
|
|
120
|
+
const slack = Math.max(targetWidth - baseWidth, 0);
|
|
121
|
+
if (slack > 0) {
|
|
122
|
+
wordSpacing = slack / gapCount;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
const targetWidth = line.targetWidth ?? baseWidth;
|
|
127
|
+
const advanceWidth =
|
|
128
|
+
wordSpacing !== undefined && wordSpacing !== 0 && targetWidth > 0
|
|
129
|
+
? Math.max(targetWidth, baseWidth)
|
|
130
|
+
: Math.max(baseWidth, 0);
|
|
131
|
+
|
|
132
|
+
// Apply horizontal text alignment
|
|
133
|
+
let startX = node.box.x;
|
|
134
|
+
if (advanceWidth > 0 && Number.isFinite(contentWidth) && contentWidth > 0) {
|
|
135
|
+
const slack = Math.max(contentWidth - advanceWidth, 0);
|
|
136
|
+
if (effectiveTextAlign === "center") {
|
|
137
|
+
startX = node.box.x + slack / 2;
|
|
138
|
+
} else if (effectiveTextAlign === "right" || effectiveTextAlign === "end") {
|
|
139
|
+
startX = node.box.x + slack;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const resolvedShadows = resolveTextShadows(node, defaultColor);
|
|
144
|
+
const baseLineMatrix = { a: 1, b: 0, c: 0, d: 1, e: startX, f: baseline };
|
|
145
|
+
runs.push({
|
|
146
|
+
text: normalizedText,
|
|
147
|
+
fontFamily,
|
|
148
|
+
fontSize,
|
|
149
|
+
fontWeight,
|
|
150
|
+
fontStyle,
|
|
151
|
+
fontVariant,
|
|
152
|
+
fill: defaultColor,
|
|
153
|
+
letterSpacing,
|
|
154
|
+
lineMatrix: baseLineMatrix,
|
|
155
|
+
wordSpacing,
|
|
156
|
+
decorations: decoration ? { ...decoration } : undefined,
|
|
157
|
+
advanceWidth,
|
|
158
|
+
textShadows: resolvedShadows,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
return runs;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Fallback para o comportamento original se não houver quebra de linha calculada
|
|
165
|
+
if (node.textContent) {
|
|
166
|
+
const raw = node.textContent;
|
|
167
|
+
const transformed = applyTextTransform(raw, node.style.textTransform);
|
|
168
|
+
const normalized = transformed.normalize("NFC");
|
|
169
|
+
// Se não houver lineBoxes, a baseline é a calculada para a caixa inteira.
|
|
170
|
+
const baseline = node.box.baseline > 0 ? node.box.baseline : node.box.y + node.box.contentHeight;
|
|
171
|
+
const advanceWidth = Math.max(estimateLineWidth(normalized, node.style), 0);
|
|
172
|
+
const startX = resolveFallbackStartX(node, advanceWidth, effectiveTextAlign);
|
|
173
|
+
|
|
174
|
+
const resolvedShadows = resolveTextShadows(node, defaultColor);
|
|
175
|
+
const baseLineMatrix = { a: 1, b: 0, c: 0, d: 1, e: startX, f: baseline };
|
|
176
|
+
return [{
|
|
177
|
+
text: normalized,
|
|
178
|
+
fontFamily,
|
|
179
|
+
fontSize,
|
|
180
|
+
fontWeight,
|
|
181
|
+
fontStyle,
|
|
182
|
+
fontVariant,
|
|
183
|
+
letterSpacing,
|
|
184
|
+
fill: defaultColor,
|
|
185
|
+
lineMatrix: baseLineMatrix,
|
|
186
|
+
decorations: decoration ? { ...decoration } : undefined,
|
|
187
|
+
advanceWidth,
|
|
188
|
+
textShadows: resolvedShadows,
|
|
189
|
+
}];
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return [];
|
|
193
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { Rect, TextMatrix } from "../types.js";
|
|
2
|
+
import { CoordinateTransformer } from "./coordinate-transformer.js";
|
|
3
|
+
import { ShapeRenderer } from "../renderers/shape-renderer.js";
|
|
4
|
+
import { svgMatrixToPdf } from "../transform-adapter.js";
|
|
5
|
+
|
|
6
|
+
export class TransformScopeManager {
|
|
7
|
+
constructor(
|
|
8
|
+
private readonly coordinateTransformer: CoordinateTransformer,
|
|
9
|
+
private readonly shapeRenderer: ShapeRenderer,
|
|
10
|
+
) {}
|
|
11
|
+
|
|
12
|
+
beginTransformScope(transform: TextMatrix, rect: Rect): void {
|
|
13
|
+
// Store transform info for shape renderer to use
|
|
14
|
+
const pdfMatrix = svgMatrixToPdf(transform);
|
|
15
|
+
if (!pdfMatrix) {
|
|
16
|
+
this.shapeRenderer.pushRawCommands(["q"]);
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Convert rect position to PDF coordinates
|
|
21
|
+
const xPt = this.coordinateTransformer.convertPxToPt(rect.x);
|
|
22
|
+
const localY = rect.y - this.coordinateTransformer.pageOffsetPx;
|
|
23
|
+
const yPt = this.coordinateTransformer.pageHeightPt - this.coordinateTransformer.convertPxToPt(localY);
|
|
24
|
+
|
|
25
|
+
// CSS transforms default to a 50% / 50% origin. Model that by translating
|
|
26
|
+
// relative coordinates so the transform happens around the center of the
|
|
27
|
+
// border box.
|
|
28
|
+
const originOffsetX = Number.isFinite(rect.width) ? rect.width / 2 : 0;
|
|
29
|
+
const originOffsetY = Number.isFinite(rect.height) ? rect.height / 2 : 0;
|
|
30
|
+
const translateBackX = this.coordinateTransformer.convertPxToPt(-originOffsetX);
|
|
31
|
+
const translateBackY = this.coordinateTransformer.convertPxToPt(originOffsetY);
|
|
32
|
+
const translateForwardX = this.coordinateTransformer.convertPxToPt(originOffsetX);
|
|
33
|
+
const translateForwardY = this.coordinateTransformer.convertPxToPt(-originOffsetY);
|
|
34
|
+
|
|
35
|
+
// Set up transform context: translate to position, apply transform
|
|
36
|
+
// This way shapes can be drawn at origin (0,0) and will be positioned and transformed correctly
|
|
37
|
+
const cmds: string[] = [
|
|
38
|
+
"q",
|
|
39
|
+
// Position element on page (applied last in the matrix chain)
|
|
40
|
+
`1 0 0 1 ${formatNumber(xPt)} ${formatNumber(yPt)} cm`,
|
|
41
|
+
// Translate back from pivot after applying skew/rotate
|
|
42
|
+
`1 0 0 1 ${formatNumber(translateForwardX)} ${formatNumber(translateForwardY)} cm`,
|
|
43
|
+
// Apply the css transform (already converted into PDF space)
|
|
44
|
+
`${formatNumber(pdfMatrix.a)} ${formatNumber(pdfMatrix.b)} ${formatNumber(pdfMatrix.c)} ${formatNumber(pdfMatrix.d)} 0 0 cm`,
|
|
45
|
+
// Move the local coordinate system so the transform pivot is the center of the box
|
|
46
|
+
`1 0 0 1 ${formatNumber(translateBackX)} ${formatNumber(translateBackY)} cm`,
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
// Add marker for testing
|
|
50
|
+
if (pdfMatrix.b !== 0 || pdfMatrix.c !== 0) {
|
|
51
|
+
cmds.push(`%PAGYRA_TRANSFORM ${formatNumber(pdfMatrix.a)} ${formatNumber(pdfMatrix.b)} ${formatNumber(pdfMatrix.c)} ${formatNumber(pdfMatrix.d)} ${formatNumber(pdfMatrix.e)} ${formatNumber(pdfMatrix.f)}`);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
this.shapeRenderer.pushRawCommands(cmds);
|
|
55
|
+
this.shapeRenderer.setTransformContext(rect);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
endTransformScope(): void {
|
|
59
|
+
this.shapeRenderer.pushRawCommands(["Q"]);
|
|
60
|
+
this.shapeRenderer.clearTransformContext();
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function formatNumber(value: number): string {
|
|
65
|
+
if (!Number.isFinite(value)) {
|
|
66
|
+
return "0";
|
|
67
|
+
}
|
|
68
|
+
return Number.isInteger(value) ? value.toString() : value.toFixed(4).replace(/0+$/, "").replace(/\.$/, "");
|
|
69
|
+
}
|