pagyra-js 0.0.20 → 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 -417
- package/dist/src/html/image-converter.d.ts +5 -0
- 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-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 +11 -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/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/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
|
@@ -1,417 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import {} from "./css/parse-css.js";
|
|
3
|
-
import { LayoutNode } from "../dom/node.js";
|
|
4
|
-
import { ComputedStyle } from "../css/style.js";
|
|
5
|
-
import { cloneLineHeight } from "../css/line-height.js";
|
|
6
|
-
import { computeStyleForElement } from "../css/compute-style.js";
|
|
7
|
-
import { convertImageElement, resolveImageSource, canLoadHttpResource } from "./image-converter.js";
|
|
8
|
-
import { Display, WhiteSpace } from "../css/enums.js";
|
|
9
|
-
import { parseSvg } from "../svg/parser.js";
|
|
10
|
-
import { ImageService } from "../image/image-service.js";
|
|
11
|
-
import { log } from "../logging/debug.js";
|
|
12
|
-
import { decodeBase64ToUint8Array } from "../utils/base64.js";
|
|
13
|
-
import { defaultFormRegistry, extractFormControlData } from "../dom/form-registry.js";
|
|
14
|
-
function findMeaningfulSibling(start, direction) {
|
|
15
|
-
let current = start;
|
|
16
|
-
const getNext = direction === "previous"
|
|
17
|
-
? (node) => node.previousSibling
|
|
18
|
-
: (node) => node.nextSibling;
|
|
19
|
-
while (current) {
|
|
20
|
-
if (current.nodeType === current.TEXT_NODE) {
|
|
21
|
-
const content = current.textContent ?? "";
|
|
22
|
-
if (content.replace(/\s+/g, "").length > 0) {
|
|
23
|
-
return current;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
else if (current.nodeType === current.ELEMENT_NODE) {
|
|
27
|
-
const tagName = current.tagName.toLowerCase();
|
|
28
|
-
if (!["script", "style", "meta", "link"].includes(tagName)) {
|
|
29
|
-
return current;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
current = getNext(current);
|
|
33
|
-
}
|
|
34
|
-
return null;
|
|
35
|
-
}
|
|
36
|
-
function hasMeaningfulPreviousSibling(node) {
|
|
37
|
-
return findMeaningfulSibling(node.previousSibling, "previous") !== null;
|
|
38
|
-
}
|
|
39
|
-
function hasMeaningfulNextSibling(node) {
|
|
40
|
-
return findMeaningfulSibling(node.nextSibling, "next") !== null;
|
|
41
|
-
}
|
|
42
|
-
function isInlineDisplay(display) {
|
|
43
|
-
return (display === Display.Inline ||
|
|
44
|
-
display === Display.InlineBlock ||
|
|
45
|
-
display === Display.InlineFlex ||
|
|
46
|
-
display === Display.InlineGrid ||
|
|
47
|
-
display === Display.InlineTable);
|
|
48
|
-
}
|
|
49
|
-
function shouldPreserveCollapsedWhitespace(children, style) {
|
|
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
|
-
function extractCssUrl(raw) {
|
|
57
|
-
const trimmed = raw.trim();
|
|
58
|
-
if (!trimmed) {
|
|
59
|
-
return "";
|
|
60
|
-
}
|
|
61
|
-
if (trimmed.startsWith("url(") && trimmed.endsWith(")")) {
|
|
62
|
-
let inner = trimmed.slice(4, -1).trim();
|
|
63
|
-
if ((inner.startsWith("'") && inner.endsWith("'")) ||
|
|
64
|
-
(inner.startsWith("\"") && inner.endsWith("\""))) {
|
|
65
|
-
inner = inner.slice(1, -1);
|
|
66
|
-
}
|
|
67
|
-
return inner;
|
|
68
|
-
}
|
|
69
|
-
if ((trimmed.startsWith("'") && trimmed.endsWith("'")) ||
|
|
70
|
-
(trimmed.startsWith("\"") && trimmed.endsWith("\""))) {
|
|
71
|
-
return trimmed.slice(1, -1);
|
|
72
|
-
}
|
|
73
|
-
return trimmed;
|
|
74
|
-
}
|
|
75
|
-
function isDataUri(value) {
|
|
76
|
-
return /^data:/i.test(value);
|
|
77
|
-
}
|
|
78
|
-
function isHttpUrl(value) {
|
|
79
|
-
return /^https?:\/\//i.test(value);
|
|
80
|
-
}
|
|
81
|
-
function parseSpan(raw) {
|
|
82
|
-
if (!raw)
|
|
83
|
-
return undefined;
|
|
84
|
-
const parsed = Number.parseInt(raw, 10);
|
|
85
|
-
return Number.isFinite(parsed) && parsed > 0 ? parsed : undefined;
|
|
86
|
-
}
|
|
87
|
-
async function loadBackgroundImage(cssUrl, context) {
|
|
88
|
-
const imageService = ImageService.getInstance(context.environment);
|
|
89
|
-
const resolvedSrc = resolveImageSource(cssUrl, context);
|
|
90
|
-
if (isHttpUrl(resolvedSrc) && !canLoadHttpResource(resolvedSrc, context)) {
|
|
91
|
-
log('dom-converter', 'warn', `Skipping remote background image (${resolvedSrc}); remote assets are not supported.`);
|
|
92
|
-
return null;
|
|
93
|
-
}
|
|
94
|
-
try {
|
|
95
|
-
let imageInfo;
|
|
96
|
-
if (isDataUri(resolvedSrc)) {
|
|
97
|
-
const comma = resolvedSrc.indexOf(",");
|
|
98
|
-
if (comma < 0) {
|
|
99
|
-
log('dom-converter', 'warn', `Unsupported data URI format for background image: ${cssUrl}`);
|
|
100
|
-
return null;
|
|
101
|
-
}
|
|
102
|
-
const meta = resolvedSrc.substring(5, comma);
|
|
103
|
-
const isBase64 = meta.endsWith(";base64");
|
|
104
|
-
const payload = resolvedSrc.substring(comma + 1);
|
|
105
|
-
const bytes = isBase64
|
|
106
|
-
? decodeBase64ToUint8Array(payload)
|
|
107
|
-
: new TextEncoder().encode(decodeURIComponent(payload));
|
|
108
|
-
const copy = bytes.slice();
|
|
109
|
-
imageInfo = await imageService.decodeImage(copy.buffer);
|
|
110
|
-
}
|
|
111
|
-
else {
|
|
112
|
-
imageInfo = await imageService.loadImage(resolvedSrc);
|
|
113
|
-
}
|
|
114
|
-
return { info: imageInfo, resolvedSrc };
|
|
115
|
-
}
|
|
116
|
-
catch (error) {
|
|
117
|
-
log('dom-converter', 'warn', `Failed to load background image ${cssUrl}:`, error instanceof Error ? error.message : String(error));
|
|
118
|
-
return null;
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
async function hydrateBackgroundImages(style, context) {
|
|
122
|
-
if (!style.backgroundLayers || style.backgroundLayers.length === 0) {
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
for (const layer of style.backgroundLayers) {
|
|
126
|
-
if (layer.kind !== "image") {
|
|
127
|
-
continue;
|
|
128
|
-
}
|
|
129
|
-
if (layer.imageInfo) {
|
|
130
|
-
continue;
|
|
131
|
-
}
|
|
132
|
-
const cssUrl = extractCssUrl(layer.url);
|
|
133
|
-
if (!cssUrl) {
|
|
134
|
-
continue;
|
|
135
|
-
}
|
|
136
|
-
const loaded = await loadBackgroundImage(cssUrl, context);
|
|
137
|
-
if (!loaded) {
|
|
138
|
-
continue;
|
|
139
|
-
}
|
|
140
|
-
layer.originalUrl = cssUrl;
|
|
141
|
-
layer.resolvedUrl = loaded.resolvedSrc;
|
|
142
|
-
layer.imageInfo = loaded.info;
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
export async function convertDomNode(node, cssRules, parentStyle, context) {
|
|
146
|
-
const extendedNode = node;
|
|
147
|
-
log('dom-converter', 'debug', `convertDomNode - entering function for node type: ${node.nodeType}, tagName: ${extendedNode.tagName || 'text node'}`);
|
|
148
|
-
if (node.nodeType === node.TEXT_NODE) {
|
|
149
|
-
const raw = node.textContent ?? "";
|
|
150
|
-
const collapsed = raw.replace(/\s+/g, " ").normalize("NFC");
|
|
151
|
-
const trimmed = collapsed.trim();
|
|
152
|
-
const hasPrev = hasMeaningfulPreviousSibling(node);
|
|
153
|
-
const hasNext = hasMeaningfulNextSibling(node);
|
|
154
|
-
if (trimmed.length === 0) {
|
|
155
|
-
const keepSpace = hasPrev && hasNext;
|
|
156
|
-
if (!keepSpace) {
|
|
157
|
-
return null;
|
|
158
|
-
}
|
|
159
|
-
log('dom-converter', 'debug', "convertDomNode - processing text node: (single space)");
|
|
160
|
-
const textStyle = new ComputedStyle({
|
|
161
|
-
display: Display.Inline,
|
|
162
|
-
color: parentStyle.color,
|
|
163
|
-
fontSize: parentStyle.fontSize,
|
|
164
|
-
lineHeight: cloneLineHeight(parentStyle.lineHeight),
|
|
165
|
-
fontFamily: parentStyle.fontFamily,
|
|
166
|
-
fontWeight: parentStyle.fontWeight,
|
|
167
|
-
fontStyle: parentStyle.fontStyle,
|
|
168
|
-
letterSpacing: parentStyle.letterSpacing,
|
|
169
|
-
wordSpacing: parentStyle.wordSpacing,
|
|
170
|
-
textDecorationLine: parentStyle.textDecorationLine,
|
|
171
|
-
textDecorationColor: parentStyle.textDecorationColor,
|
|
172
|
-
textDecorationStyle: parentStyle.textDecorationStyle,
|
|
173
|
-
textTransform: parentStyle.textTransform,
|
|
174
|
-
transform: parentStyle.transform,
|
|
175
|
-
textShadows: parentStyle.textShadows,
|
|
176
|
-
});
|
|
177
|
-
return new LayoutNode(textStyle, [], {
|
|
178
|
-
textContent: " ",
|
|
179
|
-
customData: {
|
|
180
|
-
preserveLeadingSpace: true,
|
|
181
|
-
preserveTrailingSpace: true,
|
|
182
|
-
},
|
|
183
|
-
});
|
|
184
|
-
}
|
|
185
|
-
let text = trimmed;
|
|
186
|
-
const preserveLeading = collapsed.startsWith(" ") && hasPrev;
|
|
187
|
-
const preserveTrailing = collapsed.endsWith(" ") && hasNext;
|
|
188
|
-
if (preserveLeading) {
|
|
189
|
-
text = " " + text;
|
|
190
|
-
}
|
|
191
|
-
if (preserveTrailing) {
|
|
192
|
-
text = text + " ";
|
|
193
|
-
}
|
|
194
|
-
log('dom-converter', 'debug', "convertDomNode - processing text node:", text.substring(0, 50) + (text.length > 50 ? '...' : ''));
|
|
195
|
-
const textStyle = new ComputedStyle({
|
|
196
|
-
display: Display.Inline,
|
|
197
|
-
color: parentStyle.color,
|
|
198
|
-
fontSize: parentStyle.fontSize,
|
|
199
|
-
lineHeight: cloneLineHeight(parentStyle.lineHeight),
|
|
200
|
-
fontFamily: parentStyle.fontFamily,
|
|
201
|
-
fontWeight: parentStyle.fontWeight,
|
|
202
|
-
fontStyle: parentStyle.fontStyle,
|
|
203
|
-
letterSpacing: parentStyle.letterSpacing,
|
|
204
|
-
wordSpacing: parentStyle.wordSpacing,
|
|
205
|
-
textDecorationLine: parentStyle.textDecorationLine,
|
|
206
|
-
textDecorationColor: parentStyle.textDecorationColor,
|
|
207
|
-
textDecorationStyle: parentStyle.textDecorationStyle,
|
|
208
|
-
textTransform: parentStyle.textTransform,
|
|
209
|
-
transform: parentStyle.transform,
|
|
210
|
-
textShadows: parentStyle.textShadows,
|
|
211
|
-
});
|
|
212
|
-
return new LayoutNode(textStyle, [], {
|
|
213
|
-
textContent: text,
|
|
214
|
-
customData: {
|
|
215
|
-
preserveLeadingSpace: preserveLeading,
|
|
216
|
-
preserveTrailingSpace: preserveTrailing,
|
|
217
|
-
},
|
|
218
|
-
});
|
|
219
|
-
}
|
|
220
|
-
if (node.nodeType !== node.ELEMENT_NODE)
|
|
221
|
-
return null;
|
|
222
|
-
const element = node;
|
|
223
|
-
const tagName = element.tagName.toLowerCase();
|
|
224
|
-
log('dom-converter', 'debug', `convertDomNode - processing element: ${tagName}, with style attr: ${element.getAttribute("style")}`);
|
|
225
|
-
if (tagName === "script" || tagName === "style")
|
|
226
|
-
return null;
|
|
227
|
-
// Handle image elements
|
|
228
|
-
if (tagName === "img") {
|
|
229
|
-
return await convertImageElement(element, cssRules, parentStyle, context);
|
|
230
|
-
}
|
|
231
|
-
if (tagName === "svg") {
|
|
232
|
-
const ownStyle = computeStyleForElement(element, cssRules, parentStyle, context.units, context.rootFontSize);
|
|
233
|
-
log('dom-converter', 'debug', "convertDomNode - computed style backgroundLayers:", ownStyle.backgroundLayers);
|
|
234
|
-
const svgRoot = parseSvg(element, { warn: (message) => log('svg-parser', 'warn', message) });
|
|
235
|
-
if (!svgRoot) {
|
|
236
|
-
return new LayoutNode(ownStyle, [], { tagName });
|
|
237
|
-
}
|
|
238
|
-
const intrinsic = resolveSvgIntrinsicSize(svgRoot, element);
|
|
239
|
-
return new LayoutNode(ownStyle, [], {
|
|
240
|
-
tagName,
|
|
241
|
-
intrinsicInlineSize: intrinsic.width,
|
|
242
|
-
intrinsicBlockSize: intrinsic.height,
|
|
243
|
-
customData: {
|
|
244
|
-
svg: {
|
|
245
|
-
root: svgRoot,
|
|
246
|
-
intrinsicWidth: intrinsic.width,
|
|
247
|
-
intrinsicHeight: intrinsic.height,
|
|
248
|
-
resourceBaseDir: context?.resourceBaseDir,
|
|
249
|
-
assetRootDir: context?.assetRootDir,
|
|
250
|
-
},
|
|
251
|
-
},
|
|
252
|
-
});
|
|
253
|
-
}
|
|
254
|
-
if (tagName === "br") {
|
|
255
|
-
const textStyle = new ComputedStyle({
|
|
256
|
-
display: Display.Inline,
|
|
257
|
-
color: parentStyle.color,
|
|
258
|
-
fontSize: parentStyle.fontSize,
|
|
259
|
-
lineHeight: cloneLineHeight(parentStyle.lineHeight),
|
|
260
|
-
fontFamily: parentStyle.fontFamily,
|
|
261
|
-
fontWeight: parentStyle.fontWeight,
|
|
262
|
-
fontStyle: parentStyle.fontStyle,
|
|
263
|
-
textTransform: parentStyle.textTransform,
|
|
264
|
-
});
|
|
265
|
-
return new LayoutNode(textStyle, [], { textContent: "\n" });
|
|
266
|
-
}
|
|
267
|
-
if (defaultFormRegistry.isFormElement(tagName)) {
|
|
268
|
-
const formControlData = extractFormControlData(element, tagName);
|
|
269
|
-
if (formControlData) {
|
|
270
|
-
const ownStyle = computeStyleForElement(element, cssRules, parentStyle, context.units, context.rootFontSize);
|
|
271
|
-
await hydrateBackgroundImages(ownStyle, context);
|
|
272
|
-
const options = { tagName };
|
|
273
|
-
const id = element.getAttribute("id");
|
|
274
|
-
if (id) {
|
|
275
|
-
options.customData = { id, formControl: formControlData };
|
|
276
|
-
}
|
|
277
|
-
else {
|
|
278
|
-
options.customData = { formControl: formControlData };
|
|
279
|
-
}
|
|
280
|
-
return new LayoutNode(ownStyle, [], options);
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
// ✅ Coalescing de #text
|
|
284
|
-
const ownStyle = computeStyleForElement(element, cssRules, parentStyle, context.units, context.rootFontSize);
|
|
285
|
-
await hydrateBackgroundImages(ownStyle, context);
|
|
286
|
-
log('dom-converter', 'debug', "convertDomNode - computed style backgroundLayers:", ownStyle.backgroundLayers);
|
|
287
|
-
// Log if this is the div element that should have the gradient
|
|
288
|
-
if (element.tagName.toLowerCase() === 'div' && element.getAttribute("style")?.includes('linear-gradient')) {
|
|
289
|
-
log('dom-converter', 'debug', "Found div with gradient style!");
|
|
290
|
-
}
|
|
291
|
-
const layoutChildren = [];
|
|
292
|
-
let textBuf = "";
|
|
293
|
-
const childNodes = element.childNodes;
|
|
294
|
-
if (!childNodes) {
|
|
295
|
-
return new LayoutNode(ownStyle, [], { tagName });
|
|
296
|
-
}
|
|
297
|
-
for (const child of Array.from(childNodes)) {
|
|
298
|
-
if (child.nodeType === child.TEXT_NODE) {
|
|
299
|
-
textBuf += child.textContent ?? "";
|
|
300
|
-
continue;
|
|
301
|
-
}
|
|
302
|
-
if (textBuf) {
|
|
303
|
-
let normalized = textBuf.replace(/\s+/g, " ").normalize("NFC");
|
|
304
|
-
if (normalized.trim().length === 0) {
|
|
305
|
-
normalized = shouldPreserveCollapsedWhitespace(layoutChildren, ownStyle) ? " " : "";
|
|
306
|
-
}
|
|
307
|
-
if (normalized) {
|
|
308
|
-
const preserveLeading = normalized.startsWith(" ");
|
|
309
|
-
const preserveTrailing = normalized.endsWith(" ");
|
|
310
|
-
layoutChildren.push(new LayoutNode(new ComputedStyle({
|
|
311
|
-
display: Display.Inline,
|
|
312
|
-
color: ownStyle.color,
|
|
313
|
-
fontSize: ownStyle.fontSize,
|
|
314
|
-
lineHeight: cloneLineHeight(ownStyle.lineHeight),
|
|
315
|
-
fontFamily: ownStyle.fontFamily,
|
|
316
|
-
fontWeight: ownStyle.fontWeight,
|
|
317
|
-
fontStyle: ownStyle.fontStyle,
|
|
318
|
-
letterSpacing: ownStyle.letterSpacing,
|
|
319
|
-
wordSpacing: ownStyle.wordSpacing,
|
|
320
|
-
overflowWrap: ownStyle.overflowWrap,
|
|
321
|
-
whiteSpace: ownStyle.whiteSpace,
|
|
322
|
-
textDecorationLine: ownStyle.textDecorationLine,
|
|
323
|
-
textDecorationColor: ownStyle.textDecorationColor,
|
|
324
|
-
textDecorationStyle: ownStyle.textDecorationStyle,
|
|
325
|
-
textTransform: ownStyle.textTransform,
|
|
326
|
-
transform: ownStyle.transform,
|
|
327
|
-
textShadows: ownStyle.textShadows,
|
|
328
|
-
}), [], {
|
|
329
|
-
textContent: normalized,
|
|
330
|
-
customData: {
|
|
331
|
-
preserveLeadingSpace: preserveLeading,
|
|
332
|
-
preserveTrailingSpace: preserveTrailing,
|
|
333
|
-
},
|
|
334
|
-
}));
|
|
335
|
-
}
|
|
336
|
-
textBuf = "";
|
|
337
|
-
}
|
|
338
|
-
const sub = await convertDomNode(child, cssRules, ownStyle, context);
|
|
339
|
-
if (sub)
|
|
340
|
-
layoutChildren.push(sub);
|
|
341
|
-
}
|
|
342
|
-
if (textBuf) {
|
|
343
|
-
let normalized = textBuf.replace(/\s+/g, " ").normalize("NFC");
|
|
344
|
-
if (normalized.trim().length === 0) {
|
|
345
|
-
normalized = shouldPreserveCollapsedWhitespace(layoutChildren, ownStyle) ? " " : "";
|
|
346
|
-
}
|
|
347
|
-
if (normalized) {
|
|
348
|
-
const preserveLeading = normalized.startsWith(" ");
|
|
349
|
-
const preserveTrailing = normalized.endsWith(" ");
|
|
350
|
-
layoutChildren.push(new LayoutNode(new ComputedStyle({
|
|
351
|
-
display: Display.Inline,
|
|
352
|
-
color: ownStyle.color,
|
|
353
|
-
fontSize: ownStyle.fontSize,
|
|
354
|
-
lineHeight: cloneLineHeight(ownStyle.lineHeight),
|
|
355
|
-
fontFamily: ownStyle.fontFamily,
|
|
356
|
-
fontWeight: ownStyle.fontWeight,
|
|
357
|
-
fontStyle: ownStyle.fontStyle,
|
|
358
|
-
letterSpacing: ownStyle.letterSpacing,
|
|
359
|
-
wordSpacing: ownStyle.wordSpacing,
|
|
360
|
-
overflowWrap: ownStyle.overflowWrap,
|
|
361
|
-
whiteSpace: ownStyle.whiteSpace,
|
|
362
|
-
textDecorationLine: ownStyle.textDecorationLine,
|
|
363
|
-
textDecorationColor: ownStyle.textDecorationColor,
|
|
364
|
-
textDecorationStyle: ownStyle.textDecorationStyle,
|
|
365
|
-
textTransform: ownStyle.textTransform,
|
|
366
|
-
transform: ownStyle.transform,
|
|
367
|
-
textShadows: ownStyle.textShadows,
|
|
368
|
-
}), [], {
|
|
369
|
-
textContent: normalized,
|
|
370
|
-
customData: {
|
|
371
|
-
preserveLeadingSpace: preserveLeading,
|
|
372
|
-
preserveTrailingSpace: preserveTrailing,
|
|
373
|
-
},
|
|
374
|
-
}));
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
// Preserve the original HTML ID
|
|
378
|
-
const id = element.getAttribute("id");
|
|
379
|
-
const options = { tagName };
|
|
380
|
-
if (tagName === "td" || tagName === "th") {
|
|
381
|
-
options.tableColSpan = parseSpan(element.getAttribute("colspan")) ?? 1;
|
|
382
|
-
options.tableRowSpan = parseSpan(element.getAttribute("rowspan")) ?? 1;
|
|
383
|
-
}
|
|
384
|
-
if (id) {
|
|
385
|
-
options.customData = { ...options.customData, id };
|
|
386
|
-
}
|
|
387
|
-
return new LayoutNode(ownStyle, layoutChildren, options);
|
|
388
|
-
}
|
|
389
|
-
function resolveSvgIntrinsicSize(svg, element) {
|
|
390
|
-
let width = svg.width;
|
|
391
|
-
let height = svg.height;
|
|
392
|
-
if (svg.viewBox) {
|
|
393
|
-
if (!width || width <= 0) {
|
|
394
|
-
width = svg.viewBox.width;
|
|
395
|
-
}
|
|
396
|
-
if (!height || height <= 0) {
|
|
397
|
-
height = svg.viewBox.height;
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
if (!width || width <= 0) {
|
|
401
|
-
width = attributeToNumber(element.getAttribute("width")) ?? 100;
|
|
402
|
-
}
|
|
403
|
-
if (!height || height <= 0) {
|
|
404
|
-
height = attributeToNumber(element.getAttribute("height")) ?? width;
|
|
405
|
-
}
|
|
406
|
-
return {
|
|
407
|
-
width: Number.isFinite(width) && width > 0 ? width : 100,
|
|
408
|
-
height: Number.isFinite(height) && height > 0 ? height : 100,
|
|
409
|
-
};
|
|
410
|
-
}
|
|
411
|
-
function attributeToNumber(raw) {
|
|
412
|
-
if (!raw) {
|
|
413
|
-
return undefined;
|
|
414
|
-
}
|
|
415
|
-
const value = Number.parseFloat(raw);
|
|
416
|
-
return Number.isFinite(value) ? value : undefined;
|
|
417
|
-
}
|
|
1
|
+
export { convertDomNode } from "./dom-converter/index.js";
|
|
@@ -2,12 +2,17 @@ import { type DomEl, type CssRuleEntry } from "./css/parse-css.js";
|
|
|
2
2
|
import { LayoutNode } from "../dom/node.js";
|
|
3
3
|
import { ComputedStyle } from "../css/style.js";
|
|
4
4
|
import type { UnitParsers } from "../units/units.js";
|
|
5
|
+
import type { CounterContext } from "../layout/counter.js";
|
|
6
|
+
import type { InterBlockWhitespaceMode } from "../html-to-pdf/types.js";
|
|
5
7
|
export interface ConversionContext {
|
|
6
8
|
resourceBaseDir: string;
|
|
7
9
|
assetRootDir: string;
|
|
8
10
|
units: UnitParsers;
|
|
9
11
|
rootFontSize: number;
|
|
10
12
|
environment?: import("../environment/environment.js").Environment;
|
|
13
|
+
interBlockWhitespace?: InterBlockWhitespaceMode;
|
|
14
|
+
counterContext?: CounterContext;
|
|
15
|
+
rootCounterScopeId?: string | null;
|
|
11
16
|
}
|
|
12
17
|
export declare function resolveImageSource(src: string, context: ConversionContext): string;
|
|
13
18
|
export declare function canLoadHttpResource(url: string, context: ConversionContext): boolean;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Environment } from "../environment/environment.js";
|
|
2
|
+
export declare function parseInputDocument(htmlInput: string, normalizedHtml: string): Document;
|
|
3
|
+
interface CollectCssArtifactsOptions {
|
|
4
|
+
document: Document;
|
|
5
|
+
cssInput: string;
|
|
6
|
+
resourceBaseDir: string;
|
|
7
|
+
assetRootDir: string;
|
|
8
|
+
environment: Environment;
|
|
9
|
+
}
|
|
10
|
+
export declare function collectCssArtifacts(options: CollectCssArtifactsOptions): Promise<{
|
|
11
|
+
cssRules: import("../html/css/parse-css.js").CssRuleEntry[];
|
|
12
|
+
fontFaceRules: import("../html/css/parse-css.js").FontFaceRule[];
|
|
13
|
+
}>;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { log } from "../logging/debug.js";
|
|
2
|
+
import { parseCss } from "../html/css/parse-css.js";
|
|
3
|
+
import { loadStylesheetFromHref } from "./resource-loader.js";
|
|
4
|
+
import { needsReparse, parseDocument, wrapHtml } from "./html-parser.js";
|
|
5
|
+
export function parseInputDocument(htmlInput, normalizedHtml) {
|
|
6
|
+
log("html-to-pdf", "debug", `prepareHtmlRender - input html: ${htmlInput}`);
|
|
7
|
+
let document = parseDocument(normalizedHtml);
|
|
8
|
+
if (needsReparse(document)) {
|
|
9
|
+
document = parseDocument(wrapHtml(htmlInput));
|
|
10
|
+
}
|
|
11
|
+
if (!document) {
|
|
12
|
+
throw new Error("Failed to parse HTML into a document");
|
|
13
|
+
}
|
|
14
|
+
log("html-to-pdf", "debug", `prepareHtmlRender - parsed document body: ${document.body?.innerHTML || "no body"}`);
|
|
15
|
+
log("html-to-pdf", "debug", `prepareHtmlRender - document.documentElement tagName: ${document.documentElement?.tagName}`);
|
|
16
|
+
log("html-to-pdf", "debug", `prepareHtmlRender - document.documentElement innerHTML: ${document.documentElement?.innerHTML}`);
|
|
17
|
+
log("html-to-pdf", "debug", `prepareHtmlRender - document children count: ${document.childNodes.length}`);
|
|
18
|
+
for (let i = 0; i < document.childNodes.length; i++) {
|
|
19
|
+
const child = document.childNodes[i];
|
|
20
|
+
const tagName = child.nodeType === child.ELEMENT_NODE ? child.tagName : "text node";
|
|
21
|
+
log("html-to-pdf", "debug", `prepareHtmlRender - document child ${i}: ${child.nodeType}, ${tagName}`);
|
|
22
|
+
}
|
|
23
|
+
log("parse", "debug", "DOM parsed", { hasBody: !!document.body });
|
|
24
|
+
return document;
|
|
25
|
+
}
|
|
26
|
+
export async function collectCssArtifacts(options) {
|
|
27
|
+
let mergedCss = options.cssInput || "";
|
|
28
|
+
const styleTags = Array.from(options.document.querySelectorAll("style"));
|
|
29
|
+
for (const styleTag of styleTags) {
|
|
30
|
+
if (styleTag.textContent)
|
|
31
|
+
mergedCss += `\n${styleTag.textContent}`;
|
|
32
|
+
}
|
|
33
|
+
const linkTags = Array.from(options.document.querySelectorAll("link")).filter((link) => (link.getAttribute("rel") || "").toLowerCase() === "stylesheet");
|
|
34
|
+
for (const linkTag of linkTags) {
|
|
35
|
+
const href = linkTag.getAttribute("href");
|
|
36
|
+
if (!href)
|
|
37
|
+
continue;
|
|
38
|
+
const cssText = await loadStylesheetFromHref(href, options.resourceBaseDir, options.assetRootDir, options.environment);
|
|
39
|
+
if (cssText)
|
|
40
|
+
mergedCss += `\n${cssText}`;
|
|
41
|
+
}
|
|
42
|
+
const { styleRules: cssRules, fontFaceRules } = parseCss(mergedCss);
|
|
43
|
+
log("parse", "debug", "CSS rules", { count: cssRules.length, fontFaces: fontFaceRules.length });
|
|
44
|
+
return { cssRules, fontFaceRules };
|
|
45
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { FontConfig } from "../types/fonts.js";
|
|
2
|
+
import type { Environment } from "../environment/environment.js";
|
|
3
|
+
export declare function pickFontUrlFromSrc(src: string): string | null;
|
|
4
|
+
export declare function parseFontWeight(weightStr: string): number;
|
|
5
|
+
export declare function normalizeFontStyle(styleStr: string): "normal" | "italic";
|
|
6
|
+
interface ResourcePathContext {
|
|
7
|
+
resourceBaseDir: string;
|
|
8
|
+
assetRootDir: string;
|
|
9
|
+
environment: Environment;
|
|
10
|
+
}
|
|
11
|
+
interface ParsedFontFaceRule {
|
|
12
|
+
declarations: Record<string, string>;
|
|
13
|
+
}
|
|
14
|
+
export declare function appendFontFacesFromCssRules(fontFaceRules: ParsedFontFaceRule[], fontConfig: FontConfig | undefined, paths: ResourcePathContext): Promise<void>;
|
|
15
|
+
export declare function ensureFontFaceDataLoaded(fontConfig: FontConfig | undefined, paths: ResourcePathContext): Promise<void>;
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { loadFontData } from "./resource-loader.js";
|
|
2
|
+
export function pickFontUrlFromSrc(src) {
|
|
3
|
+
const urlRegex = /url\(\s*(['"]?)([^'")]+)\1\s*\)(?:\s*format\(\s*['"]?([^'")]+)['"]?\s*\))?/gi;
|
|
4
|
+
let fallback = null;
|
|
5
|
+
let preferred = null;
|
|
6
|
+
let match;
|
|
7
|
+
while ((match = urlRegex.exec(src)) !== null) {
|
|
8
|
+
const url = match[2];
|
|
9
|
+
const format = (match[3] || "").toLowerCase();
|
|
10
|
+
if (!fallback)
|
|
11
|
+
fallback = url;
|
|
12
|
+
if (format === "woff2") {
|
|
13
|
+
preferred = url;
|
|
14
|
+
break;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return preferred ?? fallback;
|
|
18
|
+
}
|
|
19
|
+
export function parseFontWeight(weightStr) {
|
|
20
|
+
const normalized = weightStr.trim().toLowerCase();
|
|
21
|
+
if (normalized === "bold")
|
|
22
|
+
return 700;
|
|
23
|
+
if (normalized === "normal")
|
|
24
|
+
return 400;
|
|
25
|
+
const parsed = Number.parseInt(normalized, 10);
|
|
26
|
+
return Number.isFinite(parsed) ? parsed : 400;
|
|
27
|
+
}
|
|
28
|
+
export function normalizeFontStyle(styleStr) {
|
|
29
|
+
const normalized = styleStr.trim().toLowerCase();
|
|
30
|
+
return normalized.includes("italic") || normalized.includes("oblique") ? "italic" : "normal";
|
|
31
|
+
}
|
|
32
|
+
export async function appendFontFacesFromCssRules(fontFaceRules, fontConfig, paths) {
|
|
33
|
+
if (!fontConfig) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
for (const fontFace of fontFaceRules) {
|
|
37
|
+
const fontFamily = fontFace.declarations["font-family"]?.replace(/['"]/g, "");
|
|
38
|
+
const src = fontFace.declarations["src"];
|
|
39
|
+
if (!fontFamily || !src) {
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
const targetUrl = pickFontUrlFromSrc(src);
|
|
43
|
+
if (!targetUrl) {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
const fontData = await loadFontData(targetUrl, paths.resourceBaseDir, paths.assetRootDir, paths.environment);
|
|
47
|
+
if (!fontData) {
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
const weightStr = fontFace.declarations["font-weight"] || "400";
|
|
51
|
+
const styleStr = fontFace.declarations["font-style"] || "normal";
|
|
52
|
+
fontConfig.fontFaceDefs.push({
|
|
53
|
+
name: fontFamily,
|
|
54
|
+
family: fontFamily,
|
|
55
|
+
src: targetUrl,
|
|
56
|
+
data: fontData,
|
|
57
|
+
weight: parseFontWeight(weightStr),
|
|
58
|
+
style: normalizeFontStyle(styleStr),
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
export async function ensureFontFaceDataLoaded(fontConfig, paths) {
|
|
63
|
+
if (!fontConfig) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
for (const face of fontConfig.fontFaceDefs) {
|
|
67
|
+
if (!face.data && face.src) {
|
|
68
|
+
const loaded = await loadFontData(face.src, paths.resourceBaseDir, paths.assetRootDir, paths.environment);
|
|
69
|
+
if (loaded) {
|
|
70
|
+
face.data = loaded;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { HeaderFooterHTML } from "../pdf/types.js";
|
|
2
|
+
import type { PageMarginsPx } from "../units/page-utils.js";
|
|
3
|
+
import type { Environment } from "../environment/environment.js";
|
|
4
|
+
interface ResolveHeaderFooterMaxHeightsOptions {
|
|
5
|
+
headerFooter?: Partial<HeaderFooterHTML>;
|
|
6
|
+
pageWidthPx: number;
|
|
7
|
+
pageHeightPx: number;
|
|
8
|
+
margins: PageMarginsPx;
|
|
9
|
+
resourceBaseDir: string;
|
|
10
|
+
assetRootDir: string;
|
|
11
|
+
environment: Environment;
|
|
12
|
+
}
|
|
13
|
+
export declare function resolveHeaderFooterMaxHeights(options: ResolveHeaderFooterMaxHeightsOptions): Promise<Partial<HeaderFooterHTML> | undefined>;
|
|
14
|
+
export {};
|