pagyra-js 0.0.19 → 0.0.21
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/README.md +55 -0
- package/dist/assets/fonts/licenses/selawik/SIL Open Font License.txt +43 -0
- package/dist/assets/fonts/ttf/arimo/Arimo-Bold.ttf +0 -0
- package/dist/assets/fonts/ttf/arimo/Arimo-BoldItalic.ttf +0 -0
- package/dist/assets/fonts/ttf/arimo/Arimo-Italic.ttf +0 -0
- package/dist/assets/fonts/ttf/arimo/Arimo-Regular.ttf +0 -0
- package/dist/assets/fonts/ttf/cinzeldecorative/CinzelDecorative-Black.ttf +0 -0
- package/dist/assets/fonts/ttf/cinzeldecorative/CinzelDecorative-Bold.ttf +0 -0
- package/dist/assets/fonts/ttf/cinzeldecorative/CinzelDecorative-Regular.ttf +0 -0
- package/dist/assets/fonts/ttf/dejavu/DejaVuSans.ttf +0 -0
- package/dist/assets/fonts/ttf/firecode/FiraCode-Bold.ttf +0 -0
- package/dist/assets/fonts/ttf/firecode/FiraCode-Light.ttf +0 -0
- package/dist/assets/fonts/ttf/firecode/FiraCode-Medium.ttf +0 -0
- package/dist/assets/fonts/ttf/firecode/FiraCode-Regular.ttf +0 -0
- package/dist/assets/fonts/ttf/firecode/FiraCode-SemiBold.ttf +0 -0
- package/dist/assets/fonts/ttf/notoemoji/NotoEmoji-Bold.ttf +0 -0
- package/dist/assets/fonts/ttf/notoemoji/NotoEmoji-Light.ttf +0 -0
- package/dist/assets/fonts/ttf/notoemoji/NotoEmoji-Medium.ttf +0 -0
- package/dist/assets/fonts/ttf/notoemoji/NotoEmoji-Regular.ttf +0 -0
- package/dist/assets/fonts/ttf/notoemoji/NotoEmoji-SemiBold.ttf +0 -0
- package/dist/assets/fonts/ttf/notosans/NotoSans-Regular.ttf +0 -0
- package/dist/assets/fonts/ttf/roboto/Roboto-Bold.ttf +0 -0
- package/dist/assets/fonts/ttf/roboto/Roboto-BoldItalic.ttf +0 -0
- package/dist/assets/fonts/ttf/roboto/Roboto-Italic.ttf +0 -0
- package/dist/assets/fonts/ttf/roboto/Roboto-Regular.ttf +0 -0
- package/dist/assets/fonts/ttf/selawik/selawk.ttf +0 -0
- package/dist/assets/fonts/ttf/selawik/selawkb.ttf +0 -0
- package/dist/assets/fonts/ttf/selawik/selawkl.ttf +0 -0
- package/dist/assets/fonts/ttf/selawik/selawksb.ttf +0 -0
- package/dist/assets/fonts/ttf/selawik/selawksl.ttf +0 -0
- package/dist/assets/fonts/ttf/stixtwomath/STIXTwoMath-Regular.ttf +0 -0
- package/dist/assets/fonts/ttf/tinos/Tinos-Bold.ttf +0 -0
- package/dist/assets/fonts/ttf/tinos/Tinos-BoldItalic.ttf +0 -0
- package/dist/assets/fonts/ttf/tinos/Tinos-Italic.ttf +0 -0
- package/dist/assets/fonts/ttf/tinos/Tinos-Regular.ttf +0 -0
- package/dist/assets/fonts/woff/lato/lato-latin-400-italic.woff +0 -0
- package/dist/assets/fonts/woff/lato/lato-latin-400-normal.woff +0 -0
- package/dist/assets/fonts/woff/lato/lato-latin-700-italic.woff +0 -0
- package/dist/assets/fonts/woff/lato/lato-latin-700-normal.woff +0 -0
- package/dist/assets/fonts/woff2/caveat/Caveat-Bold.woff2 +0 -0
- package/dist/assets/fonts/woff2/caveat/Caveat-Regular.woff2 +0 -0
- package/dist/assets/fonts/woff2/lato/lato-latin-400-italic.woff2 +0 -0
- package/dist/assets/fonts/woff2/lato/lato-latin-400-normal.woff2 +0 -0
- package/dist/assets/fonts/woff2/lato/lato-latin-700-italic.woff2 +0 -0
- package/dist/assets/fonts/woff2/lato/lato-latin-700-normal.woff2 +0 -0
- package/dist/browser/pagyra.min.js +34 -34
- package/dist/browser/pagyra.min.js.map +4 -4
- package/dist/playground/server.js +2 -0
- package/dist/src/css/compute-style/base-options.d.ts +7 -0
- package/dist/src/css/compute-style/base-options.js +24 -0
- package/dist/src/css/compute-style/declarations.d.ts +10 -0
- package/dist/src/css/compute-style/declarations.js +77 -0
- package/dist/src/css/compute-style/decoration.d.ts +8 -0
- package/dist/src/css/compute-style/decoration.js +55 -0
- package/dist/src/css/compute-style/defaults.d.ts +3 -0
- package/dist/src/css/compute-style/defaults.js +34 -0
- package/dist/src/css/compute-style/display.d.ts +3 -0
- package/dist/src/css/compute-style/display.js +85 -0
- package/dist/src/css/compute-style/float.d.ts +2 -0
- package/dist/src/css/compute-style/float.js +13 -0
- package/dist/src/css/compute-style/font.d.ts +12 -0
- package/dist/src/css/compute-style/font.js +57 -0
- package/dist/src/css/compute-style/overrides.d.ts +3 -0
- package/dist/src/css/compute-style/overrides.js +241 -0
- package/dist/src/css/compute-style.d.ts +2 -0
- package/dist/src/css/compute-style.js +34 -487
- package/dist/src/css/enums.d.ts +4 -0
- package/dist/src/css/enums.js +5 -0
- package/dist/src/css/layout-property-resolver.js +30 -18
- package/dist/src/css/length.d.ts +26 -2
- package/dist/src/css/length.js +48 -0
- package/dist/src/css/parsers/background-parser.js +1 -1
- package/dist/src/css/parsers/calc-parser.d.ts +2 -0
- package/dist/src/css/parsers/calc-parser.js +310 -0
- package/dist/src/css/parsers/content-parser.d.ts +2 -1
- package/dist/src/css/parsers/content-parser.js +7 -2
- package/dist/src/css/parsers/dimension-parser.js +37 -18
- package/dist/src/css/parsers/display-flex-parser.d.ts +4 -0
- package/dist/src/css/parsers/display-flex-parser.js +97 -0
- package/dist/src/css/parsers/filter-parser.d.ts +14 -0
- package/dist/src/css/parsers/filter-parser.js +255 -0
- package/dist/src/css/parsers/grid-parser-extended.d.ts +1 -0
- package/dist/src/css/parsers/grid-parser-extended.js +40 -1
- package/dist/src/css/parsers/grid-parser.d.ts +5 -2
- package/dist/src/css/parsers/grid-parser.js +71 -7
- package/dist/src/css/parsers/length-parser.d.ts +8 -3
- package/dist/src/css/parsers/length-parser.js +45 -2
- package/dist/src/css/parsers/margin-block-parser.js +3 -3
- package/dist/src/css/parsers/margin-parser.js +3 -3
- package/dist/src/css/parsers/padding-block-parser.js +3 -3
- package/dist/src/css/parsers/padding-inline-parser.js +3 -3
- package/dist/src/css/parsers/padding-parser.js +6 -6
- package/dist/src/css/parsers/position-parser.js +2 -22
- package/dist/src/css/parsers/register-parsers.js +29 -2
- package/dist/src/css/parsers/word-break-parser.d.ts +2 -0
- package/dist/src/css/parsers/word-break-parser.js +23 -0
- package/dist/src/css/properties/grid.d.ts +16 -2
- package/dist/src/css/properties/layout.d.ts +3 -1
- package/dist/src/css/properties/layout.js +1 -1
- package/dist/src/css/properties/misc.d.ts +5 -0
- package/dist/src/css/properties/typography.d.ts +3 -0
- package/dist/src/css/properties/visual.d.ts +36 -0
- package/dist/src/css/shorthands/box-shorthand.d.ts +2 -2
- package/dist/src/css/style-inheritance.d.ts +2 -1
- package/dist/src/css/style-inheritance.js +1 -0
- package/dist/src/css/style.d.ts +30 -10
- package/dist/src/css/style.js +8 -1
- package/dist/src/css/ua-defaults/base-defaults.d.ts +1 -0
- package/dist/src/css/ua-defaults/base-defaults.js +10 -1
- package/dist/src/css/ua-defaults/element-defaults.js +0 -2
- package/dist/src/html/css/parse-css.d.ts +2 -0
- package/dist/src/html/css/parse-css.js +32 -3
- package/dist/src/html/dom-converter/background-images.d.ts +3 -0
- package/dist/src/html/dom-converter/background-images.js +88 -0
- package/dist/src/html/dom-converter/convert-dom-node.d.ts +5 -0
- package/dist/src/html/dom-converter/convert-dom-node.js +81 -0
- package/dist/src/html/dom-converter/handlers/br-handler.d.ts +2 -0
- package/dist/src/html/dom-converter/handlers/br-handler.js +20 -0
- package/dist/src/html/dom-converter/handlers/form-control-handler.d.ts +2 -0
- package/dist/src/html/dom-converter/handlers/form-control-handler.js +28 -0
- package/dist/src/html/dom-converter/handlers/img-handler.d.ts +2 -0
- package/dist/src/html/dom-converter/handlers/img-handler.js +4 -0
- package/dist/src/html/dom-converter/handlers/index.d.ts +4 -0
- package/dist/src/html/dom-converter/handlers/index.js +19 -0
- package/dist/src/html/dom-converter/handlers/svg-handler.d.ts +2 -0
- package/dist/src/html/dom-converter/handlers/svg-handler.js +32 -0
- package/dist/src/html/dom-converter/handlers/types.d.ts +12 -0
- package/dist/src/html/dom-converter/handlers/types.js +2 -0
- package/dist/src/html/dom-converter/helpers.d.ts +7 -0
- package/dist/src/html/dom-converter/helpers.js +35 -0
- package/dist/src/html/dom-converter/index.d.ts +1 -0
- package/dist/src/html/dom-converter/index.js +1 -0
- package/dist/src/html/dom-converter/pseudo-elements.d.ts +6 -0
- package/dist/src/html/dom-converter/pseudo-elements.js +48 -0
- package/dist/src/html/dom-converter/text.d.ts +15 -0
- package/dist/src/html/dom-converter/text.js +170 -0
- package/dist/src/html/dom-converter.d.ts +1 -5
- package/dist/src/html/dom-converter.js +1 -412
- package/dist/src/html/image-converter.d.ts +5 -0
- package/dist/src/html/image-converter.js +8 -3
- package/dist/src/html-to-pdf/document-css.d.ts +14 -0
- package/dist/src/html-to-pdf/document-css.js +45 -0
- package/dist/src/html-to-pdf/fonts.d.ts +16 -0
- package/dist/src/html-to-pdf/fonts.js +74 -0
- package/dist/src/html-to-pdf/header-footer.d.ts +14 -0
- package/dist/src/html-to-pdf/header-footer.js +101 -0
- package/dist/src/html-to-pdf/html-parser.d.ts +6 -0
- package/dist/src/html-to-pdf/html-parser.js +81 -0
- package/dist/src/html-to-pdf/index.d.ts +3 -0
- package/dist/src/html-to-pdf/index.js +2 -0
- package/dist/src/html-to-pdf/layout-build.d.ts +37 -0
- package/dist/src/html-to-pdf/layout-build.js +73 -0
- package/dist/src/html-to-pdf/prepare-html-render.d.ts +2 -0
- package/dist/src/html-to-pdf/prepare-html-render.js +121 -0
- package/dist/src/html-to-pdf/render-finalize.d.ts +15 -0
- package/dist/src/html-to-pdf/render-finalize.js +27 -0
- package/dist/src/html-to-pdf/render-html-to-pdf.d.ts +3 -0
- package/dist/src/html-to-pdf/render-html-to-pdf.js +25 -0
- package/dist/src/html-to-pdf/resource-loader.d.ts +6 -0
- package/dist/src/html-to-pdf/resource-loader.js +120 -0
- package/dist/src/html-to-pdf/types.d.ts +38 -0
- package/dist/src/html-to-pdf/types.js +2 -0
- package/dist/src/html-to-pdf.d.ts +1 -37
- package/dist/src/html-to-pdf.js +1 -537
- package/dist/src/image/js-png-backend.d.ts +7 -0
- package/dist/src/image/js-png-backend.js +9 -0
- package/dist/src/image/png-backend.d.ts +5 -0
- package/dist/src/image/png-backend.js +1 -0
- package/dist/src/image/png-wasm-loader.d.ts +5 -0
- package/dist/src/image/png-wasm-loader.js +59 -0
- package/dist/src/image/wasm/png_decoder_wasm.d.ts +8 -0
- package/dist/src/image/wasm/png_decoder_wasm.js +24 -0
- package/dist/src/image/wasm/png_decoder_wasm_bg.js +16 -0
- package/dist/src/image/wasm-png-backend.d.ts +6 -0
- package/dist/src/image/wasm-png-backend.js +17 -0
- package/dist/src/layout/counter.d.ts +1 -2
- package/dist/src/layout/counter.js +18 -18
- package/dist/src/layout/inline/inline-utils.d.ts +1 -1
- package/dist/src/layout/inline/inline-utils.js +8 -7
- package/dist/src/layout/inline/layout.js +16 -3
- package/dist/src/layout/inline/run-placer.d.ts +1 -0
- package/dist/src/layout/inline/run-placer.js +2 -10
- package/dist/src/layout/pipeline/out-of-flow-manager.js +25 -1
- package/dist/src/layout/strategies/block.js +35 -24
- package/dist/src/layout/strategies/flex.js +305 -61
- package/dist/src/layout/strategies/form.d.ts +2 -0
- package/dist/src/layout/strategies/form.js +38 -13
- package/dist/src/layout/strategies/grid.js +166 -29
- package/dist/src/layout/strategies/image.js +53 -27
- package/dist/src/layout/strategies/inline.js +26 -21
- package/dist/src/layout/strategies/table.js +26 -18
- package/dist/src/layout/utils/content-measurer.d.ts +1 -1
- package/dist/src/layout/utils/content-measurer.js +8 -7
- package/dist/src/layout/utils/floats.d.ts +1 -0
- package/dist/src/layout/utils/floats.js +14 -12
- package/dist/src/layout/utils/margin.d.ts +4 -4
- package/dist/src/layout/utils/margin.js +20 -16
- package/dist/src/layout/utils/node-math.d.ts +12 -6
- package/dist/src/layout/utils/node-math.js +71 -41
- package/dist/src/layout/utils/sizing.js +2 -1
- package/dist/src/pdf/font-subset/font-registry.d.ts +6 -0
- package/dist/src/pdf/font-subset/font-registry.js +30 -2
- package/dist/src/pdf/header-footer-painter.d.ts +2 -0
- package/dist/src/pdf/header-footer-painter.js +52 -4
- package/dist/src/pdf/header-footer-renderer.js +12 -1
- package/dist/src/pdf/layout-tree-builder.js +5 -1
- package/dist/src/pdf/page-painter.js +13 -0
- package/dist/src/pdf/pagination.js +2 -2
- package/dist/src/pdf/renderer/box-painter.js +28 -3
- package/dist/src/pdf/renderer/page-paint.js +12 -3
- package/dist/src/pdf/renderers/radius-utils.js +31 -38
- package/dist/src/pdf/renderers/shape-renderer.js +1 -1
- package/dist/src/pdf/renderers/shape-utils.js +1 -1
- package/dist/src/pdf/renderers/text-renderer.d.ts +9 -1
- package/dist/src/pdf/renderers/text-renderer.js +36 -2
- package/dist/src/pdf/stacking/build-stacking-contexts.js +1 -2
- package/dist/src/pdf/stacking/resolve-paint-order.d.ts +5 -6
- package/dist/src/pdf/stacking/resolve-paint-order.js +29 -9
- package/dist/src/pdf/stacking/types.d.ts +14 -0
- package/dist/src/pdf/svg/shape-renderer.js +47 -20
- package/dist/src/pdf/types.d.ts +7 -1
- package/dist/src/pdf/utils/border-radius-utils.js +31 -38
- package/dist/src/pdf/utils/color-utils.js +17 -2
- package/dist/src/pdf/utils/filter-utils.d.ts +29 -0
- package/dist/src/pdf/utils/filter-utils.js +85 -0
- package/dist/src/pdf/utils/node-text-run-factory.js +1 -1
- package/dist/src/pdf/utils/text-layout-adjuster.d.ts +0 -8
- package/dist/src/pdf/utils/text-layout-adjuster.js +12 -9
- package/dist/src/shim/css-browser.d.ts +14 -9
- package/dist/src/shim/css-browser.js +50 -39
- package/dist/src/units/units.d.ts +1 -1
- package/dist/tests/css/box-sizing.spec.d.ts +1 -0
- package/dist/tests/css/box-sizing.spec.js +46 -0
- package/dist/tests/css/calc-parser.spec.d.ts +1 -0
- package/dist/tests/css/calc-parser.spec.js +68 -0
- package/dist/tests/css/container-query-units.spec.d.ts +1 -0
- package/dist/tests/css/container-query-units.spec.js +64 -0
- package/dist/tests/css/content-parser.spec.js +13 -0
- package/dist/tests/css/filter-parser.spec.d.ts +1 -0
- package/dist/tests/css/filter-parser.spec.js +116 -0
- package/dist/tests/css/flex-shorthand.spec.d.ts +1 -0
- package/dist/tests/css/flex-shorthand.spec.js +45 -0
- package/dist/tests/css/grid-clamp.spec.d.ts +1 -0
- package/dist/tests/css/grid-clamp.spec.js +82 -0
- package/dist/tests/css/parse-css-pseudo.spec.d.ts +1 -0
- package/dist/tests/css/parse-css-pseudo.spec.js +26 -0
- package/dist/tests/helpers/render-utils.d.ts +18 -2
- package/dist/tests/helpers/render-utils.js +25 -12
- package/dist/tests/html/dom-converter-pseudo-elements.spec.d.ts +1 -0
- package/dist/tests/html/dom-converter-pseudo-elements.spec.js +33 -0
- package/dist/tests/html/dom-converter-text.spec.d.ts +1 -0
- package/dist/tests/html/dom-converter-text.spec.js +67 -0
- package/dist/tests/image/png-backend.spec.d.ts +1 -0
- package/dist/tests/image/png-backend.spec.js +34 -0
- package/dist/tests/layout/box-sizing.spec.d.ts +1 -0
- package/dist/tests/layout/box-sizing.spec.js +75 -0
- package/dist/tests/layout/calc-padding.spec.d.ts +1 -0
- package/dist/tests/layout/calc-padding.spec.js +19 -0
- package/dist/tests/layout/container-query-units.spec.d.ts +1 -0
- package/dist/tests/layout/container-query-units.spec.js +24 -0
- package/dist/tests/layout/flex-auto-height.spec.d.ts +1 -0
- package/dist/tests/layout/flex-auto-height.spec.js +35 -0
- package/dist/tests/layout/flex-wrap-cards.spec.d.ts +1 -0
- package/dist/tests/layout/flex-wrap-cards.spec.js +16 -0
- package/dist/tests/layout/flex-wrap-grow-align-content.spec.d.ts +1 -0
- package/dist/tests/layout/flex-wrap-grow-align-content.spec.js +20 -0
- package/dist/tests/layout/grid-clamp-gap.spec.d.ts +1 -0
- package/dist/tests/layout/grid-clamp-gap.spec.js +22 -0
- package/dist/tests/layout/inline-fragments.spec.js +38 -0
- package/dist/tests/layout/paged-body-margin.spec.d.ts +1 -0
- package/dist/tests/layout/paged-body-margin.spec.js +92 -0
- package/dist/tests/layout/pseudo-counters-generated-content.spec.d.ts +1 -0
- package/dist/tests/layout/pseudo-counters-generated-content.spec.js +51 -0
- package/dist/tests/layout/responsive-clamp-grid-parity.spec.d.ts +1 -0
- package/dist/tests/layout/responsive-clamp-grid-parity.spec.js +75 -0
- package/dist/tests/layout/run-placer-baseline.spec.js +13 -11
- package/dist/tests/pdf/backdrop-filter-noop.spec.d.ts +1 -0
- package/dist/tests/pdf/backdrop-filter-noop.spec.js +140 -0
- package/dist/tests/pdf/filter-drop-shadow.spec.d.ts +1 -0
- package/dist/tests/pdf/filter-drop-shadow.spec.js +74 -0
- package/dist/tests/pdf/filter-opacity.spec.d.ts +1 -0
- package/dist/tests/pdf/filter-opacity.spec.js +30 -0
- package/dist/tests/pdf/font-subset-registry-key.spec.d.ts +1 -0
- package/dist/tests/pdf/font-subset-registry-key.spec.js +66 -0
- package/dist/tests/pdf/header-footer-clip-overflow.spec.d.ts +1 -0
- package/dist/tests/pdf/header-footer-clip-overflow.spec.js +45 -0
- package/dist/tests/pdf/selawik-opt-in.spec.d.ts +1 -0
- package/dist/tests/pdf/selawik-opt-in.spec.js +106 -0
- package/dist/tests/pdf/system-ui-fallback-subset-regression.spec.d.ts +1 -0
- package/dist/tests/pdf/system-ui-fallback-subset-regression.spec.js +39 -0
- package/dist/tests/pdf/text-renderer-fallback.spec.js +55 -0
- package/dist/tests/pdf/text-transform-matrix.spec.js +8 -7
- package/package.json +2 -2
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { parseFilterList } from "../../src/css/parsers/filter-parser.js";
|
|
3
|
+
describe("parseFilterList", () => {
|
|
4
|
+
// Keywords globais
|
|
5
|
+
it("returns [] for 'none'", () => {
|
|
6
|
+
expect(parseFilterList("none")).toEqual([]);
|
|
7
|
+
});
|
|
8
|
+
it("returns [] for 'initial'", () => {
|
|
9
|
+
expect(parseFilterList("initial")).toEqual([]);
|
|
10
|
+
});
|
|
11
|
+
it("returns undefined for 'inherit'", () => {
|
|
12
|
+
expect(parseFilterList("inherit")).toBeUndefined();
|
|
13
|
+
});
|
|
14
|
+
it("returns undefined for 'revert'", () => {
|
|
15
|
+
expect(parseFilterList("revert")).toBeUndefined();
|
|
16
|
+
});
|
|
17
|
+
it("returns undefined for empty string", () => {
|
|
18
|
+
expect(parseFilterList("")).toBeUndefined();
|
|
19
|
+
});
|
|
20
|
+
// Funções individuais
|
|
21
|
+
it("parses blur(5px)", () => {
|
|
22
|
+
const result = parseFilterList("blur(5px)");
|
|
23
|
+
expect(result).toEqual([{ kind: "blur", value: 5 }]);
|
|
24
|
+
});
|
|
25
|
+
it("parses blur() with no args as blur(0px)", () => {
|
|
26
|
+
const result = parseFilterList("blur()");
|
|
27
|
+
expect(result).toEqual([{ kind: "blur", value: 0 }]);
|
|
28
|
+
});
|
|
29
|
+
it("parses brightness(1.5)", () => {
|
|
30
|
+
const result = parseFilterList("brightness(1.5)");
|
|
31
|
+
expect(result).toEqual([{ kind: "brightness", value: 1.5 }]);
|
|
32
|
+
});
|
|
33
|
+
it("parses brightness(150%)", () => {
|
|
34
|
+
const result = parseFilterList("brightness(150%)");
|
|
35
|
+
expect(result).toEqual([{ kind: "brightness", value: 1.5 }]);
|
|
36
|
+
});
|
|
37
|
+
it("parses opacity(50%)", () => {
|
|
38
|
+
const result = parseFilterList("opacity(50%)");
|
|
39
|
+
expect(result).toEqual([{ kind: "opacity", value: 0.5 }]);
|
|
40
|
+
});
|
|
41
|
+
it("clamps opacity to [0, 1]", () => {
|
|
42
|
+
const result = parseFilterList("opacity(200%)");
|
|
43
|
+
expect(result).toEqual([{ kind: "opacity", value: 1 }]);
|
|
44
|
+
});
|
|
45
|
+
it("clamps grayscale to [0, 1]", () => {
|
|
46
|
+
const result = parseFilterList("grayscale(150%)");
|
|
47
|
+
expect(result).toEqual([{ kind: "grayscale", value: 1 }]);
|
|
48
|
+
});
|
|
49
|
+
it("does not clamp brightness above 1", () => {
|
|
50
|
+
const result = parseFilterList("brightness(3)");
|
|
51
|
+
expect(result).toEqual([{ kind: "brightness", value: 3 }]);
|
|
52
|
+
});
|
|
53
|
+
// Ângulos
|
|
54
|
+
it("parses hue-rotate(90deg)", () => {
|
|
55
|
+
const result = parseFilterList("hue-rotate(90deg)");
|
|
56
|
+
expect(result).toEqual([{ kind: "hue-rotate", valueDeg: 90 }]);
|
|
57
|
+
});
|
|
58
|
+
it("parses hue-rotate(0.5turn)", () => {
|
|
59
|
+
const result = parseFilterList("hue-rotate(0.5turn)");
|
|
60
|
+
expect(result).toEqual([{ kind: "hue-rotate", valueDeg: 180 }]);
|
|
61
|
+
});
|
|
62
|
+
it("parses hue-rotate(3.14159rad)", () => {
|
|
63
|
+
const result = parseFilterList("hue-rotate(3.14159rad)");
|
|
64
|
+
expect(result[0].kind).toBe("hue-rotate");
|
|
65
|
+
expect(result[0].valueDeg).toBeCloseTo(180, 1);
|
|
66
|
+
});
|
|
67
|
+
it("parses hue-rotate with negative angle", () => {
|
|
68
|
+
const result = parseFilterList("hue-rotate(-45deg)");
|
|
69
|
+
expect(result).toEqual([{ kind: "hue-rotate", valueDeg: -45 }]);
|
|
70
|
+
});
|
|
71
|
+
// drop-shadow
|
|
72
|
+
it("parses drop-shadow(2px 4px 6px black)", () => {
|
|
73
|
+
const result = parseFilterList("drop-shadow(2px 4px 6px black)");
|
|
74
|
+
expect(result).toEqual([{
|
|
75
|
+
kind: "drop-shadow",
|
|
76
|
+
offsetX: 2,
|
|
77
|
+
offsetY: 4,
|
|
78
|
+
blurRadius: 6,
|
|
79
|
+
color: "black",
|
|
80
|
+
}]);
|
|
81
|
+
});
|
|
82
|
+
it("parses drop-shadow with only offsets", () => {
|
|
83
|
+
const result = parseFilterList("drop-shadow(2px 4px)");
|
|
84
|
+
expect(result).toEqual([{
|
|
85
|
+
kind: "drop-shadow",
|
|
86
|
+
offsetX: 2,
|
|
87
|
+
offsetY: 4,
|
|
88
|
+
blurRadius: 0,
|
|
89
|
+
color: undefined,
|
|
90
|
+
}]);
|
|
91
|
+
});
|
|
92
|
+
it("parses drop-shadow with rgb() color", () => {
|
|
93
|
+
const result = parseFilterList("drop-shadow(1px 1px 3px rgb(255, 0, 0))");
|
|
94
|
+
expect(result[0].kind).toBe("drop-shadow");
|
|
95
|
+
expect(result[0].color).toBe("rgb(255, 0, 0)");
|
|
96
|
+
});
|
|
97
|
+
// Múltiplas funções
|
|
98
|
+
it("parses multiple functions separated by space", () => {
|
|
99
|
+
const result = parseFilterList("blur(2px) opacity(0.5) brightness(1.2)");
|
|
100
|
+
expect(result).toHaveLength(3);
|
|
101
|
+
expect(result[0]).toEqual({ kind: "blur", value: 2 });
|
|
102
|
+
expect(result[1]).toEqual({ kind: "opacity", value: 0.5 });
|
|
103
|
+
expect(result[2]).toEqual({ kind: "brightness", value: 1.2 });
|
|
104
|
+
});
|
|
105
|
+
// Valores inválidos
|
|
106
|
+
it("returns undefined for unknown function", () => {
|
|
107
|
+
const result = parseFilterList("foo(42)");
|
|
108
|
+
expect(result).toBeUndefined();
|
|
109
|
+
});
|
|
110
|
+
it("drops invalid values but keeps valid ones", () => {
|
|
111
|
+
const result = parseFilterList("blur(5px) unknown(42) opacity(0.8)");
|
|
112
|
+
expect(result).toHaveLength(2);
|
|
113
|
+
expect(result[0].kind).toBe("blur");
|
|
114
|
+
expect(result[1].kind).toBe("opacity");
|
|
115
|
+
});
|
|
116
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { registerAllPropertyParsers } from "../../src/css/parsers/register-parsers.js";
|
|
2
|
+
import { computeStyleForElement } from "../../src/css/compute-style.js";
|
|
3
|
+
import { ComputedStyle } from "../../src/css/style.js";
|
|
4
|
+
import { makeUnitParsers } from "../../src/units/units.js";
|
|
5
|
+
function makeElement(inlineStyle) {
|
|
6
|
+
return {
|
|
7
|
+
nodeType: 1,
|
|
8
|
+
nodeName: "DIV",
|
|
9
|
+
tagName: "div",
|
|
10
|
+
getAttribute(name) {
|
|
11
|
+
if (name === "style") {
|
|
12
|
+
return inlineStyle;
|
|
13
|
+
}
|
|
14
|
+
return null;
|
|
15
|
+
},
|
|
16
|
+
hasAttribute(name) {
|
|
17
|
+
return name === "style";
|
|
18
|
+
},
|
|
19
|
+
querySelectorAll(_selectors) {
|
|
20
|
+
return [];
|
|
21
|
+
},
|
|
22
|
+
parentElement: null,
|
|
23
|
+
firstElementChild: null,
|
|
24
|
+
lastElementChild: null,
|
|
25
|
+
nextElementSibling: null,
|
|
26
|
+
previousElementSibling: null,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
describe("flex shorthand parsing", () => {
|
|
30
|
+
beforeAll(() => {
|
|
31
|
+
registerAllPropertyParsers();
|
|
32
|
+
});
|
|
33
|
+
it("parses flex: 1 1 200px", () => {
|
|
34
|
+
const style = computeStyleForElement(makeElement("display:flex;flex:1 1 200px;"), [], new ComputedStyle(), makeUnitParsers({ viewport: { width: 800, height: 600 } }), 16);
|
|
35
|
+
expect(style.flexGrow).toBe(1);
|
|
36
|
+
expect(style.flexShrink).toBe(1);
|
|
37
|
+
expect(style.flexBasis).toBe(200);
|
|
38
|
+
});
|
|
39
|
+
it("parses single-value numeric flex shorthand", () => {
|
|
40
|
+
const style = computeStyleForElement(makeElement("display:flex;flex:2;"), [], new ComputedStyle(), makeUnitParsers({ viewport: { width: 800, height: 600 } }), 16);
|
|
41
|
+
expect(style.flexGrow).toBe(2);
|
|
42
|
+
expect(style.flexShrink).toBe(1);
|
|
43
|
+
expect(style.flexBasis).toBe(0);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { registerAllPropertyParsers } from "../../src/css/parsers/register-parsers.js";
|
|
2
|
+
import { parseGap, parseGridTemplate } from "../../src/css/parsers/grid-parser.js";
|
|
3
|
+
import { setViewportSize } from "../../src/css/viewport.js";
|
|
4
|
+
import { computeStyleForElement } from "../../src/css/compute-style.js";
|
|
5
|
+
import { ComputedStyle } from "../../src/css/style.js";
|
|
6
|
+
import { makeUnitParsers } from "../../src/units/units.js";
|
|
7
|
+
function makeElement(inlineStyle) {
|
|
8
|
+
return {
|
|
9
|
+
nodeType: 1,
|
|
10
|
+
nodeName: "DIV",
|
|
11
|
+
tagName: "div",
|
|
12
|
+
getAttribute(name) {
|
|
13
|
+
if (name === "style") {
|
|
14
|
+
return inlineStyle;
|
|
15
|
+
}
|
|
16
|
+
return null;
|
|
17
|
+
},
|
|
18
|
+
hasAttribute(name) {
|
|
19
|
+
return name === "style";
|
|
20
|
+
},
|
|
21
|
+
querySelectorAll(_selectors) {
|
|
22
|
+
return [];
|
|
23
|
+
},
|
|
24
|
+
parentElement: null,
|
|
25
|
+
firstElementChild: null,
|
|
26
|
+
lastElementChild: null,
|
|
27
|
+
nextElementSibling: null,
|
|
28
|
+
previousElementSibling: null,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
describe("grid clamp parsing and resolution", () => {
|
|
32
|
+
beforeAll(() => {
|
|
33
|
+
registerAllPropertyParsers();
|
|
34
|
+
});
|
|
35
|
+
beforeEach(() => {
|
|
36
|
+
setViewportSize(800, 600);
|
|
37
|
+
});
|
|
38
|
+
it("parses clamp() track sizes in grid-template-columns", () => {
|
|
39
|
+
const parsed = parseGridTemplate("clamp(150px, 20vw, 300px) 1fr");
|
|
40
|
+
expect(parsed).toBeDefined();
|
|
41
|
+
expect(parsed).toHaveLength(2);
|
|
42
|
+
const first = parsed?.[0];
|
|
43
|
+
const second = parsed?.[1];
|
|
44
|
+
expect(first).toEqual({
|
|
45
|
+
kind: "clamp",
|
|
46
|
+
min: 150,
|
|
47
|
+
preferred: 160,
|
|
48
|
+
max: 300,
|
|
49
|
+
});
|
|
50
|
+
expect(second).toEqual({
|
|
51
|
+
kind: "flex",
|
|
52
|
+
flex: 1,
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
it("parses clamp() values in gap shorthand", () => {
|
|
56
|
+
const parsed = parseGap("clamp(10px, 5vw, 40px)");
|
|
57
|
+
expect(parsed).toBeDefined();
|
|
58
|
+
expect(parsed?.row).toEqual({
|
|
59
|
+
kind: "clamp",
|
|
60
|
+
min: 10,
|
|
61
|
+
preferred: 40,
|
|
62
|
+
max: 40,
|
|
63
|
+
});
|
|
64
|
+
expect(parsed?.column).toEqual({
|
|
65
|
+
kind: "clamp",
|
|
66
|
+
min: 10,
|
|
67
|
+
preferred: 40,
|
|
68
|
+
max: 40,
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
it("resolves clamp() gap values and preserves clamp tracks in computed style", () => {
|
|
72
|
+
const style = computeStyleForElement(makeElement("display:grid;grid-template-columns:clamp(150px,20vw,300px) 1fr;gap:clamp(10px,5vw,40px);"), [], new ComputedStyle(), makeUnitParsers({ viewport: { width: 800, height: 600 } }), 16);
|
|
73
|
+
expect(style.rowGap).toBeCloseTo(40, 3);
|
|
74
|
+
expect(style.columnGap).toBeCloseTo(40, 3);
|
|
75
|
+
expect(style.trackListColumns[0]).toEqual({
|
|
76
|
+
kind: "clamp",
|
|
77
|
+
min: 150,
|
|
78
|
+
preferred: 160,
|
|
79
|
+
max: 300,
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { buildCssRules } from "../../src/html/css/parse-css.js";
|
|
2
|
+
describe("parse-css pseudo-element rules", () => {
|
|
3
|
+
it("keeps terminal ::before selectors and matches the host element", () => {
|
|
4
|
+
const css = ".item::before { content: 'x'; color: red; }";
|
|
5
|
+
const parsed = buildCssRules(css);
|
|
6
|
+
expect(parsed.styleRules).toHaveLength(1);
|
|
7
|
+
const rule = parsed.styleRules[0];
|
|
8
|
+
expect(rule.pseudoElement).toBe("::before");
|
|
9
|
+
const fakeEl = {
|
|
10
|
+
tagName: "div",
|
|
11
|
+
id: "",
|
|
12
|
+
classList: {
|
|
13
|
+
contains: (cls) => cls === "item",
|
|
14
|
+
},
|
|
15
|
+
parentElement: null,
|
|
16
|
+
firstElementChild: null,
|
|
17
|
+
lastElementChild: null,
|
|
18
|
+
nextElementSibling: null,
|
|
19
|
+
previousElementSibling: null,
|
|
20
|
+
ownerDocument: undefined,
|
|
21
|
+
textContent: "",
|
|
22
|
+
getAttribute: (name) => (name === "class" ? "item" : null),
|
|
23
|
+
};
|
|
24
|
+
expect(rule.match(fakeEl)).toBe(true);
|
|
25
|
+
});
|
|
26
|
+
});
|
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
import type { RenderBox, Run } from "../../src/pdf/types.js";
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
declare const DEFAULT_TEST_MARGINS: {
|
|
3
|
+
top: number;
|
|
4
|
+
right: number;
|
|
5
|
+
bottom: number;
|
|
6
|
+
left: number;
|
|
7
|
+
};
|
|
8
|
+
export interface RenderHelperOptions {
|
|
9
|
+
viewportWidth?: number;
|
|
10
|
+
viewportHeight?: number;
|
|
11
|
+
pageWidth?: number;
|
|
12
|
+
pageHeight?: number;
|
|
13
|
+
margins?: Partial<typeof DEFAULT_TEST_MARGINS>;
|
|
14
|
+
pagedBodyMargin?: "auto" | "zero";
|
|
15
|
+
interBlockWhitespace?: "collapse" | "preserve";
|
|
16
|
+
}
|
|
17
|
+
export declare function renderRuns(html: string, css?: string, options?: RenderHelperOptions): Promise<Run[]>;
|
|
18
|
+
export declare function renderTreeForHtml(html: string, css?: string, options?: RenderHelperOptions): Promise<import("../../src/pdf/types.js").LayoutTree>;
|
|
4
19
|
export declare function collectRuns(box: RenderBox): Run[];
|
|
5
20
|
export declare function collectBoxes(box: RenderBox): RenderBox[];
|
|
21
|
+
export {};
|
|
@@ -1,27 +1,40 @@
|
|
|
1
1
|
import { prepareHtmlRender } from "../../src/html-to-pdf.js";
|
|
2
|
-
|
|
2
|
+
const DEFAULT_TEST_PAGE = {
|
|
3
|
+
viewportWidth: 794,
|
|
4
|
+
viewportHeight: 1123,
|
|
5
|
+
pageWidth: 794,
|
|
6
|
+
pageHeight: 1123,
|
|
7
|
+
};
|
|
8
|
+
// Keep legacy fixture behavior stable: these tests historically rendered with 48px
|
|
9
|
+
// effective margins because prepareHtmlRender ignored helper-provided margins.
|
|
10
|
+
const DEFAULT_TEST_MARGINS = { top: 48, right: 48, bottom: 48, left: 48 };
|
|
11
|
+
export async function renderRuns(html, css = "", options = {}) {
|
|
3
12
|
const wrappedHtml = /<html[\s>]/i.test(html) ? html : `<html><body>${html}</body></html>`;
|
|
4
13
|
const { renderTree } = await prepareHtmlRender({
|
|
5
14
|
html: wrappedHtml,
|
|
6
15
|
css,
|
|
7
|
-
viewportWidth:
|
|
8
|
-
viewportHeight:
|
|
9
|
-
pageWidth:
|
|
10
|
-
pageHeight:
|
|
11
|
-
margins:
|
|
16
|
+
viewportWidth: options.viewportWidth ?? DEFAULT_TEST_PAGE.viewportWidth,
|
|
17
|
+
viewportHeight: options.viewportHeight ?? DEFAULT_TEST_PAGE.viewportHeight,
|
|
18
|
+
pageWidth: options.pageWidth ?? DEFAULT_TEST_PAGE.pageWidth,
|
|
19
|
+
pageHeight: options.pageHeight ?? DEFAULT_TEST_PAGE.pageHeight,
|
|
20
|
+
margins: options.margins ?? DEFAULT_TEST_MARGINS,
|
|
21
|
+
pagedBodyMargin: options.pagedBodyMargin ?? "auto",
|
|
22
|
+
interBlockWhitespace: options.interBlockWhitespace ?? "collapse",
|
|
12
23
|
});
|
|
13
24
|
return collectRuns(renderTree.root);
|
|
14
25
|
}
|
|
15
|
-
export async function renderTreeForHtml(html, css = "") {
|
|
26
|
+
export async function renderTreeForHtml(html, css = "", options = {}) {
|
|
16
27
|
const wrappedHtml = /<html[\s>]/i.test(html) ? html : `<html><body>${html}</body></html>`;
|
|
17
28
|
const { renderTree } = await prepareHtmlRender({
|
|
18
29
|
html: wrappedHtml,
|
|
19
30
|
css,
|
|
20
|
-
viewportWidth:
|
|
21
|
-
viewportHeight:
|
|
22
|
-
pageWidth:
|
|
23
|
-
pageHeight:
|
|
24
|
-
margins:
|
|
31
|
+
viewportWidth: options.viewportWidth ?? DEFAULT_TEST_PAGE.viewportWidth,
|
|
32
|
+
viewportHeight: options.viewportHeight ?? DEFAULT_TEST_PAGE.viewportHeight,
|
|
33
|
+
pageWidth: options.pageWidth ?? DEFAULT_TEST_PAGE.pageWidth,
|
|
34
|
+
pageHeight: options.pageHeight ?? DEFAULT_TEST_PAGE.pageHeight,
|
|
35
|
+
margins: options.margins ?? DEFAULT_TEST_MARGINS,
|
|
36
|
+
pagedBodyMargin: options.pagedBodyMargin ?? "auto",
|
|
37
|
+
interBlockWhitespace: options.interBlockWhitespace ?? "collapse",
|
|
25
38
|
});
|
|
26
39
|
return renderTree;
|
|
27
40
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { parseHTML } from "linkedom";
|
|
2
|
+
import { ComputedStyle } from "../../src/css/style.js";
|
|
3
|
+
import { buildCssRules } from "../../src/html/css/parse-css.js";
|
|
4
|
+
import { synthesizePseudoElement } from "../../src/html/dom-converter/pseudo-elements.js";
|
|
5
|
+
import { makeUnitParsers } from "../../src/units/units.js";
|
|
6
|
+
function createTestContext() {
|
|
7
|
+
return {
|
|
8
|
+
resourceBaseDir: "",
|
|
9
|
+
assetRootDir: "",
|
|
10
|
+
units: makeUnitParsers({ viewport: { width: 800, height: 600 } }),
|
|
11
|
+
rootFontSize: 16,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
describe("dom-converter/pseudo-elements", () => {
|
|
15
|
+
it("synthesizes a pseudo element with generated text content", async () => {
|
|
16
|
+
const { document } = parseHTML(`<div id="host" data-label="Alpha"></div>`);
|
|
17
|
+
const element = document.querySelector("#host");
|
|
18
|
+
const cssRules = buildCssRules(`#host::before { content: "X"; }`).styleRules;
|
|
19
|
+
const result = await synthesizePseudoElement(element, "::before", cssRules, new ComputedStyle(), createTestContext(), null);
|
|
20
|
+
expect(result).not.toBeNull();
|
|
21
|
+
expect(result?.tagName).toBe("::before");
|
|
22
|
+
expect(result?.customData).toMatchObject({ pseudoType: "before" });
|
|
23
|
+
expect(result?.children).toHaveLength(1);
|
|
24
|
+
expect(result?.children[0]?.textContent).toBe("X");
|
|
25
|
+
});
|
|
26
|
+
it("returns null when the pseudo style has no content", async () => {
|
|
27
|
+
const { document } = parseHTML(`<div id="host"></div>`);
|
|
28
|
+
const element = document.querySelector("#host");
|
|
29
|
+
const cssRules = buildCssRules(`#host { color: red; }`).styleRules;
|
|
30
|
+
const result = await synthesizePseudoElement(element, "::after", cssRules, new ComputedStyle(), createTestContext(), null);
|
|
31
|
+
expect(result).toBeNull();
|
|
32
|
+
});
|
|
33
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { parseHTML } from "linkedom";
|
|
2
|
+
import { Display } from "../../src/css/enums.js";
|
|
3
|
+
import { ComputedStyle } from "../../src/css/style.js";
|
|
4
|
+
import { LayoutNode } from "../../src/dom/node.js";
|
|
5
|
+
import { convertTextDomNode, flushBufferedText } from "../../src/html/dom-converter/text.js";
|
|
6
|
+
describe("dom-converter/text whitespace handling", () => {
|
|
7
|
+
it("preserves a single collapsed space between meaningful siblings", () => {
|
|
8
|
+
const { document } = parseHTML(`<div><span>A</span> <span>B</span></div>`);
|
|
9
|
+
const root = document.querySelector("div");
|
|
10
|
+
const textNode = root?.childNodes[1] ?? null;
|
|
11
|
+
expect(textNode).toBeTruthy();
|
|
12
|
+
const result = convertTextDomNode(textNode, new ComputedStyle());
|
|
13
|
+
expect(result).not.toBeNull();
|
|
14
|
+
expect(result?.textContent).toBe(" ");
|
|
15
|
+
expect(result?.customData).toMatchObject({
|
|
16
|
+
preserveLeadingSpace: true,
|
|
17
|
+
preserveTrailingSpace: true,
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
it("drops whitespace-only text at the edge of an element", () => {
|
|
21
|
+
const { document } = parseHTML(`<div> <span>B</span></div>`);
|
|
22
|
+
const root = document.querySelector("div");
|
|
23
|
+
const textNode = root?.childNodes[0] ?? null;
|
|
24
|
+
const result = convertTextDomNode(textNode, new ComputedStyle());
|
|
25
|
+
expect(result).toBeNull();
|
|
26
|
+
});
|
|
27
|
+
it("removes leading collapsed whitespace at block edges while preserving interior separation", () => {
|
|
28
|
+
const { document } = parseHTML(`<div> hello <span>world</span> </div>`);
|
|
29
|
+
const root = document.querySelector("div");
|
|
30
|
+
const leadingTextNode = root?.childNodes[0] ?? null;
|
|
31
|
+
const trailingTextNode = root?.childNodes[2] ?? null;
|
|
32
|
+
const leading = convertTextDomNode(leadingTextNode, new ComputedStyle());
|
|
33
|
+
const trailing = convertTextDomNode(trailingTextNode, new ComputedStyle());
|
|
34
|
+
expect(leading?.textContent).toBe("hello ");
|
|
35
|
+
expect(trailing).toBeNull();
|
|
36
|
+
});
|
|
37
|
+
it("preserves space before trailing text content after an inline sibling", () => {
|
|
38
|
+
const { document } = parseHTML(`<div><span>A</span> tail </div>`);
|
|
39
|
+
const root = document.querySelector("div");
|
|
40
|
+
const trailingTextNode = root?.childNodes[1] ?? null;
|
|
41
|
+
const trailing = convertTextDomNode(trailingTextNode, new ComputedStyle());
|
|
42
|
+
expect(trailing?.textContent).toBe(" tail");
|
|
43
|
+
});
|
|
44
|
+
it("preserves buffered whitespace after inline content but drops it after block content", () => {
|
|
45
|
+
const inlineChildren = [new LayoutNode(new ComputedStyle({ display: Display.Inline }))];
|
|
46
|
+
const blockChildren = [new LayoutNode(new ComputedStyle({ display: Display.Block }))];
|
|
47
|
+
const style = new ComputedStyle();
|
|
48
|
+
flushBufferedText(inlineChildren, " ", style);
|
|
49
|
+
flushBufferedText(blockChildren, " ", style);
|
|
50
|
+
expect(inlineChildren.at(-1)?.textContent).toBe(" ");
|
|
51
|
+
expect(blockChildren).toHaveLength(1);
|
|
52
|
+
});
|
|
53
|
+
it("drops whitespace-only text between block siblings by default", () => {
|
|
54
|
+
const { document } = parseHTML(`<body><p>A</p>\n <p>B</p></body>`);
|
|
55
|
+
const body = document.querySelector("body");
|
|
56
|
+
const textNode = body?.childNodes[1] ?? null;
|
|
57
|
+
const result = convertTextDomNode(textNode, new ComputedStyle({ display: Display.Block }));
|
|
58
|
+
expect(result).toBeNull();
|
|
59
|
+
});
|
|
60
|
+
it("preserves whitespace-only text between block siblings when preserve mode is enabled", () => {
|
|
61
|
+
const { document } = parseHTML(`<body><p>A</p>\n <p>B</p></body>`);
|
|
62
|
+
const body = document.querySelector("body");
|
|
63
|
+
const textNode = body?.childNodes[1] ?? null;
|
|
64
|
+
const result = convertTextDomNode(textNode, new ComputedStyle({ display: Display.Block }), { interBlockWhitespace: "preserve" });
|
|
65
|
+
expect(result?.textContent).toBe(" ");
|
|
66
|
+
});
|
|
67
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
+
import { ImageService } from '../../src/image/image-service.js';
|
|
3
|
+
import { PngWasmLoader } from '../../src/image/png-wasm-loader.js';
|
|
4
|
+
import { NodeEnvironment } from '../../src/environment/node-environment.js';
|
|
5
|
+
describe('ImageService PNG Backend', () => {
|
|
6
|
+
beforeEach(() => {
|
|
7
|
+
ImageService['instance'] = undefined;
|
|
8
|
+
PngWasmLoader.reset();
|
|
9
|
+
});
|
|
10
|
+
it('should use JS backend when configured', async () => {
|
|
11
|
+
const env = new NodeEnvironment();
|
|
12
|
+
const service = ImageService.getInstance(env);
|
|
13
|
+
service.setPngBackendConfig({ pngBackend: 'js' });
|
|
14
|
+
expect(service.getPngBackendConfig()).toEqual({ pngBackend: 'js' });
|
|
15
|
+
});
|
|
16
|
+
it('should default to auto backend', async () => {
|
|
17
|
+
const env = new NodeEnvironment();
|
|
18
|
+
const service = ImageService.getInstance(env);
|
|
19
|
+
expect(service.getPngBackendConfig()).toEqual({ pngBackend: 'auto' });
|
|
20
|
+
});
|
|
21
|
+
it('should fallback to JS when WASM is not available', async () => {
|
|
22
|
+
const env = new NodeEnvironment();
|
|
23
|
+
const service = ImageService.getInstance(env);
|
|
24
|
+
service.setPngBackendConfig({ pngBackend: 'auto' });
|
|
25
|
+
const isWasmAvailable = await PngWasmLoader.isAvailable();
|
|
26
|
+
expect(isWasmAvailable).toBe(false);
|
|
27
|
+
});
|
|
28
|
+
it('should throw in strict mode when WASM unavailable', async () => {
|
|
29
|
+
const env = new NodeEnvironment();
|
|
30
|
+
const service = ImageService.getInstance(env);
|
|
31
|
+
service.setPngBackendConfig({ pngBackend: 'wasm', wasm: { strict: true } });
|
|
32
|
+
await expect(PngWasmLoader.getOrInit()).rejects.toThrow();
|
|
33
|
+
});
|
|
34
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { collectBoxes, renderTreeForHtml } from "../helpers/render-utils.js";
|
|
2
|
+
function findByTag(boxes, tagName) {
|
|
3
|
+
const found = boxes.find((b) => b.tagName === tagName);
|
|
4
|
+
if (!found) {
|
|
5
|
+
throw new Error(`Expected to find <${tagName}> in render tree`);
|
|
6
|
+
}
|
|
7
|
+
return found;
|
|
8
|
+
}
|
|
9
|
+
describe("layout box-sizing behavior", () => {
|
|
10
|
+
it("uses content-box as default and border-box when specified for width", async () => {
|
|
11
|
+
const html = `
|
|
12
|
+
<article style="width:200px;padding:20px;border:5px solid #000">A</article>
|
|
13
|
+
<aside style="width:200px;padding:20px;border:5px solid #000;box-sizing:border-box">B</aside>
|
|
14
|
+
`;
|
|
15
|
+
const tree = await renderTreeForHtml(html);
|
|
16
|
+
const boxes = collectBoxes(tree.root);
|
|
17
|
+
const contentBox = findByTag(boxes, "article");
|
|
18
|
+
const borderBox = findByTag(boxes, "aside");
|
|
19
|
+
expect(contentBox.contentBox.width).toBeCloseTo(200, 3);
|
|
20
|
+
expect(contentBox.borderBox.width).toBeCloseTo(250, 3);
|
|
21
|
+
expect(borderBox.contentBox.width).toBeCloseTo(150, 3);
|
|
22
|
+
expect(borderBox.borderBox.width).toBeCloseTo(200, 3);
|
|
23
|
+
});
|
|
24
|
+
it("applies max-width in border-box space when box-sizing is border-box", async () => {
|
|
25
|
+
const html = `
|
|
26
|
+
<section style="width:400px;max-width:220px;padding:10px;border:5px solid #000;box-sizing:border-box">X</section>
|
|
27
|
+
`;
|
|
28
|
+
const tree = await renderTreeForHtml(html);
|
|
29
|
+
const boxes = collectBoxes(tree.root);
|
|
30
|
+
const section = findByTag(boxes, "section");
|
|
31
|
+
expect(section.contentBox.width).toBeCloseTo(190, 3);
|
|
32
|
+
expect(section.borderBox.width).toBeCloseTo(220, 3);
|
|
33
|
+
});
|
|
34
|
+
it("applies explicit height in border-box space for block containers", async () => {
|
|
35
|
+
const html = `
|
|
36
|
+
<main style="height:200px;padding:20px;border:5px solid #000;box-sizing:border-box"></main>
|
|
37
|
+
`;
|
|
38
|
+
const tree = await renderTreeForHtml(html);
|
|
39
|
+
const boxes = collectBoxes(tree.root);
|
|
40
|
+
const main = findByTag(boxes, "main");
|
|
41
|
+
expect(main.contentBox.height).toBeCloseTo(150, 3);
|
|
42
|
+
expect(main.borderBox.height).toBeCloseTo(200, 3);
|
|
43
|
+
});
|
|
44
|
+
it("applies explicit height in border-box space for grid and table containers", async () => {
|
|
45
|
+
const html = `
|
|
46
|
+
<div style="display:grid;height:200px;padding:20px;border:5px solid #000;box-sizing:border-box"></div>
|
|
47
|
+
<table style="height:200px;padding:20px;border:5px solid #000;box-sizing:border-box">
|
|
48
|
+
<tr><td>cell</td></tr>
|
|
49
|
+
</table>
|
|
50
|
+
`;
|
|
51
|
+
const tree = await renderTreeForHtml(html);
|
|
52
|
+
const boxes = collectBoxes(tree.root);
|
|
53
|
+
const grid = boxes.find((b) => b.tagName === "div");
|
|
54
|
+
const table = findByTag(boxes, "table");
|
|
55
|
+
if (!grid) {
|
|
56
|
+
throw new Error("Expected to find grid container");
|
|
57
|
+
}
|
|
58
|
+
expect(grid.contentBox.height).toBeCloseTo(150, 3);
|
|
59
|
+
expect(grid.borderBox.height).toBeCloseTo(200, 3);
|
|
60
|
+
expect(table.contentBox.height).toBeCloseTo(150, 3);
|
|
61
|
+
expect(table.borderBox.height).toBeCloseTo(200, 3);
|
|
62
|
+
});
|
|
63
|
+
it("applies border-box sizing to form controls with explicit width and height", async () => {
|
|
64
|
+
const html = `
|
|
65
|
+
<input type="text" style="width:200px;height:60px;padding:10px;border:5px solid #000;box-sizing:border-box" value="ok" />
|
|
66
|
+
`;
|
|
67
|
+
const tree = await renderTreeForHtml(html);
|
|
68
|
+
const boxes = collectBoxes(tree.root);
|
|
69
|
+
const input = findByTag(boxes, "input");
|
|
70
|
+
expect(input.contentBox.width).toBeCloseTo(170, 3);
|
|
71
|
+
expect(input.borderBox.width).toBeCloseTo(200, 3);
|
|
72
|
+
expect(input.contentBox.height).toBeCloseTo(30, 3);
|
|
73
|
+
expect(input.borderBox.height).toBeCloseTo(60, 3);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { collectBoxes, renderTreeForHtml } from "../helpers/render-utils.js";
|
|
2
|
+
function findByTag(boxes, tagName) {
|
|
3
|
+
const found = boxes.find((b) => b.tagName === tagName);
|
|
4
|
+
if (!found) {
|
|
5
|
+
throw new Error(`Expected to find <${tagName}> in render tree`);
|
|
6
|
+
}
|
|
7
|
+
return found;
|
|
8
|
+
}
|
|
9
|
+
describe("calc() layout for padding", () => {
|
|
10
|
+
it("resolves calc(px + %) using containing block width", async () => {
|
|
11
|
+
const html = "<!DOCTYPE html><html><body style=\"margin:0;\"><main style=\"width:400px;padding:calc(10px + 2%);height:40px;background:#ddd;\">X</main></body></html>";
|
|
12
|
+
const tree = await renderTreeForHtml(html);
|
|
13
|
+
const boxes = collectBoxes(tree.root);
|
|
14
|
+
const main = findByTag(boxes, "main");
|
|
15
|
+
const expectedPadding = 10 + 0.02 * 400;
|
|
16
|
+
expect(main.contentBox.width).toBeCloseTo(400, 2);
|
|
17
|
+
expect(main.borderBox.width).toBeCloseTo(400 + expectedPadding * 2, 2);
|
|
18
|
+
});
|
|
19
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { collectBoxes, renderTreeForHtml } from "../helpers/render-utils.js";
|
|
2
|
+
function findByTag(boxes, tagName) {
|
|
3
|
+
const found = boxes.find((b) => b.tagName === tagName);
|
|
4
|
+
if (!found) {
|
|
5
|
+
throw new Error(`Expected to find <${tagName}> in render tree`);
|
|
6
|
+
}
|
|
7
|
+
return found;
|
|
8
|
+
}
|
|
9
|
+
describe("container query unit layout", () => {
|
|
10
|
+
it("resolves cqw/cqh/cqmin/cqmax against containing block dimensions", async () => {
|
|
11
|
+
const html = "<!DOCTYPE html><html><body style=\"margin:0;\"><section style=\"width:400px;height:300px;\"><div id=\"a\" style=\"width:50cqw;height:25cqh;\"></div><div id=\"b\" style=\"width:10cqmin;height:10cqmax;\"></div></section></body></html>";
|
|
12
|
+
const tree = await renderTreeForHtml(html);
|
|
13
|
+
const boxes = collectBoxes(tree.root).filter((box) => box.tagName === "div");
|
|
14
|
+
if (boxes.length !== 2) {
|
|
15
|
+
throw new Error(`Expected exactly 2 divs, got ${boxes.length}`);
|
|
16
|
+
}
|
|
17
|
+
const first = boxes[0];
|
|
18
|
+
const second = boxes[1];
|
|
19
|
+
expect(first.contentBox.width).toBeCloseTo(200, 2);
|
|
20
|
+
expect(first.contentBox.height).toBeCloseTo(75, 2);
|
|
21
|
+
expect(second.contentBox.width).toBeCloseTo(30, 2);
|
|
22
|
+
expect(second.contentBox.height).toBeCloseTo(40, 2);
|
|
23
|
+
});
|
|
24
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|