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,88 @@
|
|
|
1
|
+
import { ComputedStyle } from "../../css/style.js";
|
|
2
|
+
import { ImageService } from "../../image/image-service.js";
|
|
3
|
+
import { log } from "../../logging/debug.js";
|
|
4
|
+
import { decodeBase64ToUint8Array } from "../../utils/base64.js";
|
|
5
|
+
import { canLoadHttpResource, resolveImageSource } from "../image-converter.js";
|
|
6
|
+
function extractCssUrl(raw) {
|
|
7
|
+
const trimmed = raw.trim();
|
|
8
|
+
if (!trimmed) {
|
|
9
|
+
return "";
|
|
10
|
+
}
|
|
11
|
+
if (trimmed.startsWith("url(") && trimmed.endsWith(")")) {
|
|
12
|
+
let inner = trimmed.slice(4, -1).trim();
|
|
13
|
+
if ((inner.startsWith("'") && inner.endsWith("'")) ||
|
|
14
|
+
(inner.startsWith("\"") && inner.endsWith("\""))) {
|
|
15
|
+
inner = inner.slice(1, -1);
|
|
16
|
+
}
|
|
17
|
+
return inner;
|
|
18
|
+
}
|
|
19
|
+
if ((trimmed.startsWith("'") && trimmed.endsWith("'")) ||
|
|
20
|
+
(trimmed.startsWith("\"") && trimmed.endsWith("\""))) {
|
|
21
|
+
return trimmed.slice(1, -1);
|
|
22
|
+
}
|
|
23
|
+
return trimmed;
|
|
24
|
+
}
|
|
25
|
+
function isDataUri(value) {
|
|
26
|
+
return /^data:/i.test(value);
|
|
27
|
+
}
|
|
28
|
+
function isHttpUrl(value) {
|
|
29
|
+
return /^https?:\/\//i.test(value);
|
|
30
|
+
}
|
|
31
|
+
async function loadBackgroundImage(cssUrl, context) {
|
|
32
|
+
const imageService = ImageService.getInstance(context.environment);
|
|
33
|
+
const resolvedSrc = resolveImageSource(cssUrl, context);
|
|
34
|
+
if (isHttpUrl(resolvedSrc) && !canLoadHttpResource(resolvedSrc, context)) {
|
|
35
|
+
log("dom-converter", "warn", `Skipping remote background image (${resolvedSrc}); remote assets are not supported.`);
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
try {
|
|
39
|
+
let imageInfo;
|
|
40
|
+
if (isDataUri(resolvedSrc)) {
|
|
41
|
+
const comma = resolvedSrc.indexOf(",");
|
|
42
|
+
if (comma < 0) {
|
|
43
|
+
log("dom-converter", "warn", `Unsupported data URI format for background image: ${cssUrl}`);
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
const meta = resolvedSrc.substring(5, comma);
|
|
47
|
+
const isBase64 = meta.endsWith(";base64");
|
|
48
|
+
const payload = resolvedSrc.substring(comma + 1);
|
|
49
|
+
const bytes = isBase64
|
|
50
|
+
? decodeBase64ToUint8Array(payload)
|
|
51
|
+
: new TextEncoder().encode(decodeURIComponent(payload));
|
|
52
|
+
const copy = bytes.slice();
|
|
53
|
+
imageInfo = await imageService.decodeImage(copy.buffer);
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
imageInfo = await imageService.loadImage(resolvedSrc);
|
|
57
|
+
}
|
|
58
|
+
return { info: imageInfo, resolvedSrc };
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
log("dom-converter", "warn", `Failed to load background image ${cssUrl}:`, error instanceof Error ? error.message : String(error));
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
export async function hydrateBackgroundImages(style, context) {
|
|
66
|
+
if (!style.backgroundLayers || style.backgroundLayers.length === 0) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
for (const layer of style.backgroundLayers) {
|
|
70
|
+
if (layer.kind !== "image") {
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
if (layer.imageInfo) {
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
const cssUrl = extractCssUrl(layer.url);
|
|
77
|
+
if (!cssUrl) {
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
const loaded = await loadBackgroundImage(cssUrl, context);
|
|
81
|
+
if (!loaded) {
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
layer.originalUrl = cssUrl;
|
|
85
|
+
layer.resolvedUrl = loaded.resolvedSrc;
|
|
86
|
+
layer.imageInfo = loaded.info;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { ComputedStyle } from "../../css/style.js";
|
|
2
|
+
import { LayoutNode } from "../../dom/node.js";
|
|
3
|
+
import type { CssRuleEntry } from "../css/parse-css.js";
|
|
4
|
+
import type { ConversionContext } from "../image-converter.js";
|
|
5
|
+
export declare function convertDomNode(node: Node, cssRules: CssRuleEntry[], parentStyle: ComputedStyle, context: ConversionContext, parentCounterScopeId?: string | null): Promise<LayoutNode | null>;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { computeStyleForElement } from "../../css/compute-style.js";
|
|
2
|
+
import { ComputedStyle } from "../../css/style.js";
|
|
3
|
+
import { LayoutNode } from "../../dom/node.js";
|
|
4
|
+
import { log } from "../../logging/debug.js";
|
|
5
|
+
import { hydrateBackgroundImages } from "./background-images.js";
|
|
6
|
+
import { isIgnoredElementTag, tryHandleSpecialElement } from "./handlers/index.js";
|
|
7
|
+
import { parseSpan } from "./helpers.js";
|
|
8
|
+
import { registerCounterScopeForNode, synthesizePseudoElement } from "./pseudo-elements.js";
|
|
9
|
+
import { convertTextDomNode } from "./text.js";
|
|
10
|
+
export async function convertDomNode(node, cssRules, parentStyle, context, parentCounterScopeId = context.rootCounterScopeId ?? null) {
|
|
11
|
+
const extendedNode = node;
|
|
12
|
+
log("dom-converter", "debug", `convertDomNode - entering function for node type: ${node.nodeType}, tagName: ${extendedNode.tagName || "text node"}`);
|
|
13
|
+
if (node.nodeType === node.TEXT_NODE) {
|
|
14
|
+
const textNode = convertTextDomNode(node, parentStyle, {
|
|
15
|
+
interBlockWhitespace: context.interBlockWhitespace ?? "collapse",
|
|
16
|
+
});
|
|
17
|
+
if (!textNode) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
log("dom-converter", "debug", "convertDomNode - processing text node:", `${textNode.textContent?.substring(0, 50) ?? ""}${(textNode.textContent?.length ?? 0) > 50 ? "..." : ""}`);
|
|
21
|
+
return textNode;
|
|
22
|
+
}
|
|
23
|
+
if (node.nodeType !== node.ELEMENT_NODE)
|
|
24
|
+
return null;
|
|
25
|
+
const element = node;
|
|
26
|
+
const tagName = element.tagName.toLowerCase();
|
|
27
|
+
log("dom-converter", "debug", `convertDomNode - processing element: ${tagName}, with style attr: ${element.getAttribute("style")}`);
|
|
28
|
+
if (isIgnoredElementTag(tagName))
|
|
29
|
+
return null;
|
|
30
|
+
const specialElementNode = await tryHandleSpecialElement({
|
|
31
|
+
element,
|
|
32
|
+
tagName,
|
|
33
|
+
cssRules,
|
|
34
|
+
parentStyle,
|
|
35
|
+
context,
|
|
36
|
+
});
|
|
37
|
+
if (specialElementNode) {
|
|
38
|
+
return specialElementNode;
|
|
39
|
+
}
|
|
40
|
+
const ownStyle = computeStyleForElement(element, cssRules, parentStyle, context.units, context.rootFontSize);
|
|
41
|
+
await hydrateBackgroundImages(ownStyle, context);
|
|
42
|
+
log("dom-converter", "debug", "convertDomNode - computed style backgroundLayers:", ownStyle.backgroundLayers);
|
|
43
|
+
if (element.tagName.toLowerCase() === "div" && element.getAttribute("style")?.includes("linear-gradient")) {
|
|
44
|
+
log("dom-converter", "debug", "Found div with gradient style!");
|
|
45
|
+
}
|
|
46
|
+
const elementCounterScopeId = registerCounterScopeForNode(ownStyle, context, parentCounterScopeId) ?? undefined;
|
|
47
|
+
const currentScopeId = elementCounterScopeId ?? parentCounterScopeId;
|
|
48
|
+
const layoutChildren = [];
|
|
49
|
+
const beforePseudo = await synthesizePseudoElement(element, "::before", cssRules, ownStyle, context, currentScopeId);
|
|
50
|
+
if (beforePseudo) {
|
|
51
|
+
layoutChildren.push(beforePseudo);
|
|
52
|
+
}
|
|
53
|
+
const childNodes = element.childNodes;
|
|
54
|
+
if (!childNodes) {
|
|
55
|
+
return new LayoutNode(ownStyle, [], { tagName });
|
|
56
|
+
}
|
|
57
|
+
for (const child of Array.from(childNodes)) {
|
|
58
|
+
const sub = await convertDomNode(child, cssRules, ownStyle, context, currentScopeId);
|
|
59
|
+
if (sub) {
|
|
60
|
+
layoutChildren.push(sub);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
const afterPseudo = await synthesizePseudoElement(element, "::after", cssRules, ownStyle, context, currentScopeId);
|
|
64
|
+
if (afterPseudo) {
|
|
65
|
+
layoutChildren.push(afterPseudo);
|
|
66
|
+
}
|
|
67
|
+
const id = element.getAttribute("id");
|
|
68
|
+
const options = { tagName };
|
|
69
|
+
if (tagName === "td" || tagName === "th") {
|
|
70
|
+
options.tableColSpan = parseSpan(element.getAttribute("colspan")) ?? 1;
|
|
71
|
+
options.tableRowSpan = parseSpan(element.getAttribute("rowspan")) ?? 1;
|
|
72
|
+
}
|
|
73
|
+
if (id) {
|
|
74
|
+
options.customData = { ...options.customData, id };
|
|
75
|
+
}
|
|
76
|
+
const layoutNode = new LayoutNode(ownStyle, layoutChildren, options);
|
|
77
|
+
if (elementCounterScopeId) {
|
|
78
|
+
layoutNode.counterScopeId = elementCounterScopeId;
|
|
79
|
+
}
|
|
80
|
+
return layoutNode;
|
|
81
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Display } from "../../../css/enums.js";
|
|
2
|
+
import { cloneLineHeight } from "../../../css/line-height.js";
|
|
3
|
+
import { ComputedStyle } from "../../../css/style.js";
|
|
4
|
+
import { LayoutNode } from "../../../dom/node.js";
|
|
5
|
+
function createBreakNode(parentStyle) {
|
|
6
|
+
const textStyle = new ComputedStyle({
|
|
7
|
+
display: Display.Inline,
|
|
8
|
+
color: parentStyle.color,
|
|
9
|
+
fontSize: parentStyle.fontSize,
|
|
10
|
+
lineHeight: cloneLineHeight(parentStyle.lineHeight),
|
|
11
|
+
fontFamily: parentStyle.fontFamily,
|
|
12
|
+
fontWeight: parentStyle.fontWeight,
|
|
13
|
+
fontStyle: parentStyle.fontStyle,
|
|
14
|
+
textTransform: parentStyle.textTransform,
|
|
15
|
+
});
|
|
16
|
+
return new LayoutNode(textStyle, [], { textContent: "\n" });
|
|
17
|
+
}
|
|
18
|
+
export const brHandler = ({ parentStyle }) => {
|
|
19
|
+
return createBreakNode(parentStyle);
|
|
20
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { computeStyleForElement } from "../../../css/compute-style.js";
|
|
2
|
+
import { ComputedStyle } from "../../../css/style.js";
|
|
3
|
+
import { LayoutNode } from "../../../dom/node.js";
|
|
4
|
+
import { defaultFormRegistry, extractFormControlData } from "../../../dom/form-registry.js";
|
|
5
|
+
import { hydrateBackgroundImages } from "../background-images.js";
|
|
6
|
+
async function createFormControlNode(element, tagName, cssRules, parentStyle, context) {
|
|
7
|
+
if (!defaultFormRegistry.isFormElement(tagName)) {
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
const formControlData = extractFormControlData(element, tagName);
|
|
11
|
+
if (!formControlData) {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
const ownStyle = computeStyleForElement(element, cssRules, parentStyle, context.units, context.rootFontSize);
|
|
15
|
+
await hydrateBackgroundImages(ownStyle, context);
|
|
16
|
+
const options = { tagName };
|
|
17
|
+
const id = element.getAttribute("id");
|
|
18
|
+
if (id) {
|
|
19
|
+
options.customData = { id, formControl: formControlData };
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
options.customData = { formControl: formControlData };
|
|
23
|
+
}
|
|
24
|
+
return new LayoutNode(ownStyle, [], options);
|
|
25
|
+
}
|
|
26
|
+
export const formControlHandler = async ({ element, tagName, cssRules, parentStyle, context }) => {
|
|
27
|
+
return await createFormControlNode(element, tagName, cssRules, parentStyle, context);
|
|
28
|
+
};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { LayoutNode } from "../../../dom/node.js";
|
|
2
|
+
import type { SpecialElementHandlerArgs } from "./types.js";
|
|
3
|
+
export declare function isIgnoredElementTag(tagName: string): boolean;
|
|
4
|
+
export declare function tryHandleSpecialElement(args: SpecialElementHandlerArgs): Promise<LayoutNode | null>;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { brHandler } from "./br-handler.js";
|
|
2
|
+
import { formControlHandler } from "./form-control-handler.js";
|
|
3
|
+
import { imgHandler } from "./img-handler.js";
|
|
4
|
+
import { svgHandler } from "./svg-handler.js";
|
|
5
|
+
const exactTagHandlers = new Map([
|
|
6
|
+
["img", imgHandler],
|
|
7
|
+
["svg", svgHandler],
|
|
8
|
+
["br", brHandler],
|
|
9
|
+
]);
|
|
10
|
+
export function isIgnoredElementTag(tagName) {
|
|
11
|
+
return tagName === "script" || tagName === "style";
|
|
12
|
+
}
|
|
13
|
+
export async function tryHandleSpecialElement(args) {
|
|
14
|
+
const exactHandler = exactTagHandlers.get(args.tagName);
|
|
15
|
+
if (exactHandler) {
|
|
16
|
+
return await exactHandler(args);
|
|
17
|
+
}
|
|
18
|
+
return await formControlHandler(args);
|
|
19
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { computeStyleForElement } from "../../../css/compute-style.js";
|
|
2
|
+
import { ComputedStyle } from "../../../css/style.js";
|
|
3
|
+
import { LayoutNode } from "../../../dom/node.js";
|
|
4
|
+
import { log } from "../../../logging/debug.js";
|
|
5
|
+
import { parseSvg } from "../../../svg/parser.js";
|
|
6
|
+
import { resolveSvgIntrinsicSize } from "../helpers.js";
|
|
7
|
+
function createSvgNode(element, tagName, cssRules, parentStyle, context) {
|
|
8
|
+
const ownStyle = computeStyleForElement(element, cssRules, parentStyle, context.units, context.rootFontSize);
|
|
9
|
+
log("dom-converter", "debug", "convertDomNode - computed style backgroundLayers:", ownStyle.backgroundLayers);
|
|
10
|
+
const svgRoot = parseSvg(element, { warn: (message) => log("svg-parser", "warn", message) });
|
|
11
|
+
if (!svgRoot) {
|
|
12
|
+
return new LayoutNode(ownStyle, [], { tagName });
|
|
13
|
+
}
|
|
14
|
+
const intrinsic = resolveSvgIntrinsicSize(svgRoot, element);
|
|
15
|
+
return new LayoutNode(ownStyle, [], {
|
|
16
|
+
tagName,
|
|
17
|
+
intrinsicInlineSize: intrinsic.width,
|
|
18
|
+
intrinsicBlockSize: intrinsic.height,
|
|
19
|
+
customData: {
|
|
20
|
+
svg: {
|
|
21
|
+
root: svgRoot,
|
|
22
|
+
intrinsicWidth: intrinsic.width,
|
|
23
|
+
intrinsicHeight: intrinsic.height,
|
|
24
|
+
resourceBaseDir: context?.resourceBaseDir,
|
|
25
|
+
assetRootDir: context?.assetRootDir,
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
export const svgHandler = ({ element, tagName, cssRules, parentStyle, context }) => {
|
|
31
|
+
return createSvgNode(element, tagName, cssRules, parentStyle, context);
|
|
32
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { ComputedStyle } from "../../../css/style.js";
|
|
2
|
+
import { LayoutNode } from "../../../dom/node.js";
|
|
3
|
+
import type { CssRuleEntry, DomEl } from "../../css/parse-css.js";
|
|
4
|
+
import type { ConversionContext } from "../../image-converter.js";
|
|
5
|
+
export interface SpecialElementHandlerArgs {
|
|
6
|
+
element: DomEl;
|
|
7
|
+
tagName: string;
|
|
8
|
+
cssRules: CssRuleEntry[];
|
|
9
|
+
parentStyle: ComputedStyle;
|
|
10
|
+
context: ConversionContext;
|
|
11
|
+
}
|
|
12
|
+
export type SpecialElementHandler = (args: SpecialElementHandlerArgs) => Promise<LayoutNode | null> | LayoutNode | null;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { SvgRootNode } from "../../svg/types.js";
|
|
2
|
+
import type { SvgElement } from "../../types/core.js";
|
|
3
|
+
export declare function parseSpan(raw: string | null): number | undefined;
|
|
4
|
+
export declare function resolveSvgIntrinsicSize(svg: SvgRootNode, element: SvgElement): {
|
|
5
|
+
width: number;
|
|
6
|
+
height: number;
|
|
7
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export function parseSpan(raw) {
|
|
2
|
+
if (!raw)
|
|
3
|
+
return undefined;
|
|
4
|
+
const parsed = Number.parseInt(raw, 10);
|
|
5
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : undefined;
|
|
6
|
+
}
|
|
7
|
+
export function resolveSvgIntrinsicSize(svg, element) {
|
|
8
|
+
let width = svg.width;
|
|
9
|
+
let height = svg.height;
|
|
10
|
+
if (svg.viewBox) {
|
|
11
|
+
if (!width || width <= 0) {
|
|
12
|
+
width = svg.viewBox.width;
|
|
13
|
+
}
|
|
14
|
+
if (!height || height <= 0) {
|
|
15
|
+
height = svg.viewBox.height;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
if (!width || width <= 0) {
|
|
19
|
+
width = attributeToNumber(element.getAttribute("width")) ?? 100;
|
|
20
|
+
}
|
|
21
|
+
if (!height || height <= 0) {
|
|
22
|
+
height = attributeToNumber(element.getAttribute("height")) ?? width;
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
width: Number.isFinite(width) && width > 0 ? width : 100,
|
|
26
|
+
height: Number.isFinite(height) && height > 0 ? height : 100,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
function attributeToNumber(raw) {
|
|
30
|
+
if (!raw) {
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
const value = Number.parseFloat(raw);
|
|
34
|
+
return Number.isFinite(value) ? value : undefined;
|
|
35
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { convertDomNode } from "./convert-dom-node.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { convertDomNode } from "./convert-dom-node.js";
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ComputedStyle } from "../../css/style.js";
|
|
2
|
+
import { LayoutNode } from "../../dom/node.js";
|
|
3
|
+
import type { CssPseudoElement, CssRuleEntry, DomEl } from "../css/parse-css.js";
|
|
4
|
+
import type { ConversionContext } from "../image-converter.js";
|
|
5
|
+
export declare function registerCounterScopeForNode(style: ComputedStyle, context: ConversionContext, parentScopeId: string | null): string | undefined;
|
|
6
|
+
export declare function synthesizePseudoElement(element: DomEl, pseudoType: CssPseudoElement, cssRules: CssRuleEntry[], parentStyle: ComputedStyle, context: ConversionContext, parentCounterScopeId: string | null): Promise<LayoutNode | null>;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { evaluateContent } from "../../css/parsers/content-parser.js";
|
|
2
|
+
import { LayoutNode } from "../../dom/node.js";
|
|
3
|
+
import { applyCounterIncrements, applyCounterResets } from "../../layout/counter.js";
|
|
4
|
+
import { computeStyleForPseudoElement } from "../../css/compute-style.js";
|
|
5
|
+
import { hydrateBackgroundImages } from "./background-images.js";
|
|
6
|
+
import { createGeneratedTextNode } from "./text.js";
|
|
7
|
+
export function registerCounterScopeForNode(style, context, parentScopeId) {
|
|
8
|
+
if (!context.counterContext) {
|
|
9
|
+
return undefined;
|
|
10
|
+
}
|
|
11
|
+
const scopeId = context.counterContext.registerScope(parentScopeId);
|
|
12
|
+
if (style.counterReset && style.counterReset.length > 0) {
|
|
13
|
+
applyCounterResets(context.counterContext, style.counterReset, scopeId);
|
|
14
|
+
}
|
|
15
|
+
if (style.counterIncrement && style.counterIncrement.length > 0) {
|
|
16
|
+
applyCounterIncrements(context.counterContext, style.counterIncrement, scopeId);
|
|
17
|
+
}
|
|
18
|
+
return scopeId;
|
|
19
|
+
}
|
|
20
|
+
export async function synthesizePseudoElement(element, pseudoType, cssRules, parentStyle, context, parentCounterScopeId) {
|
|
21
|
+
const pseudoStyle = computeStyleForPseudoElement(element, cssRules, pseudoType, parentStyle, context.units, context.rootFontSize);
|
|
22
|
+
if (!pseudoStyle.content) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
await hydrateBackgroundImages(pseudoStyle, context);
|
|
26
|
+
const pseudoScopeId = registerCounterScopeForNode(pseudoStyle, context, parentCounterScopeId);
|
|
27
|
+
const effectiveScopeId = pseudoScopeId ?? parentCounterScopeId;
|
|
28
|
+
const generatedText = evaluateContent(pseudoStyle.content, {
|
|
29
|
+
getCounter: (name) => context.counterContext?.getCounter(name, effectiveScopeId) ?? 0,
|
|
30
|
+
getAttribute: (name) => element.getAttribute(name),
|
|
31
|
+
quoteDepth: 0,
|
|
32
|
+
});
|
|
33
|
+
const children = [];
|
|
34
|
+
const textNode = createGeneratedTextNode(generatedText, pseudoStyle);
|
|
35
|
+
if (textNode) {
|
|
36
|
+
children.push(textNode);
|
|
37
|
+
}
|
|
38
|
+
const pseudoNode = new LayoutNode(pseudoStyle, children, {
|
|
39
|
+
tagName: pseudoType,
|
|
40
|
+
customData: {
|
|
41
|
+
pseudoType: pseudoType === "::before" ? "before" : "after",
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
if (pseudoScopeId) {
|
|
45
|
+
pseudoNode.counterScopeId = pseudoScopeId;
|
|
46
|
+
}
|
|
47
|
+
return pseudoNode;
|
|
48
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { LayoutNode } from "../../dom/node.js";
|
|
2
|
+
import { ComputedStyle } from "../../css/style.js";
|
|
3
|
+
import { Display } from "../../css/enums.js";
|
|
4
|
+
import type { InterBlockWhitespaceMode } from "../../html-to-pdf/types.js";
|
|
5
|
+
interface TextConversionOptions {
|
|
6
|
+
interBlockWhitespace?: InterBlockWhitespaceMode;
|
|
7
|
+
}
|
|
8
|
+
export declare function isGridOrFlexContainer(display: Display): boolean;
|
|
9
|
+
export declare function shouldPreserveCollapsedWhitespace(children: LayoutNode[], style: ComputedStyle): boolean;
|
|
10
|
+
export declare function createInlineTextStyle(parentStyle: ComputedStyle): ComputedStyle;
|
|
11
|
+
export declare function createInlineTextLayoutNode(text: string, parentStyle: ComputedStyle, preserveLeadingSpace: boolean, preserveTrailingSpace: boolean): LayoutNode;
|
|
12
|
+
export declare function createGeneratedTextNode(text: string, parentStyle: ComputedStyle): LayoutNode | null;
|
|
13
|
+
export declare function convertTextDomNode(node: Node, parentStyle: ComputedStyle, options?: TextConversionOptions): LayoutNode | null;
|
|
14
|
+
export declare function flushBufferedText(layoutChildren: LayoutNode[], textBuffer: string, ownStyle: ComputedStyle): string;
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { LayoutNode } from "../../dom/node.js";
|
|
2
|
+
import { ComputedStyle } from "../../css/style.js";
|
|
3
|
+
import { cloneLineHeight } from "../../css/line-height.js";
|
|
4
|
+
import { Display, WhiteSpace } from "../../css/enums.js";
|
|
5
|
+
const BLOCK_TAGS = new Set([
|
|
6
|
+
"address", "article", "aside", "blockquote", "canvas", "dd", "div", "dl", "dt",
|
|
7
|
+
"fieldset", "figcaption", "figure", "footer", "form", "h1", "h2", "h3", "h4",
|
|
8
|
+
"h5", "h6", "header", "hr", "li", "main", "nav", "noscript", "ol", "p", "pre",
|
|
9
|
+
"section", "table", "tbody", "thead", "tfoot", "tr", "td", "th", "ul", "video",
|
|
10
|
+
]);
|
|
11
|
+
function findMeaningfulSibling(start, direction) {
|
|
12
|
+
let current = start;
|
|
13
|
+
const getNext = direction === "previous"
|
|
14
|
+
? (node) => node.previousSibling
|
|
15
|
+
: (node) => node.nextSibling;
|
|
16
|
+
while (current) {
|
|
17
|
+
if (current.nodeType === current.TEXT_NODE) {
|
|
18
|
+
const content = current.textContent ?? "";
|
|
19
|
+
if (content.replace(/\s+/g, "").length > 0) {
|
|
20
|
+
return current;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
else if (current.nodeType === current.ELEMENT_NODE) {
|
|
24
|
+
const tagName = current.tagName.toLowerCase();
|
|
25
|
+
if (!["script", "style", "meta", "link"].includes(tagName)) {
|
|
26
|
+
return current;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
current = getNext(current);
|
|
30
|
+
}
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
function isInlineDisplay(display) {
|
|
34
|
+
return (display === Display.Inline ||
|
|
35
|
+
display === Display.InlineBlock ||
|
|
36
|
+
display === Display.InlineFlex ||
|
|
37
|
+
display === Display.InlineGrid ||
|
|
38
|
+
display === Display.InlineTable);
|
|
39
|
+
}
|
|
40
|
+
export function isGridOrFlexContainer(display) {
|
|
41
|
+
return (display === Display.Grid ||
|
|
42
|
+
display === Display.InlineGrid ||
|
|
43
|
+
display === Display.Flex ||
|
|
44
|
+
display === Display.InlineFlex);
|
|
45
|
+
}
|
|
46
|
+
export function shouldPreserveCollapsedWhitespace(children, style) {
|
|
47
|
+
if (isGridOrFlexContainer(style.display)) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
if (style.whiteSpace === WhiteSpace.Pre || style.whiteSpace === WhiteSpace.PreWrap) {
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
const lastChild = children.length > 0 ? children[children.length - 1] : null;
|
|
54
|
+
return !!lastChild && isInlineDisplay(lastChild.style.display);
|
|
55
|
+
}
|
|
56
|
+
export function createInlineTextStyle(parentStyle) {
|
|
57
|
+
return new ComputedStyle({
|
|
58
|
+
display: Display.Inline,
|
|
59
|
+
color: parentStyle.color,
|
|
60
|
+
fontSize: parentStyle.fontSize,
|
|
61
|
+
lineHeight: cloneLineHeight(parentStyle.lineHeight),
|
|
62
|
+
fontFamily: parentStyle.fontFamily,
|
|
63
|
+
fontWeight: parentStyle.fontWeight,
|
|
64
|
+
fontStyle: parentStyle.fontStyle,
|
|
65
|
+
letterSpacing: parentStyle.letterSpacing,
|
|
66
|
+
wordSpacing: parentStyle.wordSpacing,
|
|
67
|
+
overflowWrap: parentStyle.overflowWrap,
|
|
68
|
+
wordBreak: parentStyle.wordBreak,
|
|
69
|
+
whiteSpace: parentStyle.whiteSpace,
|
|
70
|
+
textDecorationLine: parentStyle.textDecorationLine,
|
|
71
|
+
textDecorationColor: parentStyle.textDecorationColor,
|
|
72
|
+
textDecorationStyle: parentStyle.textDecorationStyle,
|
|
73
|
+
textTransform: parentStyle.textTransform,
|
|
74
|
+
transform: parentStyle.transform,
|
|
75
|
+
textShadows: parentStyle.textShadows,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
export function createInlineTextLayoutNode(text, parentStyle, preserveLeadingSpace, preserveTrailingSpace) {
|
|
79
|
+
return new LayoutNode(createInlineTextStyle(parentStyle), [], {
|
|
80
|
+
textContent: text,
|
|
81
|
+
customData: {
|
|
82
|
+
preserveLeadingSpace,
|
|
83
|
+
preserveTrailingSpace,
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
export function createGeneratedTextNode(text, parentStyle) {
|
|
88
|
+
if (text.length === 0) {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
return createInlineTextLayoutNode(text, parentStyle, true, true);
|
|
92
|
+
}
|
|
93
|
+
export function convertTextDomNode(node, parentStyle, options = {}) {
|
|
94
|
+
const raw = node.textContent ?? "";
|
|
95
|
+
const collapsed = raw.replace(/\s+/g, " ").normalize("NFC");
|
|
96
|
+
const trimmed = collapsed.trim();
|
|
97
|
+
const prev = findMeaningfulSibling(node.previousSibling, "previous");
|
|
98
|
+
const next = findMeaningfulSibling(node.nextSibling, "next");
|
|
99
|
+
const hasPrev = prev !== null;
|
|
100
|
+
const hasNext = next !== null;
|
|
101
|
+
const interBlockWhitespace = options.interBlockWhitespace ?? "collapse";
|
|
102
|
+
const preserveLeadingCandidate = collapsed.startsWith(" ") && hasPrev;
|
|
103
|
+
const preserveTrailingCandidate = collapsed.endsWith(" ") && hasNext;
|
|
104
|
+
const preserveLeading = preserveLeadingCandidate && shouldKeepBoundarySpace(prev, interBlockWhitespace, parentStyle);
|
|
105
|
+
const preserveTrailing = preserveTrailingCandidate && shouldKeepBoundarySpace(next, interBlockWhitespace, parentStyle);
|
|
106
|
+
if (trimmed.length === 0) {
|
|
107
|
+
if (isGridOrFlexContainer(parentStyle.display)) {
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
const keepSpace = hasPrev &&
|
|
111
|
+
hasNext &&
|
|
112
|
+
(interBlockWhitespace === "preserve" || shouldKeepInterSiblingWhitespace(prev, next, parentStyle));
|
|
113
|
+
if (!keepSpace) {
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
return createInlineTextLayoutNode(" ", parentStyle, true, true);
|
|
117
|
+
}
|
|
118
|
+
let text = trimmed;
|
|
119
|
+
if (preserveLeading) {
|
|
120
|
+
text = " " + text;
|
|
121
|
+
}
|
|
122
|
+
if (preserveTrailing) {
|
|
123
|
+
text = text + " ";
|
|
124
|
+
}
|
|
125
|
+
return createInlineTextLayoutNode(text, parentStyle, preserveLeading, preserveTrailing);
|
|
126
|
+
}
|
|
127
|
+
function shouldKeepBoundarySpace(sibling, mode, parentStyle) {
|
|
128
|
+
if (mode === "preserve") {
|
|
129
|
+
return true;
|
|
130
|
+
}
|
|
131
|
+
if (isInlineDisplay(parentStyle.display)) {
|
|
132
|
+
return true;
|
|
133
|
+
}
|
|
134
|
+
return isInlineLikeNode(sibling);
|
|
135
|
+
}
|
|
136
|
+
function shouldKeepInterSiblingWhitespace(prev, next, parentStyle) {
|
|
137
|
+
if (isInlineDisplay(parentStyle.display)) {
|
|
138
|
+
return true;
|
|
139
|
+
}
|
|
140
|
+
return isInlineLikeNode(prev) && isInlineLikeNode(next);
|
|
141
|
+
}
|
|
142
|
+
function isInlineLikeNode(node) {
|
|
143
|
+
if (!node) {
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
if (node.nodeType === node.TEXT_NODE) {
|
|
147
|
+
return true;
|
|
148
|
+
}
|
|
149
|
+
if (node.nodeType !== node.ELEMENT_NODE) {
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
const tagName = node.tagName.toLowerCase();
|
|
153
|
+
return !BLOCK_TAGS.has(tagName);
|
|
154
|
+
}
|
|
155
|
+
export function flushBufferedText(layoutChildren, textBuffer, ownStyle) {
|
|
156
|
+
if (!textBuffer) {
|
|
157
|
+
return "";
|
|
158
|
+
}
|
|
159
|
+
let normalized = textBuffer.replace(/\s+/g, " ").normalize("NFC");
|
|
160
|
+
if (normalized.trim().length === 0) {
|
|
161
|
+
normalized = shouldPreserveCollapsedWhitespace(layoutChildren, ownStyle) ? " " : "";
|
|
162
|
+
}
|
|
163
|
+
if (!normalized) {
|
|
164
|
+
return "";
|
|
165
|
+
}
|
|
166
|
+
const preserveLeading = normalized.startsWith(" ");
|
|
167
|
+
const preserveTrailing = normalized.endsWith(" ");
|
|
168
|
+
layoutChildren.push(createInlineTextLayoutNode(normalized, ownStyle, preserveLeading, preserveTrailing));
|
|
169
|
+
return "";
|
|
170
|
+
}
|
|
@@ -1,5 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { LayoutNode } from "../dom/node.js";
|
|
3
|
-
import { ComputedStyle } from "../css/style.js";
|
|
4
|
-
import { type ConversionContext } from "./image-converter.js";
|
|
5
|
-
export declare function convertDomNode(node: Node, cssRules: CssRuleEntry[], parentStyle: ComputedStyle, context: ConversionContext): Promise<LayoutNode | null>;
|
|
1
|
+
export { convertDomNode } from "./dom-converter/index.js";
|