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,242 @@
|
|
|
1
|
+
import type { ImageInfo } from "./types.js";
|
|
2
|
+
import { WebpRiffParser } from "./webp-riff-parser.js";
|
|
3
|
+
import { Vp8lDecoder } from "./webp-vp8l-decoder.js";
|
|
4
|
+
|
|
5
|
+
// ============================================================================
|
|
6
|
+
// Types & Interfaces
|
|
7
|
+
// ============================================================================
|
|
8
|
+
|
|
9
|
+
export interface DecodeOptions {
|
|
10
|
+
maxWidth?: number;
|
|
11
|
+
maxHeight?: number;
|
|
12
|
+
scale?: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// ============================================================================
|
|
16
|
+
// Constants
|
|
17
|
+
// ============================================================================
|
|
18
|
+
|
|
19
|
+
const TEXT_DECODER = new TextDecoder("ascii");
|
|
20
|
+
|
|
21
|
+
// ============================================================================
|
|
22
|
+
// DataReader Class
|
|
23
|
+
// ============================================================================
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Sequential binary data reader with automatic offset tracking
|
|
27
|
+
*/
|
|
28
|
+
export class DataReader {
|
|
29
|
+
private view: DataView;
|
|
30
|
+
private offset: number = 0;
|
|
31
|
+
|
|
32
|
+
constructor(buffer: ArrayBuffer) {
|
|
33
|
+
this.view = new DataView(buffer);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
public seek(offset: number): void {
|
|
37
|
+
if (offset < 0 || offset > this.view.byteLength) {
|
|
38
|
+
throw new RangeError(`Seek offset ${offset} out of bounds [0, ${this.view.byteLength}]`);
|
|
39
|
+
}
|
|
40
|
+
this.offset = offset;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
public tell(): number {
|
|
44
|
+
return this.offset;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
public hasMore(): boolean {
|
|
48
|
+
return this.offset < this.view.byteLength;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
public getUint8(): number {
|
|
52
|
+
this.checkBounds(1);
|
|
53
|
+
const value = this.view.getUint8(this.offset);
|
|
54
|
+
this.offset += 1;
|
|
55
|
+
return value;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
public getUint16(littleEndian: boolean = false): number {
|
|
59
|
+
this.checkBounds(2);
|
|
60
|
+
const value = this.view.getUint16(this.offset, littleEndian);
|
|
61
|
+
this.offset += 2;
|
|
62
|
+
return value;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
public getUint32(littleEndian: boolean = false): number {
|
|
66
|
+
this.checkBounds(4);
|
|
67
|
+
const value = this.view.getUint32(this.offset, littleEndian);
|
|
68
|
+
this.offset += 4;
|
|
69
|
+
return value;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
public getString(length: number): string {
|
|
73
|
+
this.checkBounds(length);
|
|
74
|
+
const bytes = new Uint8Array(this.view.buffer, this.view.byteOffset + this.offset, length);
|
|
75
|
+
this.offset += length;
|
|
76
|
+
return TEXT_DECODER.decode(bytes);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
public getView(length: number): DataView {
|
|
80
|
+
this.checkBounds(length);
|
|
81
|
+
const view = new DataView(this.view.buffer, this.view.byteOffset + this.offset, length);
|
|
82
|
+
this.offset += length;
|
|
83
|
+
return view;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
private checkBounds(length: number): void {
|
|
87
|
+
if (this.offset + length > this.view.byteLength) {
|
|
88
|
+
throw new RangeError(`Read beyond buffer bounds: ${this.offset + length} > ${this.view.byteLength}`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Reads individual bits from a DataView (LSB first)
|
|
95
|
+
*/
|
|
96
|
+
export class BitReader {
|
|
97
|
+
private view: DataView;
|
|
98
|
+
private bytePos: number = 0;
|
|
99
|
+
private bitPos: number = 0;
|
|
100
|
+
|
|
101
|
+
constructor(view: DataView) {
|
|
102
|
+
this.view = view;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
public readBits(n: number): number {
|
|
106
|
+
if (n > 32) {
|
|
107
|
+
throw new Error("Cannot read more than 32 bits at once");
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
let value = 0;
|
|
111
|
+
for (let i = 0; i < n; i++) {
|
|
112
|
+
if (this.bytePos >= this.view.byteLength) {
|
|
113
|
+
// Pad with zeros if we run out of data (end of stream)
|
|
114
|
+
return value;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const byte = this.view.getUint8(this.bytePos);
|
|
118
|
+
const bit = (byte >> this.bitPos) & 1;
|
|
119
|
+
value |= bit << i;
|
|
120
|
+
|
|
121
|
+
this.bitPos++;
|
|
122
|
+
if (this.bitPos === 8) {
|
|
123
|
+
this.bitPos = 0;
|
|
124
|
+
this.bytePos++;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return value;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
public hasMore(): boolean {
|
|
131
|
+
return this.bytePos < this.view.byteLength;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// For debugging - peek at next bits without consuming them
|
|
135
|
+
public peekBits(n: number): number {
|
|
136
|
+
const oldBytePos = this.bytePos;
|
|
137
|
+
const oldBitPos = this.bitPos;
|
|
138
|
+
const value = this.readBits(n);
|
|
139
|
+
this.bytePos = oldBytePos;
|
|
140
|
+
this.bitPos = oldBitPos;
|
|
141
|
+
return value;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export abstract class BaseDecoder {
|
|
146
|
+
|
|
147
|
+
protected static calculateDimensions(
|
|
148
|
+
width: number,
|
|
149
|
+
height: number,
|
|
150
|
+
options: DecodeOptions
|
|
151
|
+
): { targetWidth: number; targetHeight: number } {
|
|
152
|
+
let targetWidth = width;
|
|
153
|
+
let targetHeight = height;
|
|
154
|
+
|
|
155
|
+
if (options.scale && options.scale > 0) {
|
|
156
|
+
targetWidth = Math.max(1, Math.round(width * options.scale));
|
|
157
|
+
targetHeight = Math.max(1, Math.round(height * options.scale));
|
|
158
|
+
} else if (options.maxWidth || options.maxHeight) {
|
|
159
|
+
const scale = Math.min(
|
|
160
|
+
options.maxWidth ? options.maxWidth / width : Infinity,
|
|
161
|
+
options.maxHeight ? options.maxHeight / height : Infinity
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
if (scale > 0 && scale < 1) {
|
|
165
|
+
targetWidth = Math.max(1, Math.round(width * scale));
|
|
166
|
+
targetHeight = Math.max(1, Math.round(height * scale));
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return { targetWidth, targetHeight };
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
protected static resizeNN(
|
|
174
|
+
src: Uint8Array,
|
|
175
|
+
sw: number,
|
|
176
|
+
sh: number,
|
|
177
|
+
tw: number,
|
|
178
|
+
th: number,
|
|
179
|
+
channels: number,
|
|
180
|
+
): Uint8Array {
|
|
181
|
+
if (sw === tw && sh === th) return src;
|
|
182
|
+
|
|
183
|
+
const dst = new Uint8Array(tw * th * channels);
|
|
184
|
+
const xRatio = sw / tw;
|
|
185
|
+
const yRatio = sh / th;
|
|
186
|
+
|
|
187
|
+
for (let y = 0; y < th; y++) {
|
|
188
|
+
const sy = Math.min(sh - 1, Math.floor(y * yRatio));
|
|
189
|
+
const srcRowOffset = sy * sw * channels;
|
|
190
|
+
const dstRowOffset = y * tw * channels;
|
|
191
|
+
|
|
192
|
+
for (let x = 0; x < tw; x++) {
|
|
193
|
+
const sx = Math.min(sw - 1, Math.floor(x * xRatio));
|
|
194
|
+
const si = srcRowOffset + sx * channels;
|
|
195
|
+
const di = dstRowOffset + x * channels;
|
|
196
|
+
|
|
197
|
+
for (let c = 0; c < channels; c++) {
|
|
198
|
+
dst[di + c] = src[si + c];
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return dst;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
public abstract decode(buffer: ArrayBuffer, options?: DecodeOptions): Promise<ImageInfo>;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export class WebpDecoder extends BaseDecoder {
|
|
209
|
+
private riffParser = new WebpRiffParser();
|
|
210
|
+
private vp8lDecoder = new Vp8lDecoder();
|
|
211
|
+
|
|
212
|
+
public async decode(
|
|
213
|
+
buffer: ArrayBuffer,
|
|
214
|
+
options: DecodeOptions = {},
|
|
215
|
+
): Promise<ImageInfo> {
|
|
216
|
+
const reader = new DataReader(buffer);
|
|
217
|
+
|
|
218
|
+
// Validate RIFF header
|
|
219
|
+
this.riffParser.validateHeader(reader);
|
|
220
|
+
|
|
221
|
+
// Parse chunks
|
|
222
|
+
const chunks = this.riffParser.parseChunks(reader);
|
|
223
|
+
|
|
224
|
+
// Determine format and decode
|
|
225
|
+
const vp8xChunk = chunks.find(c => c.fourCC === 'VP8X');
|
|
226
|
+
if (vp8xChunk) {
|
|
227
|
+
return this.vp8lDecoder.decodeVp8x(vp8xChunk, chunks, options, WebpDecoder.calculateDimensions, WebpDecoder.resizeNN);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const vp8lChunk = chunks.find(c => c.fourCC === 'VP8L');
|
|
231
|
+
if (vp8lChunk) {
|
|
232
|
+
return this.vp8lDecoder.decodeVp8l(vp8lChunk, options, WebpDecoder.calculateDimensions, WebpDecoder.resizeNN);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const vp8Chunk = chunks.find(c => c.fourCC === 'VP8 ');
|
|
236
|
+
if (vp8Chunk) {
|
|
237
|
+
throw new Error("VP8 (lossy) WebP format is not yet supported");
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
throw new Error("Unsupported WebP format: No recognized image chunk found");
|
|
241
|
+
}
|
|
242
|
+
}
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import { BitReader } from "./webp-decoder.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Huffman code representation
|
|
5
|
+
*/
|
|
6
|
+
export interface HuffmanCode {
|
|
7
|
+
symbol: number;
|
|
8
|
+
length: number;
|
|
9
|
+
code: number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Huffman tree with optional lookup table for performance
|
|
14
|
+
*/
|
|
15
|
+
export interface HuffmanTree {
|
|
16
|
+
codes: HuffmanCode[];
|
|
17
|
+
maxLength: number;
|
|
18
|
+
lookupTable?: Map<number, number>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const CODE_LENGTH_ORDER = [17, 18, 0, 1, 2, 3, 4, 5, 16, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] as const;
|
|
22
|
+
const HUFFMAN_GROUPS = 5;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Huffman code decoder for WebP VP8L format
|
|
26
|
+
* Responsibility: Build and decode Huffman trees
|
|
27
|
+
*/
|
|
28
|
+
export class WebpHuffmanDecoder {
|
|
29
|
+
/**
|
|
30
|
+
* Read all Huffman code groups (5 groups for VP8L)
|
|
31
|
+
*/
|
|
32
|
+
readHuffmanCodes(br: BitReader): HuffmanTree[] {
|
|
33
|
+
const huffmanCodes: HuffmanTree[] = [];
|
|
34
|
+
|
|
35
|
+
for (let i = 0; i < HUFFMAN_GROUPS; i++) {
|
|
36
|
+
const alphabetSize = i === 4 ? 40 : 280; // 256 + 24 = 280
|
|
37
|
+
huffmanCodes[i] = this.readHuffmanCode(br, alphabetSize);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return huffmanCodes;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Read a single Huffman code
|
|
45
|
+
*/
|
|
46
|
+
private readHuffmanCode(br: BitReader, alphabetSize: number): HuffmanTree {
|
|
47
|
+
const simple = br.readBits(1);
|
|
48
|
+
|
|
49
|
+
if (simple) {
|
|
50
|
+
return this.readSimpleHuffmanCode(br);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Read code length codes
|
|
54
|
+
const codeLengthCodeLengths = new Array(19).fill(0);
|
|
55
|
+
const numCodeLengthCodes = 4 + br.readBits(4);
|
|
56
|
+
|
|
57
|
+
for (let i = 0; i < numCodeLengthCodes; i++) {
|
|
58
|
+
codeLengthCodeLengths[CODE_LENGTH_ORDER[i]] = br.readBits(3);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const codeLengthTree = this.buildHuffmanTree(codeLengthCodeLengths);
|
|
62
|
+
const codeLengths = this.readCodeLengths(br, codeLengthTree, alphabetSize);
|
|
63
|
+
|
|
64
|
+
return this.buildHuffmanTree(codeLengths);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Read simple Huffman code (1 or 2 symbols)
|
|
69
|
+
*/
|
|
70
|
+
private readSimpleHuffmanCode(br: BitReader): HuffmanTree {
|
|
71
|
+
const numSymbols = br.readBits(1) + 1;
|
|
72
|
+
const symbols: number[] = [];
|
|
73
|
+
|
|
74
|
+
const is8Bits = br.readBits(1);
|
|
75
|
+
const bitCount = is8Bits ? 8 : 1;
|
|
76
|
+
|
|
77
|
+
symbols.push(br.readBits(bitCount));
|
|
78
|
+
if (numSymbols === 2) {
|
|
79
|
+
symbols.push(br.readBits(bitCount));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return this.buildSimpleHuffman(symbols);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Read code lengths for symbols
|
|
87
|
+
*/
|
|
88
|
+
private readCodeLengths(br: BitReader, codeLengthTree: HuffmanTree, alphabetSize: number): number[] {
|
|
89
|
+
const codeLengths = new Array(alphabetSize).fill(0);
|
|
90
|
+
let i = 0;
|
|
91
|
+
|
|
92
|
+
while (i < alphabetSize) {
|
|
93
|
+
const code = this.readSymbol(br, codeLengthTree);
|
|
94
|
+
|
|
95
|
+
if (code < 16) {
|
|
96
|
+
codeLengths[i++] = code;
|
|
97
|
+
} else {
|
|
98
|
+
let repeatCount = 0;
|
|
99
|
+
let repeatValue = 0;
|
|
100
|
+
|
|
101
|
+
if (code === 16) {
|
|
102
|
+
repeatCount = 3 + br.readBits(2);
|
|
103
|
+
repeatValue = codeLengths[i - 1];
|
|
104
|
+
} else if (code === 17) {
|
|
105
|
+
repeatCount = 3 + br.readBits(3);
|
|
106
|
+
} else if (code === 18) {
|
|
107
|
+
repeatCount = 11 + br.readBits(7);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
for (let j = 0; j < repeatCount && i < alphabetSize; j++) {
|
|
111
|
+
codeLengths[i++] = repeatValue;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return codeLengths;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Build simple Huffman tree from 1-2 symbols
|
|
121
|
+
*/
|
|
122
|
+
private buildSimpleHuffman(symbols: number[]): HuffmanTree {
|
|
123
|
+
if (symbols.length === 1) {
|
|
124
|
+
return { codes: [{ symbol: symbols[0], length: 0, code: 0 }], maxLength: 0 };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return {
|
|
128
|
+
codes: [
|
|
129
|
+
{ symbol: symbols[0], length: 1, code: 0 },
|
|
130
|
+
{ symbol: symbols[1], length: 1, code: 1 }
|
|
131
|
+
],
|
|
132
|
+
maxLength: 1
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Build Huffman tree from code lengths using canonical Huffman code algorithm
|
|
138
|
+
*/
|
|
139
|
+
buildHuffmanTree(codeLengths: number[]): HuffmanTree {
|
|
140
|
+
// Filter out invalid values and find the max length
|
|
141
|
+
const validLengths = codeLengths.filter(len => len > 0 && Number.isFinite(len));
|
|
142
|
+
const maxLength = validLengths.length > 0 ? Math.max(...validLengths) : 0;
|
|
143
|
+
const codes: HuffmanCode[] = [];
|
|
144
|
+
|
|
145
|
+
if (maxLength === 0) {
|
|
146
|
+
return { codes: [], maxLength: 0, lookupTable: new Map() };
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const bl_count = new Array(maxLength + 1).fill(0);
|
|
150
|
+
const next_code = new Array(maxLength + 1).fill(0);
|
|
151
|
+
|
|
152
|
+
// Count codes per length
|
|
153
|
+
for (const len of codeLengths) {
|
|
154
|
+
if (len > 0 && Number.isFinite(len)) bl_count[len]++;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Calculate starting codes for each length
|
|
158
|
+
next_code[1] = 0;
|
|
159
|
+
for (let bits = 2; bits <= maxLength; bits++) {
|
|
160
|
+
next_code[bits] = (next_code[bits - 1] + bl_count[bits - 1]) << 1;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Assign codes to symbols
|
|
164
|
+
const lookupTable = new Map<number, number>();
|
|
165
|
+
for (let n = 0; n < codeLengths.length; n++) {
|
|
166
|
+
const len = codeLengths[n];
|
|
167
|
+
if (len > 0 && Number.isFinite(len)) {
|
|
168
|
+
const codeValue = next_code[len];
|
|
169
|
+
codes.push({ symbol: n, length: len, code: codeValue });
|
|
170
|
+
// Create lookup key using a bit shift approach
|
|
171
|
+
// Store as (code << 4) | length for fast lookup
|
|
172
|
+
const key = (codeValue << 4) | len;
|
|
173
|
+
lookupTable.set(key, n);
|
|
174
|
+
next_code[len]++;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return { codes, maxLength, lookupTable };
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Read a symbol from the bitstream using the Huffman tree
|
|
183
|
+
*/
|
|
184
|
+
readSymbol(br: BitReader, tree: HuffmanTree): number {
|
|
185
|
+
if (tree.maxLength === 0) {
|
|
186
|
+
return tree.codes[0].symbol;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Use lookup table for O(1) access
|
|
190
|
+
if (tree.lookupTable) {
|
|
191
|
+
let code = 0;
|
|
192
|
+
for (let i = 0; i < tree.maxLength; i++) {
|
|
193
|
+
code = (code << 1) | br.readBits(1);
|
|
194
|
+
// Check if this code with the current length exists
|
|
195
|
+
const key = (code << 4) | (i + 1);
|
|
196
|
+
const symbol = tree.lookupTable.get(key);
|
|
197
|
+
if (symbol !== undefined) {
|
|
198
|
+
return symbol;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Fallback to linear search through codes
|
|
204
|
+
for (const codeInfo of tree.codes) {
|
|
205
|
+
let code = 0;
|
|
206
|
+
for (let i = 0; i < codeInfo.length; i++) {
|
|
207
|
+
code = (code << 1) | br.readBits(1);
|
|
208
|
+
}
|
|
209
|
+
if (code === codeInfo.code) {
|
|
210
|
+
return codeInfo.symbol;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// If no symbol is found, return 0 as fallback
|
|
215
|
+
// This prevents the decoder from failing completely
|
|
216
|
+
return 0;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { DataReader } from "./webp-decoder.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Represents a single RIFF chunk
|
|
5
|
+
*/
|
|
6
|
+
export interface RiffChunk {
|
|
7
|
+
fourCC: string;
|
|
8
|
+
size: number;
|
|
9
|
+
data: DataView;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Parser for WebP RIFF container structure
|
|
14
|
+
* Responsibility: Parse and validate RIFF chunks
|
|
15
|
+
*/
|
|
16
|
+
export class WebpRiffParser {
|
|
17
|
+
/**
|
|
18
|
+
* Validate RIFF header signature
|
|
19
|
+
*/
|
|
20
|
+
validateHeader(reader: DataReader): void {
|
|
21
|
+
const riff = reader.getString(4);
|
|
22
|
+
if (riff !== 'RIFF') {
|
|
23
|
+
throw new Error("Invalid WebP: Missing RIFF header");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
reader.getUint32(true); // File size (unused)
|
|
27
|
+
|
|
28
|
+
const webp = reader.getString(4);
|
|
29
|
+
if (webp !== 'WEBP') {
|
|
30
|
+
throw new Error("Invalid WebP: Missing WEBP signature");
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Parse all chunks from the RIFF container
|
|
36
|
+
*/
|
|
37
|
+
parseChunks(reader: DataReader): RiffChunk[] {
|
|
38
|
+
const chunks: RiffChunk[] = [];
|
|
39
|
+
|
|
40
|
+
while (reader.hasMore()) {
|
|
41
|
+
const fourCC = reader.getString(4);
|
|
42
|
+
const size = reader.getUint32(true);
|
|
43
|
+
const data = reader.getView(size);
|
|
44
|
+
chunks.push({ fourCC, size, data });
|
|
45
|
+
|
|
46
|
+
// Skip padding byte if chunk size is odd
|
|
47
|
+
if (size % 2 === 1 && reader.hasMore()) {
|
|
48
|
+
reader.seek(reader.tell() + 1);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return chunks;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import { BitReader, type DecodeOptions } from "./webp-decoder.js";
|
|
2
|
+
import type { ImageInfo } from "./types.js";
|
|
3
|
+
import { WebpHuffmanDecoder, type HuffmanTree } from "./webp-huffman.js";
|
|
4
|
+
import type { RiffChunk } from "./webp-riff-parser.js";
|
|
5
|
+
|
|
6
|
+
const VP8L_SIGNATURE = 0x2f;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* VP8L (lossless WebP) decoder
|
|
10
|
+
* Responsibility: Decode VP8L image data
|
|
11
|
+
*/
|
|
12
|
+
export class Vp8lDecoder {
|
|
13
|
+
private huffmanDecoder = new WebpHuffmanDecoder();
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Decode a VP8L chunk into image data
|
|
17
|
+
*/
|
|
18
|
+
decodeVp8l(chunk: RiffChunk, options: DecodeOptions, calculateDimensions: (w: number, h: number, opts: DecodeOptions) => { targetWidth: number; targetHeight: number }, resizeNN: (src: Uint8Array, sw: number, sh: number, tw: number, th: number, channels: number) => Uint8Array): ImageInfo {
|
|
19
|
+
const br = new BitReader(chunk.data);
|
|
20
|
+
|
|
21
|
+
// Read signature
|
|
22
|
+
const signature = br.readBits(8);
|
|
23
|
+
if (signature !== VP8L_SIGNATURE) {
|
|
24
|
+
throw new Error("Invalid VP8L signature");
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Read dimensions
|
|
28
|
+
const width = br.readBits(14) + 1;
|
|
29
|
+
const height = br.readBits(14) + 1;
|
|
30
|
+
const version = br.readBits(3);
|
|
31
|
+
|
|
32
|
+
if (version !== 0) {
|
|
33
|
+
throw new Error(`Unsupported VP8L version: ${version}`);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Skip transforms for now (they're complex)
|
|
37
|
+
let transformsPresent = br.readBits(1);
|
|
38
|
+
while (transformsPresent) {
|
|
39
|
+
const transformType = br.readBits(2);
|
|
40
|
+
|
|
41
|
+
if (transformType === 0 || transformType === 1) { // Predictor/Color Transform
|
|
42
|
+
const sizeBits = br.readBits(3) + 2;
|
|
43
|
+
const blockWidth = this.subSampleSize(width, sizeBits);
|
|
44
|
+
const blockHeight = this.subSampleSize(height, sizeBits);
|
|
45
|
+
this.skipTransformImage(br, blockWidth, blockHeight);
|
|
46
|
+
} else if (transformType === 3) { // Color Indexing
|
|
47
|
+
// Skip palette reading for simplicity
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
transformsPresent = br.readBits(1);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Read Huffman codes and decode pixel data
|
|
54
|
+
const huffmanCodes = this.huffmanDecoder.readHuffmanCodes(br);
|
|
55
|
+
const pixels = this.decodePixelData(br, width, height, huffmanCodes);
|
|
56
|
+
|
|
57
|
+
// Calculate target dimensions and resize if needed
|
|
58
|
+
const { targetWidth, targetHeight } = calculateDimensions(width, height, options);
|
|
59
|
+
const finalPixels = resizeNN(pixels, width, height, targetWidth, targetHeight, 4);
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
width: targetWidth,
|
|
63
|
+
height: targetHeight,
|
|
64
|
+
format: "webp",
|
|
65
|
+
channels: 4,
|
|
66
|
+
bitsPerChannel: 8,
|
|
67
|
+
data: finalPixels.buffer as ArrayBuffer,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Decode pixel data from VP8L bitstream
|
|
73
|
+
*/
|
|
74
|
+
private decodePixelData(
|
|
75
|
+
br: BitReader,
|
|
76
|
+
width: number,
|
|
77
|
+
height: number,
|
|
78
|
+
huffmanCodes: HuffmanTree[]
|
|
79
|
+
): Uint8Array {
|
|
80
|
+
const pixels = new Uint8Array(width * height * 4);
|
|
81
|
+
let pixelIndex = 0;
|
|
82
|
+
const totalPixels = width * height;
|
|
83
|
+
|
|
84
|
+
for (let i = 0; i < totalPixels; i++) {
|
|
85
|
+
const green = this.huffmanDecoder.readSymbol(br, huffmanCodes[0]);
|
|
86
|
+
|
|
87
|
+
if (green < 256) {
|
|
88
|
+
// Literal pixel
|
|
89
|
+
const red = this.huffmanDecoder.readSymbol(br, huffmanCodes[1]);
|
|
90
|
+
const blue = this.huffmanDecoder.readSymbol(br, huffmanCodes[2]);
|
|
91
|
+
const alpha = this.huffmanDecoder.readSymbol(br, huffmanCodes[3]);
|
|
92
|
+
|
|
93
|
+
pixels[pixelIndex++] = red;
|
|
94
|
+
pixels[pixelIndex++] = green;
|
|
95
|
+
pixels[pixelIndex++] = blue;
|
|
96
|
+
pixels[pixelIndex++] = alpha;
|
|
97
|
+
} else {
|
|
98
|
+
// Backward reference (LZ77)
|
|
99
|
+
const lengthSymbol = green - 256;
|
|
100
|
+
const length = this.getLengthFromSymbol(lengthSymbol, br);
|
|
101
|
+
const distSymbol = this.huffmanDecoder.readSymbol(br, huffmanCodes[4]);
|
|
102
|
+
const distance = this.getDistanceFromSymbol(distSymbol, br);
|
|
103
|
+
|
|
104
|
+
// Copy pixels from earlier in the stream
|
|
105
|
+
for (let j = 0; j < length && i + j < totalPixels; j++) {
|
|
106
|
+
const srcIdx = pixelIndex - distance * 4;
|
|
107
|
+
if (srcIdx >= 0) {
|
|
108
|
+
pixels[pixelIndex++] = pixels[srcIdx];
|
|
109
|
+
pixels[pixelIndex++] = pixels[srcIdx + 1];
|
|
110
|
+
pixels[pixelIndex++] = pixels[srcIdx + 2];
|
|
111
|
+
pixels[pixelIndex++] = pixels[srcIdx + 3];
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
i += length - 1;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return pixels;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Calculate subsampled size
|
|
123
|
+
*/
|
|
124
|
+
private subSampleSize(size: number, samplingBits: number): number {
|
|
125
|
+
return (size + (1 << samplingBits) - 1) >> samplingBits;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Skip transform image data (simplified skip logic)
|
|
130
|
+
*/
|
|
131
|
+
private skipTransformImage(br: BitReader, width: number, height: number): void {
|
|
132
|
+
// Skip the Huffman codes and pixel data for transforms
|
|
133
|
+
try {
|
|
134
|
+
this.huffmanDecoder.readHuffmanCodes(br); // Skip Huffman codes
|
|
135
|
+
} catch {
|
|
136
|
+
// If reading Huffman codes fails, skip a reasonable amount of bits
|
|
137
|
+
const maxBitsToSkip = Math.min(width * height * 4, 10000);
|
|
138
|
+
for (let i = 0; i < maxBitsToSkip && br.hasMore(); i++) {
|
|
139
|
+
br.readBits(1);
|
|
140
|
+
}
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
const totalPixels = width * height;
|
|
144
|
+
// For simplicity, just skip a reasonable amount of data
|
|
145
|
+
// This is not accurate but avoids the complex decoding
|
|
146
|
+
for (let i = 0; i < Math.min(totalPixels * 4, 10000); i++) {
|
|
147
|
+
if (!br.hasMore()) break;
|
|
148
|
+
br.readBits(1);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Get length from LZ77 symbol
|
|
154
|
+
*/
|
|
155
|
+
private getLengthFromSymbol(symbol: number, br: BitReader): number {
|
|
156
|
+
if (symbol < 4) return symbol + 1;
|
|
157
|
+
const extraBits = (symbol - 2) >> 1;
|
|
158
|
+
const offset = (2 + (symbol & 1)) << extraBits;
|
|
159
|
+
return offset + br.readBits(extraBits) + 1;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Get distance from LZ77 symbol
|
|
164
|
+
*/
|
|
165
|
+
private getDistanceFromSymbol(symbol: number, br: BitReader): number {
|
|
166
|
+
if (symbol < 4) return symbol + 1;
|
|
167
|
+
const extraBits = (symbol - 2) >> 1;
|
|
168
|
+
const offset = (2 + (symbol & 1)) << extraBits;
|
|
169
|
+
return offset + br.readBits(extraBits) + 1;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Decode VP8X extended format
|
|
174
|
+
*/
|
|
175
|
+
decodeVp8x(chunk: RiffChunk, chunks: RiffChunk[], options: DecodeOptions, calculateDimensions: (w: number, h: number, opts: DecodeOptions) => { targetWidth: number; targetHeight: number }, resizeNN: (src: Uint8Array, sw: number, sh: number, tw: number, th: number, channels: number) => Uint8Array): ImageInfo {
|
|
176
|
+
const data = new Uint8Array(chunk.data.buffer, chunk.data.byteOffset, chunk.data.byteLength);
|
|
177
|
+
|
|
178
|
+
// VP8X flags (currently unused but available for future features)
|
|
179
|
+
const flags = data[0];
|
|
180
|
+
const hasAnimation = (flags & 0x02) !== 0;
|
|
181
|
+
|
|
182
|
+
if (hasAnimation) {
|
|
183
|
+
throw new Error("Animated WebP is not supported");
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Find the actual image data chunk
|
|
187
|
+
const vp8lChunk = chunks.find(c => c.fourCC === 'VP8L');
|
|
188
|
+
if (vp8lChunk) {
|
|
189
|
+
return this.decodeVp8l(vp8lChunk, options, calculateDimensions, resizeNN);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const vp8Chunk = chunks.find(c => c.fourCC === 'VP8 ');
|
|
193
|
+
if (vp8Chunk) {
|
|
194
|
+
throw new Error("VP8 (lossy) WebP format is not yet supported");
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
throw new Error("No image data found in VP8X container");
|
|
198
|
+
}
|
|
199
|
+
}
|