From dd6b06a80a1947843fd9f4347668d043ef5b779f Mon Sep 17 00:00:00 2001 From: krflol Date: Wed, 14 Oct 2026 22:28:12 -0500 Subject: [PATCH 1/3] Prepare hidden-text and font-decoration engine update checks --- .github/workflows/ci.yml | 9 + docs/engine-2.5.24.md | 53 + test/engine-regressions.mjs | 8 +- test/fixtures/decoration-thickness.json | 398 ++++++ test/fixtures/hidden-text-2.5.24.json | 1608 +++++++++++++++++++++++ test/hidden-text.mjs | 8 + tools/browser-requirements.txt | 1 + tools/prepare-browser-check.mjs | 10 + tools/render-engine-regressions.mjs | 6 +- tools/render-hidden-text.mjs | 92 ++ tools/verify-engine-regressions.py | 72 +- tools/verify-hidden-text.py | 97 ++ tools/verify-native.mjs | 7 +- 13 files changed, 2355 insertions(+), 14 deletions(-) create mode 100644 docs/engine-2.5.24.md create mode 100644 test/fixtures/decoration-thickness.json create mode 100644 test/fixtures/hidden-text-2.5.24.json create mode 100644 test/hidden-text.mjs create mode 100644 tools/render-hidden-text.mjs create mode 100644 tools/verify-hidden-text.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 72cb898..62371f4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,6 +43,10 @@ jobs: run: | node tools/render-engine-regressions.mjs python tools/verify-engine-regressions.py + - name: Verify hidden text and author display overrides in installed output + run: | + node tools/render-hidden-text.mjs + python tools/verify-hidden-text.py - name: Verify inline wrapping from the installed package run: | npm run render:inline @@ -103,6 +107,7 @@ jobs: output/standard-baseline/ output/standard-fonts/ output/engine-regressions/ + output/hidden-text/ retention-days: 7 installed: @@ -152,6 +157,8 @@ jobs: npm run verify:standard node tools/render-engine-regressions.mjs python tools/verify-engine-regressions.py + node tools/render-hidden-text.mjs + python tools/verify-hidden-text.py - uses: actions/upload-artifact@v4 if: always() && matrix.os == 'windows-latest' && matrix.node == '22' with: @@ -162,6 +169,7 @@ jobs: output/inline-wrapping/ output/standard-fonts/ output/engine-regressions/ + output/hidden-text/ retention-days: 7 - uses: actions/upload-artifact@v4 if: always() @@ -221,6 +229,7 @@ jobs: timeout-minutes: 5 - run: python tools/check-browser.py --browser ${{ matrix.browser }} --label ci - run: python tools/verify-engine-regressions.py output/browser-${{ matrix.browser }}-ci --browser + - run: python tools/verify-hidden-text.py output/browser-${{ matrix.browser }}-ci --browser - uses: actions/upload-artifact@v4 if: always() with: diff --git a/docs/engine-2.5.24.md b/docs/engine-2.5.24.md new file mode 100644 index 0000000..b3bd8f6 --- /dev/null +++ b/docs/engine-2.5.24.md @@ -0,0 +1,53 @@ +# Engine 2.5.24 update checks + +The engine update is being prepared. Publishing it requires the unmodified +published 2.5.24 crate, a locked WebAssembly build, an isolated npm-tarball +installation, and the Node and browser checks below. + +The update carries two fixes: + +- Script/style elements are hidden by default, and ordinary `display:none` + descendants stay hidden in flattened paragraphs, lists, captions, and cells. + Author CSS can deliberately display source; `display:revert` restores the + default display value. +- `text-decoration-thickness: from-font` uses the selected font's preferred + thickness. Bundled Inter at 40px specifies 2.051pt; the previous engine used + the automatic 3pt stroke. + +The [94 visible-content pairs](../test/fixtures/hidden-text-2.5.24.json) retain +control PDFs and finalized-preview expectations from the installed core +candidate. The published npm 0.5.0 package reproduces 77 affected pairs in each +Node isolation mode while preserving all control outputs. + +The [24 decoration fixtures](../test/fixtures/decoration-thickness.json) cover +underlines, overlines, strike-throughs, and combinations at two sizes. fontTools +reads the bundled font's OpenType metrics, and pypdf measures emitted colored +stroke widths. The npm 0.5.0 negative control retains eight affected cases and +40 passing explicit-length, automatic, and small-text controls across both +isolation modes. + +From a built checkout with verifier dependencies installed: + +```sh +npm run verify:pack +node tools/render-hidden-text.mjs +python tools/verify-hidden-text.py +node tools/render-engine-regressions.mjs +python tools/verify-engine-regressions.py +npm run verify:native +npm run verify:browser:prepare +python tools/check-browser.py --browser chrome --label candidate +python tools/verify-hidden-text.py output/browser-chrome-candidate --browser +python tools/verify-engine-regressions.py output/browser-chrome-candidate --browser +``` + +CI repeats the browser download checks in Chrome, Firefox, and WebKit. Use a +fresh output directory for each retained run. The Node render tools accept +`--package-root` and `--out` for a separate installation. To reproduce the old +package's defects, use `render-hidden-text.mjs --negative-control`, or render +engine regressions with `--decoration-only` and verify them with +`verify-engine-regressions.py --negative-decoration-control`. + +Independent PDFium source/control pixels are compared within each platform. +These checks do not claim identical PDFium rasterization across operating +systems, general browser pixel parity, or PDF standards conformance. diff --git a/test/engine-regressions.mjs b/test/engine-regressions.mjs index 8bf4656..ad48fb0 100644 --- a/test/engine-regressions.mjs +++ b/test/engine-regressions.mjs @@ -2,11 +2,13 @@ import { readFile } from 'node:fs/promises'; export const engineRegressionSource = JSON.parse(await readFile(new URL('./fixtures/engine-2.5.22.json', import.meta.url), 'utf8')); -export const engineRegressionFixtures = engineRegressionSource.cases.map(({ name, pages, html, css, previewDpi, fontFiles = [] }) => +export const decorationRegressionSource = JSON.parse(await readFile(new URL('./fixtures/decoration-thickness.json', import.meta.url), 'utf8')); +export const engineRegressionFixtures = [...engineRegressionSource.cases, ...decorationRegressionSource.cases].map(({ name, pages, html, css, previewDpi, fontFiles = [] }) => ({ name, pages, html, css, previewDpi, fontFiles })); -export async function loadEngineRegressionFixtures() { - return Promise.all(engineRegressionFixtures.map(async ({ fontFiles, ...fixture }) => ({ ...fixture, +export async function loadEngineRegressionFixtures({ decorationOnly = false } = {}) { + const selected = decorationOnly ? engineRegressionFixtures.filter(fixture => fixture.name.startsWith('decoration-')) : engineRegressionFixtures; + return Promise.all(selected.map(async ({ fontFiles, ...fixture }) => ({ ...fixture, fonts: await Promise.all(fontFiles.map(name => readFile(new URL('./fonts/' + name, import.meta.url)))), }))); } diff --git a/test/fixtures/decoration-thickness.json b/test/fixtures/decoration-thickness.json new file mode 100644 index 0000000..71f3599 --- /dev/null +++ b/test/fixtures/decoration-thickness.json @@ -0,0 +1,398 @@ +{ + "schema": "fullbleed.javascript.decoration-thickness-fixtures.v1", + "upstreamRelease": "2.5.23", + "source": "https://github.com/fullbleed-engine/fullbleed-official/releases/tag/v2.5.23", + "font": { + "file": "Inter-Variable.ttf", + "sha256": "29160a80ff49ddcab2c97711247e08b1fab27a484a329ce8b813d820dc559031", + "unitsPerEm": 2048, + "underlineThickness": 140 + }, + "scope": "Independent OpenType post-table expectations for emitted colored stroke widths; public Node 0.5.0 is the pre-fix control.", + "cases": [ + { + "name": "decoration-inter-40-underline-font", + "family": "decoration", + "html": "

Invoice AAA HHH

", + "css": "@page {size:480px 140px;margin:0}\nhtml,body {margin:0;padding:0;color:black;background:white}\np {position:absolute;left:24px;top:30px;margin:0;line-height:64px;text-decoration-color:#d00000;text-decoration-skip-ink:none;text-underline-offset:8px}\np {font-family:Inter;font-size:40px;text-decoration-line:underline;text-decoration-thickness:from-font}", + "pages": 1, + "previewDpi": 144, + "expected": { + "variant": "font", + "fontSizePx": 40, + "lines": "underline", + "widthPt": 2.051, + "strokes": 1, + "text": "Invoice AAA HHH" + } + }, + { + "name": "decoration-inter-40-underline-length", + "family": "decoration", + "html": "

Invoice AAA HHH

", + "css": "@page {size:480px 140px;margin:0}\nhtml,body {margin:0;padding:0;color:black;background:white}\np {position:absolute;left:24px;top:30px;margin:0;line-height:64px;text-decoration-color:#d00000;text-decoration-skip-ink:none;text-underline-offset:8px}\np {font-family:Inter;font-size:40px;text-decoration-line:underline;text-decoration-thickness:2.051pt}", + "pages": 1, + "previewDpi": 144, + "expected": { + "variant": "length", + "fontSizePx": 40, + "lines": "underline", + "widthPt": 2.051, + "strokes": 1, + "text": "Invoice AAA HHH" + } + }, + { + "name": "decoration-inter-40-underline-auto", + "family": "decoration", + "html": "

Invoice AAA HHH

", + "css": "@page {size:480px 140px;margin:0}\nhtml,body {margin:0;padding:0;color:black;background:white}\np {position:absolute;left:24px;top:30px;margin:0;line-height:64px;text-decoration-color:#d00000;text-decoration-skip-ink:none;text-underline-offset:8px}\np {font-family:Inter;font-size:40px;text-decoration-line:underline;text-decoration-thickness:auto}", + "pages": 1, + "previewDpi": 144, + "expected": { + "variant": "auto", + "fontSizePx": 40, + "lines": "underline", + "widthPt": 3.0, + "strokes": 1, + "text": "Invoice AAA HHH" + } + }, + { + "name": "decoration-inter-40-overline-font", + "family": "decoration", + "html": "

Invoice AAA HHH

", + "css": "@page {size:480px 140px;margin:0}\nhtml,body {margin:0;padding:0;color:black;background:white}\np {position:absolute;left:24px;top:30px;margin:0;line-height:64px;text-decoration-color:#d00000;text-decoration-skip-ink:none;text-underline-offset:8px}\np {font-family:Inter;font-size:40px;text-decoration-line:overline;text-decoration-thickness:from-font}", + "pages": 1, + "previewDpi": 144, + "expected": { + "variant": "font", + "fontSizePx": 40, + "lines": "overline", + "widthPt": 2.051, + "strokes": 1, + "text": "Invoice AAA HHH" + } + }, + { + "name": "decoration-inter-40-overline-length", + "family": "decoration", + "html": "

Invoice AAA HHH

", + "css": "@page {size:480px 140px;margin:0}\nhtml,body {margin:0;padding:0;color:black;background:white}\np {position:absolute;left:24px;top:30px;margin:0;line-height:64px;text-decoration-color:#d00000;text-decoration-skip-ink:none;text-underline-offset:8px}\np {font-family:Inter;font-size:40px;text-decoration-line:overline;text-decoration-thickness:2.051pt}", + "pages": 1, + "previewDpi": 144, + "expected": { + "variant": "length", + "fontSizePx": 40, + "lines": "overline", + "widthPt": 2.051, + "strokes": 1, + "text": "Invoice AAA HHH" + } + }, + { + "name": "decoration-inter-40-overline-auto", + "family": "decoration", + "html": "

Invoice AAA HHH

", + "css": "@page {size:480px 140px;margin:0}\nhtml,body {margin:0;padding:0;color:black;background:white}\np {position:absolute;left:24px;top:30px;margin:0;line-height:64px;text-decoration-color:#d00000;text-decoration-skip-ink:none;text-underline-offset:8px}\np {font-family:Inter;font-size:40px;text-decoration-line:overline;text-decoration-thickness:auto}", + "pages": 1, + "previewDpi": 144, + "expected": { + "variant": "auto", + "fontSizePx": 40, + "lines": "overline", + "widthPt": 3.0, + "strokes": 1, + "text": "Invoice AAA HHH" + } + }, + { + "name": "decoration-inter-40-line-through-font", + "family": "decoration", + "html": "

Invoice AAA HHH

", + "css": "@page {size:480px 140px;margin:0}\nhtml,body {margin:0;padding:0;color:black;background:white}\np {position:absolute;left:24px;top:30px;margin:0;line-height:64px;text-decoration-color:#d00000;text-decoration-skip-ink:none;text-underline-offset:8px}\np {font-family:Inter;font-size:40px;text-decoration-line:line-through;text-decoration-thickness:from-font}", + "pages": 1, + "previewDpi": 144, + "expected": { + "variant": "font", + "fontSizePx": 40, + "lines": "line-through", + "widthPt": 2.051, + "strokes": 1, + "text": "Invoice AAA HHH" + } + }, + { + "name": "decoration-inter-40-line-through-length", + "family": "decoration", + "html": "

Invoice AAA HHH

", + "css": "@page {size:480px 140px;margin:0}\nhtml,body {margin:0;padding:0;color:black;background:white}\np {position:absolute;left:24px;top:30px;margin:0;line-height:64px;text-decoration-color:#d00000;text-decoration-skip-ink:none;text-underline-offset:8px}\np {font-family:Inter;font-size:40px;text-decoration-line:line-through;text-decoration-thickness:2.051pt}", + "pages": 1, + "previewDpi": 144, + "expected": { + "variant": "length", + "fontSizePx": 40, + "lines": "line-through", + "widthPt": 2.051, + "strokes": 1, + "text": "Invoice AAA HHH" + } + }, + { + "name": "decoration-inter-40-line-through-auto", + "family": "decoration", + "html": "

Invoice AAA HHH

", + "css": "@page {size:480px 140px;margin:0}\nhtml,body {margin:0;padding:0;color:black;background:white}\np {position:absolute;left:24px;top:30px;margin:0;line-height:64px;text-decoration-color:#d00000;text-decoration-skip-ink:none;text-underline-offset:8px}\np {font-family:Inter;font-size:40px;text-decoration-line:line-through;text-decoration-thickness:auto}", + "pages": 1, + "previewDpi": 144, + "expected": { + "variant": "auto", + "fontSizePx": 40, + "lines": "line-through", + "widthPt": 3.0, + "strokes": 1, + "text": "Invoice AAA HHH" + } + }, + { + "name": "decoration-inter-40-underline-overline-line-through-font", + "family": "decoration", + "html": "

Invoice AAA HHH

", + "css": "@page {size:480px 140px;margin:0}\nhtml,body {margin:0;padding:0;color:black;background:white}\np {position:absolute;left:24px;top:30px;margin:0;line-height:64px;text-decoration-color:#d00000;text-decoration-skip-ink:none;text-underline-offset:8px}\np {font-family:Inter;font-size:40px;text-decoration-line:underline overline line-through;text-decoration-thickness:from-font}", + "pages": 1, + "previewDpi": 144, + "expected": { + "variant": "font", + "fontSizePx": 40, + "lines": "underline overline line-through", + "widthPt": 2.051, + "strokes": 3, + "text": "Invoice AAA HHH" + } + }, + { + "name": "decoration-inter-40-underline-overline-line-through-length", + "family": "decoration", + "html": "

Invoice AAA HHH

", + "css": "@page {size:480px 140px;margin:0}\nhtml,body {margin:0;padding:0;color:black;background:white}\np {position:absolute;left:24px;top:30px;margin:0;line-height:64px;text-decoration-color:#d00000;text-decoration-skip-ink:none;text-underline-offset:8px}\np {font-family:Inter;font-size:40px;text-decoration-line:underline overline line-through;text-decoration-thickness:2.051pt}", + "pages": 1, + "previewDpi": 144, + "expected": { + "variant": "length", + "fontSizePx": 40, + "lines": "underline overline line-through", + "widthPt": 2.051, + "strokes": 3, + "text": "Invoice AAA HHH" + } + }, + { + "name": "decoration-inter-40-underline-overline-line-through-auto", + "family": "decoration", + "html": "

Invoice AAA HHH

", + "css": "@page {size:480px 140px;margin:0}\nhtml,body {margin:0;padding:0;color:black;background:white}\np {position:absolute;left:24px;top:30px;margin:0;line-height:64px;text-decoration-color:#d00000;text-decoration-skip-ink:none;text-underline-offset:8px}\np {font-family:Inter;font-size:40px;text-decoration-line:underline overline line-through;text-decoration-thickness:auto}", + "pages": 1, + "previewDpi": 144, + "expected": { + "variant": "auto", + "fontSizePx": 40, + "lines": "underline overline line-through", + "widthPt": 3.0, + "strokes": 3, + "text": "Invoice AAA HHH" + } + }, + { + "name": "decoration-inter-12-underline-font", + "family": "decoration", + "html": "

Invoice AAA HHH

", + "css": "@page {size:480px 140px;margin:0}\nhtml,body {margin:0;padding:0;color:black;background:white}\np {position:absolute;left:24px;top:30px;margin:0;line-height:64px;text-decoration-color:#d00000;text-decoration-skip-ink:none;text-underline-offset:8px}\np {font-family:Inter;font-size:12px;text-decoration-line:underline;text-decoration-thickness:from-font}", + "pages": 1, + "previewDpi": 144, + "expected": { + "variant": "font", + "fontSizePx": 12, + "lines": "underline", + "widthPt": 0.75, + "strokes": 1, + "text": "Invoice AAA HHH" + } + }, + { + "name": "decoration-inter-12-underline-length", + "family": "decoration", + "html": "

Invoice AAA HHH

", + "css": "@page {size:480px 140px;margin:0}\nhtml,body {margin:0;padding:0;color:black;background:white}\np {position:absolute;left:24px;top:30px;margin:0;line-height:64px;text-decoration-color:#d00000;text-decoration-skip-ink:none;text-underline-offset:8px}\np {font-family:Inter;font-size:12px;text-decoration-line:underline;text-decoration-thickness:0.75pt}", + "pages": 1, + "previewDpi": 144, + "expected": { + "variant": "length", + "fontSizePx": 12, + "lines": "underline", + "widthPt": 0.75, + "strokes": 1, + "text": "Invoice AAA HHH" + } + }, + { + "name": "decoration-inter-12-underline-auto", + "family": "decoration", + "html": "

Invoice AAA HHH

", + "css": "@page {size:480px 140px;margin:0}\nhtml,body {margin:0;padding:0;color:black;background:white}\np {position:absolute;left:24px;top:30px;margin:0;line-height:64px;text-decoration-color:#d00000;text-decoration-skip-ink:none;text-underline-offset:8px}\np {font-family:Inter;font-size:12px;text-decoration-line:underline;text-decoration-thickness:auto}", + "pages": 1, + "previewDpi": 144, + "expected": { + "variant": "auto", + "fontSizePx": 12, + "lines": "underline", + "widthPt": 0.75, + "strokes": 1, + "text": "Invoice AAA HHH" + } + }, + { + "name": "decoration-inter-12-overline-font", + "family": "decoration", + "html": "

Invoice AAA HHH

", + "css": "@page {size:480px 140px;margin:0}\nhtml,body {margin:0;padding:0;color:black;background:white}\np {position:absolute;left:24px;top:30px;margin:0;line-height:64px;text-decoration-color:#d00000;text-decoration-skip-ink:none;text-underline-offset:8px}\np {font-family:Inter;font-size:12px;text-decoration-line:overline;text-decoration-thickness:from-font}", + "pages": 1, + "previewDpi": 144, + "expected": { + "variant": "font", + "fontSizePx": 12, + "lines": "overline", + "widthPt": 0.75, + "strokes": 1, + "text": "Invoice AAA HHH" + } + }, + { + "name": "decoration-inter-12-overline-length", + "family": "decoration", + "html": "

Invoice AAA HHH

", + "css": "@page {size:480px 140px;margin:0}\nhtml,body {margin:0;padding:0;color:black;background:white}\np {position:absolute;left:24px;top:30px;margin:0;line-height:64px;text-decoration-color:#d00000;text-decoration-skip-ink:none;text-underline-offset:8px}\np {font-family:Inter;font-size:12px;text-decoration-line:overline;text-decoration-thickness:0.75pt}", + "pages": 1, + "previewDpi": 144, + "expected": { + "variant": "length", + "fontSizePx": 12, + "lines": "overline", + "widthPt": 0.75, + "strokes": 1, + "text": "Invoice AAA HHH" + } + }, + { + "name": "decoration-inter-12-overline-auto", + "family": "decoration", + "html": "

Invoice AAA HHH

", + "css": "@page {size:480px 140px;margin:0}\nhtml,body {margin:0;padding:0;color:black;background:white}\np {position:absolute;left:24px;top:30px;margin:0;line-height:64px;text-decoration-color:#d00000;text-decoration-skip-ink:none;text-underline-offset:8px}\np {font-family:Inter;font-size:12px;text-decoration-line:overline;text-decoration-thickness:auto}", + "pages": 1, + "previewDpi": 144, + "expected": { + "variant": "auto", + "fontSizePx": 12, + "lines": "overline", + "widthPt": 0.75, + "strokes": 1, + "text": "Invoice AAA HHH" + } + }, + { + "name": "decoration-inter-12-line-through-font", + "family": "decoration", + "html": "

Invoice AAA HHH

", + "css": "@page {size:480px 140px;margin:0}\nhtml,body {margin:0;padding:0;color:black;background:white}\np {position:absolute;left:24px;top:30px;margin:0;line-height:64px;text-decoration-color:#d00000;text-decoration-skip-ink:none;text-underline-offset:8px}\np {font-family:Inter;font-size:12px;text-decoration-line:line-through;text-decoration-thickness:from-font}", + "pages": 1, + "previewDpi": 144, + "expected": { + "variant": "font", + "fontSizePx": 12, + "lines": "line-through", + "widthPt": 0.75, + "strokes": 1, + "text": "Invoice AAA HHH" + } + }, + { + "name": "decoration-inter-12-line-through-length", + "family": "decoration", + "html": "

Invoice AAA HHH

", + "css": "@page {size:480px 140px;margin:0}\nhtml,body {margin:0;padding:0;color:black;background:white}\np {position:absolute;left:24px;top:30px;margin:0;line-height:64px;text-decoration-color:#d00000;text-decoration-skip-ink:none;text-underline-offset:8px}\np {font-family:Inter;font-size:12px;text-decoration-line:line-through;text-decoration-thickness:0.75pt}", + "pages": 1, + "previewDpi": 144, + "expected": { + "variant": "length", + "fontSizePx": 12, + "lines": "line-through", + "widthPt": 0.75, + "strokes": 1, + "text": "Invoice AAA HHH" + } + }, + { + "name": "decoration-inter-12-line-through-auto", + "family": "decoration", + "html": "

Invoice AAA HHH

", + "css": "@page {size:480px 140px;margin:0}\nhtml,body {margin:0;padding:0;color:black;background:white}\np {position:absolute;left:24px;top:30px;margin:0;line-height:64px;text-decoration-color:#d00000;text-decoration-skip-ink:none;text-underline-offset:8px}\np {font-family:Inter;font-size:12px;text-decoration-line:line-through;text-decoration-thickness:auto}", + "pages": 1, + "previewDpi": 144, + "expected": { + "variant": "auto", + "fontSizePx": 12, + "lines": "line-through", + "widthPt": 0.75, + "strokes": 1, + "text": "Invoice AAA HHH" + } + }, + { + "name": "decoration-inter-12-underline-overline-line-through-font", + "family": "decoration", + "html": "

Invoice AAA HHH

", + "css": "@page {size:480px 140px;margin:0}\nhtml,body {margin:0;padding:0;color:black;background:white}\np {position:absolute;left:24px;top:30px;margin:0;line-height:64px;text-decoration-color:#d00000;text-decoration-skip-ink:none;text-underline-offset:8px}\np {font-family:Inter;font-size:12px;text-decoration-line:underline overline line-through;text-decoration-thickness:from-font}", + "pages": 1, + "previewDpi": 144, + "expected": { + "variant": "font", + "fontSizePx": 12, + "lines": "underline overline line-through", + "widthPt": 0.75, + "strokes": 3, + "text": "Invoice AAA HHH" + } + }, + { + "name": "decoration-inter-12-underline-overline-line-through-length", + "family": "decoration", + "html": "

Invoice AAA HHH

", + "css": "@page {size:480px 140px;margin:0}\nhtml,body {margin:0;padding:0;color:black;background:white}\np {position:absolute;left:24px;top:30px;margin:0;line-height:64px;text-decoration-color:#d00000;text-decoration-skip-ink:none;text-underline-offset:8px}\np {font-family:Inter;font-size:12px;text-decoration-line:underline overline line-through;text-decoration-thickness:0.75pt}", + "pages": 1, + "previewDpi": 144, + "expected": { + "variant": "length", + "fontSizePx": 12, + "lines": "underline overline line-through", + "widthPt": 0.75, + "strokes": 3, + "text": "Invoice AAA HHH" + } + }, + { + "name": "decoration-inter-12-underline-overline-line-through-auto", + "family": "decoration", + "html": "

Invoice AAA HHH

", + "css": "@page {size:480px 140px;margin:0}\nhtml,body {margin:0;padding:0;color:black;background:white}\np {position:absolute;left:24px;top:30px;margin:0;line-height:64px;text-decoration-color:#d00000;text-decoration-skip-ink:none;text-underline-offset:8px}\np {font-family:Inter;font-size:12px;text-decoration-line:underline overline line-through;text-decoration-thickness:auto}", + "pages": 1, + "previewDpi": 144, + "expected": { + "variant": "auto", + "fontSizePx": 12, + "lines": "underline overline line-through", + "widthPt": 0.75, + "strokes": 3, + "text": "Invoice AAA HHH" + } + } + ] +} diff --git a/test/fixtures/hidden-text-2.5.24.json b/test/fixtures/hidden-text-2.5.24.json new file mode 100644 index 0000000..3bbe4e9 --- /dev/null +++ b/test/fixtures/hidden-text-2.5.24.json @@ -0,0 +1,1608 @@ +{ + "schema": "fullbleed.javascript.hidden-text-fixtures.v1", + "engineVersion": "2.5.24", + "sourceCommit": "6251674614fff698ce685733e81bf481e6e81313", + "sourceReview": "https://github.com/fullbleed-engine/fullbleed-official/pull/79", + "sourceVerificationSha256": "7d5a431ca20aa50d94022494a0d6acdbfd73632aca7159f6aa60a0cfc541a7cd", + "scope": "Visible-content controls from the installed core candidate; finalized native PNGs at 96 DPI. No browser/engine pixel parity or PDF conformance claim.", + "cases": [ + { + "name": "script-blocks", + "kind": "hidden", + "sourceHtml": "

Document AAA

Before

After

", + "controlHtml": "

Document AAA

Before

After

", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "941b22bd5f30bf0f3a46faa6d749ee865eb785dfa623089f3dad0fe7d14a61f7", + "png": "a602dde65015c5ed2945dad9d84381e122adfc7c5577d8ea18bb68e80e471410", + "text": [ + "Document AAA Before After" + ] + } + }, + { + "name": "script-paragraph", + "kind": "hidden", + "sourceHtml": "

Document AAA

Before After

", + "controlHtml": "

Document AAA

Before After

", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": false, + "expected": { + "pdf": "17cd57e1c75e11561e6a2328aed0ec906a2e6994397cdde064ab56bc14c90f2e", + "png": "4deab8e90ee41a7be394e225a4b419e922f290d4501446750a892ba99aafd9a3", + "text": [ + "Document AAA Before After" + ] + } + }, + { + "name": "script-styled-inline", + "kind": "hidden", + "sourceHtml": "

Document AAA

Before After

", + "controlHtml": "

Document AAA

Before After

", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "b40ab03462771d4b765e8490348ff3f8bebd582c9bbdcdbbbc05872c8685a493", + "png": "9613f7a59130dc1203f9e588e9b75ff5749d392ad30784fa3f13a196cbd2deec", + "text": [ + "Document AAA Before After" + ] + } + }, + { + "name": "script-contents", + "kind": "hidden", + "sourceHtml": "

Document AAA

Before

After

", + "controlHtml": "

Document AAA

Before

After

", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "941b22bd5f30bf0f3a46faa6d749ee865eb785dfa623089f3dad0fe7d14a61f7", + "png": "a602dde65015c5ed2945dad9d84381e122adfc7c5577d8ea18bb68e80e471410", + "text": [ + "Document AAA Before After" + ] + } + }, + { + "name": "script-flex", + "kind": "hidden", + "sourceHtml": "

Document AAA

Before

After

", + "controlHtml": "

Document AAA

Before

After

", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "f868c1d73ebb61bec2211c83351302c6aaa8f3de925e36d0fdf037babfcc1216", + "png": "b8afb7c6cd5ff169df66e658abc333b7de40307f4070924b9ff80c8a04c72ca1", + "text": [ + "Document AAA Before After" + ] + } + }, + { + "name": "script-grid", + "kind": "hidden", + "sourceHtml": "

Document AAA

Before

After

", + "controlHtml": "

Document AAA

Before

After

", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "3b1b0bbeb64dc0eb2b168ff4be2084f3c2f222e741a77ea8572c9511d30f6911", + "png": "4afde76d0b10d3bf469feb88de809609df596f426083586a06caad6c73f56dcf", + "text": [ + "Document AAA Before After" + ] + } + }, + { + "name": "script-table-cell", + "kind": "hidden", + "sourceHtml": "

Document AAA

Before After
", + "controlHtml": "

Document AAA

Before After
", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "3b81e86ebbada8ae848562469d42d7e0d8b9703b235c23ef2c524ab13551d7d9", + "png": "52cb5ae6ffdba415ad630fdb2bc5b56f59cf48800f3d91fb69a7f311407469c2", + "text": [ + "Document AAA Before After" + ] + } + }, + { + "name": "script-empty-table-cell", + "kind": "hidden", + "sourceHtml": "

Document AAA

Before

After

", + "controlHtml": "

Document AAA

Before

After

", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "690fef7da0f3008f0708198ed90b9ab713b9a7a83ce7c38692ea52af3edf365a", + "png": "a602dde65015c5ed2945dad9d84381e122adfc7c5577d8ea18bb68e80e471410", + "text": [ + "Document AAA Before After" + ] + } + }, + { + "name": "script-caption", + "kind": "hidden", + "sourceHtml": "

Document AAA

Before After
Cell
", + "controlHtml": "

Document AAA

Before After
Cell
", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": false, + "expected": { + "pdf": "1249da015de7e7e3665b3b02235f26be4869f10a2176695d67923f2feca3931b", + "png": "52cf159fef02af27e765c56fb05036c3b5ba45a81488ff14d9ddd92a33a99462", + "text": [ + "Document AAA Before After Cell" + ] + } + }, + { + "name": "script-css-caption", + "kind": "hidden", + "sourceHtml": "

Document AAA

Before After
Cell
", + "controlHtml": "

Document AAA

Before After
Cell
", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": false, + "expected": { + "pdf": "1249da015de7e7e3665b3b02235f26be4869f10a2176695d67923f2feca3931b", + "png": "52cf159fef02af27e765c56fb05036c3b5ba45a81488ff14d9ddd92a33a99462", + "text": [ + "Document AAA Before After Cell" + ] + } + }, + { + "name": "script-list", + "kind": "hidden", + "sourceHtml": "

Document AAA

", + "controlHtml": "

Document AAA

", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": false, + "expected": { + "pdf": "cc91d796958dc8df00956918bc1922e030ef796595a39dfc9ddc9b1ad1ab6b49", + "png": "e1d51f4165e613fe8358416f58c77db8adae82ba676489059ff402ea6ab5a756", + "text": [ + "Document AAA \u2022 Before After" + ] + } + }, + { + "name": "script-pre", + "kind": "hidden", + "sourceHtml": "

Document AAA

Before  After
", + "controlHtml": "

Document AAA

Before  After
", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": false, + "expected": { + "pdf": "f97d20870474202e71dace2f2d246f5704a8305cf21f0e579edcf72b43664887", + "png": "f9c59793360f5f60b27dc4130156814133fb96fcc24385d71e9653fbc75591f2", + "text": [ + "Document AAA Before After" + ] + } + }, + { + "name": "script-inline-paragraph", + "kind": "visible", + "sourceHtml": "

Document AAA

Before After

", + "controlHtml": "

Document AAA

Before SOURCE_SENTINEL After

", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n.probe {display:inline}", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "0ff638f747481f71797696ff4b0c191b4505ca6bb9bf13fb073822d0c59f2947", + "png": "4c9806bbd8cc2dc9121683bdfdc761c414aad2c83d6eb0f3ff39150e631d26b7", + "text": [ + "Document AAA Before SOURCE_SENTINEL After" + ] + } + }, + { + "name": "script-inline-pre", + "kind": "visible", + "sourceHtml": "

Document AAA

Before  After
", + "controlHtml": "

Document AAA

Before SOURCE_SENTINEL After
", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n.probe {display:inline}", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "0ff638f747481f71797696ff4b0c191b4505ca6bb9bf13fb073822d0c59f2947", + "png": "4c9806bbd8cc2dc9121683bdfdc761c414aad2c83d6eb0f3ff39150e631d26b7", + "text": [ + "Document AAA Before SOURCE_SENTINEL After" + ] + } + }, + { + "name": "script-inline-caption", + "kind": "visible", + "sourceHtml": "

Document AAA

Before After
Cell
", + "controlHtml": "

Document AAA

Before SOURCE_SENTINEL After
Cell
", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n.probe {display:inline}", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "5b10a1e7d1b570481d2f9c64f57af1f373b0d2b1c421695ceb32880356253f5a", + "png": "20286bef12b5dfdf7b152ea3101927425fb5d14db2dd1fb6f4449492d6321b82", + "text": [ + "Document AAA Before SOURCE_SENTINEL After Cell" + ] + } + }, + { + "name": "script-inline-list", + "kind": "visible", + "sourceHtml": "

Document AAA

", + "controlHtml": "

Document AAA

", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n.probe {display:inline}", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "1c847135162b5a9f207a0a4d7a7972ce796ff307680cfacd9bb5929191e42ce3", + "png": "47b4f7d2779d8b2036dc988dbc0a88f9a6e21a9a8b1ef80e3a0d7a0d4975defb", + "text": [ + "Document AAA \u2022 Before SOURCE_SENTINEL After" + ] + } + }, + { + "name": "script-block-paragraph", + "kind": "visible", + "sourceHtml": "

Document AAA

Before After

", + "controlHtml": "

Document AAA

Before SOURCE_SENTINEL After

", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n.probe {display:block}", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": false, + "expected": { + "pdf": "9f544031ce1ca7ec3bf0f22724c6eb27922a4763deb948316ab568e7c9e8a329", + "png": "1c0368d02c106d934801f456cb1343425331b7830ee4a811d7c3449d7cd77c2e", + "text": [ + "Document AAA Before SOURCE_SENTINEL After" + ] + } + }, + { + "name": "script-block-pre", + "kind": "visible", + "sourceHtml": "

Document AAA

Before  After
", + "controlHtml": "

Document AAA

Before SOURCE_SENTINEL After
", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n.probe {display:block}", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "0ff638f747481f71797696ff4b0c191b4505ca6bb9bf13fb073822d0c59f2947", + "png": "4c9806bbd8cc2dc9121683bdfdc761c414aad2c83d6eb0f3ff39150e631d26b7", + "text": [ + "Document AAA Before SOURCE_SENTINEL After" + ] + } + }, + { + "name": "script-block-caption", + "kind": "visible", + "sourceHtml": "

Document AAA

Before After
Cell
", + "controlHtml": "

Document AAA

Before SOURCE_SENTINEL After
Cell
", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n.probe {display:block}", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "5b10a1e7d1b570481d2f9c64f57af1f373b0d2b1c421695ceb32880356253f5a", + "png": "20286bef12b5dfdf7b152ea3101927425fb5d14db2dd1fb6f4449492d6321b82", + "text": [ + "Document AAA Before SOURCE_SENTINEL After Cell" + ] + } + }, + { + "name": "script-block-list", + "kind": "visible", + "sourceHtml": "

Document AAA

", + "controlHtml": "

Document AAA

", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n.probe {display:block}", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": false, + "expected": { + "pdf": "f58b8fc1c5cbda2ce13b72b67744ab1c742a2510c4a71a9caf01cac4d01617ab", + "png": "d668ab0f0b0cb89a76dac3c38b9779d8bc481e1bcd8944a9b53955ea971e1b1a", + "text": [ + "Document AAA \u2022 Before SOURCE_SENTINEL After" + ] + } + }, + { + "name": "script-contents-paragraph", + "kind": "visible", + "sourceHtml": "

Document AAA

Before After

", + "controlHtml": "

Document AAA

Before SOURCE_SENTINEL After

", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n.probe {display:contents}", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "0ff638f747481f71797696ff4b0c191b4505ca6bb9bf13fb073822d0c59f2947", + "png": "4c9806bbd8cc2dc9121683bdfdc761c414aad2c83d6eb0f3ff39150e631d26b7", + "text": [ + "Document AAA Before SOURCE_SENTINEL After" + ] + } + }, + { + "name": "script-contents-pre", + "kind": "visible", + "sourceHtml": "

Document AAA

Before  After
", + "controlHtml": "

Document AAA

Before SOURCE_SENTINEL After
", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n.probe {display:contents}", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "0ff638f747481f71797696ff4b0c191b4505ca6bb9bf13fb073822d0c59f2947", + "png": "4c9806bbd8cc2dc9121683bdfdc761c414aad2c83d6eb0f3ff39150e631d26b7", + "text": [ + "Document AAA Before SOURCE_SENTINEL After" + ] + } + }, + { + "name": "script-contents-caption", + "kind": "visible", + "sourceHtml": "

Document AAA

Before After
Cell
", + "controlHtml": "

Document AAA

Before SOURCE_SENTINEL After
Cell
", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n.probe {display:contents}", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "5b10a1e7d1b570481d2f9c64f57af1f373b0d2b1c421695ceb32880356253f5a", + "png": "20286bef12b5dfdf7b152ea3101927425fb5d14db2dd1fb6f4449492d6321b82", + "text": [ + "Document AAA Before SOURCE_SENTINEL After Cell" + ] + } + }, + { + "name": "script-contents-list", + "kind": "visible", + "sourceHtml": "

Document AAA

", + "controlHtml": "

Document AAA

", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n.probe {display:contents}", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "1c847135162b5a9f207a0a4d7a7972ce796ff307680cfacd9bb5929191e42ce3", + "png": "47b4f7d2779d8b2036dc988dbc0a88f9a6e21a9a8b1ef80e3a0d7a0d4975defb", + "text": [ + "Document AAA \u2022 Before SOURCE_SENTINEL After" + ] + } + }, + { + "name": "script-initial-paragraph", + "kind": "visible", + "sourceHtml": "

Document AAA

Before After

", + "controlHtml": "

Document AAA

Before SOURCE_SENTINEL After

", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n.probe {display:initial}", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "0ff638f747481f71797696ff4b0c191b4505ca6bb9bf13fb073822d0c59f2947", + "png": "4c9806bbd8cc2dc9121683bdfdc761c414aad2c83d6eb0f3ff39150e631d26b7", + "text": [ + "Document AAA Before SOURCE_SENTINEL After" + ] + } + }, + { + "name": "script-initial-pre", + "kind": "visible", + "sourceHtml": "

Document AAA

Before  After
", + "controlHtml": "

Document AAA

Before SOURCE_SENTINEL After
", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n.probe {display:initial}", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "0ff638f747481f71797696ff4b0c191b4505ca6bb9bf13fb073822d0c59f2947", + "png": "4c9806bbd8cc2dc9121683bdfdc761c414aad2c83d6eb0f3ff39150e631d26b7", + "text": [ + "Document AAA Before SOURCE_SENTINEL After" + ] + } + }, + { + "name": "script-initial-caption", + "kind": "visible", + "sourceHtml": "

Document AAA

Before After
Cell
", + "controlHtml": "

Document AAA

Before SOURCE_SENTINEL After
Cell
", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n.probe {display:initial}", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "5b10a1e7d1b570481d2f9c64f57af1f373b0d2b1c421695ceb32880356253f5a", + "png": "20286bef12b5dfdf7b152ea3101927425fb5d14db2dd1fb6f4449492d6321b82", + "text": [ + "Document AAA Before SOURCE_SENTINEL After Cell" + ] + } + }, + { + "name": "script-initial-list", + "kind": "visible", + "sourceHtml": "

Document AAA

", + "controlHtml": "

Document AAA

", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n.probe {display:initial}", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "1c847135162b5a9f207a0a4d7a7972ce796ff307680cfacd9bb5929191e42ce3", + "png": "47b4f7d2779d8b2036dc988dbc0a88f9a6e21a9a8b1ef80e3a0d7a0d4975defb", + "text": [ + "Document AAA \u2022 Before SOURCE_SENTINEL After" + ] + } + }, + { + "name": "script-unset-paragraph", + "kind": "visible", + "sourceHtml": "

Document AAA

Before After

", + "controlHtml": "

Document AAA

Before SOURCE_SENTINEL After

", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n.probe {display:unset}", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "0ff638f747481f71797696ff4b0c191b4505ca6bb9bf13fb073822d0c59f2947", + "png": "4c9806bbd8cc2dc9121683bdfdc761c414aad2c83d6eb0f3ff39150e631d26b7", + "text": [ + "Document AAA Before SOURCE_SENTINEL After" + ] + } + }, + { + "name": "script-unset-pre", + "kind": "visible", + "sourceHtml": "

Document AAA

Before  After
", + "controlHtml": "

Document AAA

Before SOURCE_SENTINEL After
", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n.probe {display:unset}", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "0ff638f747481f71797696ff4b0c191b4505ca6bb9bf13fb073822d0c59f2947", + "png": "4c9806bbd8cc2dc9121683bdfdc761c414aad2c83d6eb0f3ff39150e631d26b7", + "text": [ + "Document AAA Before SOURCE_SENTINEL After" + ] + } + }, + { + "name": "script-unset-caption", + "kind": "visible", + "sourceHtml": "

Document AAA

Before After
Cell
", + "controlHtml": "

Document AAA

Before SOURCE_SENTINEL After
Cell
", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n.probe {display:unset}", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "5b10a1e7d1b570481d2f9c64f57af1f373b0d2b1c421695ceb32880356253f5a", + "png": "20286bef12b5dfdf7b152ea3101927425fb5d14db2dd1fb6f4449492d6321b82", + "text": [ + "Document AAA Before SOURCE_SENTINEL After Cell" + ] + } + }, + { + "name": "script-unset-list", + "kind": "visible", + "sourceHtml": "

Document AAA

", + "controlHtml": "

Document AAA

", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n.probe {display:unset}", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "1c847135162b5a9f207a0a4d7a7972ce796ff307680cfacd9bb5929191e42ce3", + "png": "47b4f7d2779d8b2036dc988dbc0a88f9a6e21a9a8b1ef80e3a0d7a0d4975defb", + "text": [ + "Document AAA \u2022 Before SOURCE_SENTINEL After" + ] + } + }, + { + "name": "script-inline-attribute", + "kind": "visible", + "sourceHtml": "

Document AAA

Before After

", + "controlHtml": "

Document AAA

Before SOURCE_SENTINEL After

", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "0ff638f747481f71797696ff4b0c191b4505ca6bb9bf13fb073822d0c59f2947", + "png": "4c9806bbd8cc2dc9121683bdfdc761c414aad2c83d6eb0f3ff39150e631d26b7", + "text": [ + "Document AAA Before SOURCE_SENTINEL After" + ] + } + }, + { + "name": "script-revert", + "kind": "hidden", + "sourceHtml": "

Document AAA

Before

After

", + "controlHtml": "

Document AAA

Before

After

", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n.probe {display:block;display:revert}", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "941b22bd5f30bf0f3a46faa6d749ee865eb785dfa623089f3dad0fe7d14a61f7", + "png": "a602dde65015c5ed2945dad9d84381e122adfc7c5577d8ea18bb68e80e471410", + "text": [ + "Document AAA Before After" + ] + } + }, + { + "name": "script-hidden-parent", + "kind": "hidden", + "sourceHtml": "

Document AAA

Before After

", + "controlHtml": "

Document AAA

Before After

", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n.probe {display:inline}", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": false, + "expected": { + "pdf": "17cd57e1c75e11561e6a2328aed0ec906a2e6994397cdde064ab56bc14c90f2e", + "png": "4deab8e90ee41a7be394e225a4b419e922f290d4501446750a892ba99aafd9a3", + "text": [ + "Document AAA Before After" + ] + } + }, + { + "name": "script-hidden-decoration", + "kind": "hidden", + "sourceHtml": "

Document AAA

Before

After

", + "controlHtml": "

Document AAA

Before

After

", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n.probe {padding:30px;border:4px solid red;margin:20px} .probe::before {content:\"UNEXPECTED\"}", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "941b22bd5f30bf0f3a46faa6d749ee865eb785dfa623089f3dad0fe7d14a61f7", + "png": "a602dde65015c5ed2945dad9d84381e122adfc7c5577d8ea18bb68e80e471410", + "text": [ + "Document AAA Before After" + ] + } + }, + { + "name": "script-leading-space", + "kind": "visible", + "sourceHtml": "

Document AAA

After
", + "controlHtml": "

Document AAA

SOURCE_SENTINEL After
", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n.probe {display:inline;color:#751b38} .decorated::before {content:\"Prefix\"} .decorated::after {content:\"Suffix\"}", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "89d52efd9e29c4070ee1804a9342f6cdf3c8c9eeb325acf4d81e70bdb49f6243", + "png": "e12cf00ce6bc040c2aa0ad5a22bbe05ac09f5719d96351ddb157b6023e9d3c36", + "text": [ + "Document AAA Prefix SOURCE_SENTINEL AfterSuffix" + ] + } + }, + { + "name": "script-trailing-space", + "kind": "visible", + "sourceHtml": "

Document AAA

Before
", + "controlHtml": "

Document AAA

Before SOURCE_SENTINEL
", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n.probe {display:inline;color:#751b38} .decorated::before {content:\"Prefix\"} .decorated::after {content:\"Suffix\"}", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "82648411357033aedf6e2659f4d3d2848d61aeeb0776dc78eeebe4e30eeec0b0", + "png": "de90aba95e05b72658a3490a96ce25d6b681aef57ecfb7e0fa1db6106e215e51", + "text": [ + "Document AAA Prefix Before SOURCE_SENTINELSuffix" + ] + } + }, + { + "name": "script-pseudo-space", + "kind": "visible", + "sourceHtml": "

Document AAA

After

", + "controlHtml": "

Document AAA

SOURCE_SENTINEL After

", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n.probe {display:inline;color:#751b38} .decorated::before {content:\"Prefix\"} .decorated::after {content:\"Suffix\"}", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "e46ff578f9e91b4793d7c8cf7803b7499fb3851419e1a7a1bcb266fcc03a6051", + "png": "b300120859a09897a0a7791afdf753b1c3e833021684be4183af709680ad5a7c", + "text": [ + "Document AAA Prefix SOURCE_SENTINEL After Suffix" + ] + } + }, + { + "name": "script-literal-source", + "kind": "visible", + "sourceHtml": "

Document AAA

Before After

", + "controlHtml": "

Document AAA

Before if (1 < 2) & "quoted"; After

", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n.probe {display:inline}", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "4a153ab4eabd901c9b77be59df59e1b2f3df53d7d316cf1c9f041ece3886fcdd", + "png": "4e303f73337fd8bf79b568b1e68148a8b5f780759f69b8b5d73afd2a92ba683c", + "text": [ + "Document AAA Before if (1 < 2) & \"quoted\"; After" + ] + } + }, + { + "name": "style-blocks", + "kind": "hidden", + "sourceHtml": "

Document AAA

Before

After

", + "controlHtml": "

Document AAA

Before

After

", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "941b22bd5f30bf0f3a46faa6d749ee865eb785dfa623089f3dad0fe7d14a61f7", + "png": "a602dde65015c5ed2945dad9d84381e122adfc7c5577d8ea18bb68e80e471410", + "text": [ + "Document AAA Before After" + ] + } + }, + { + "name": "style-paragraph", + "kind": "hidden", + "sourceHtml": "

Document AAA

Before After

", + "controlHtml": "

Document AAA

Before After

", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": false, + "expected": { + "pdf": "17cd57e1c75e11561e6a2328aed0ec906a2e6994397cdde064ab56bc14c90f2e", + "png": "4deab8e90ee41a7be394e225a4b419e922f290d4501446750a892ba99aafd9a3", + "text": [ + "Document AAA Before After" + ] + } + }, + { + "name": "style-styled-inline", + "kind": "hidden", + "sourceHtml": "

Document AAA

Before After

", + "controlHtml": "

Document AAA

Before After

", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "b40ab03462771d4b765e8490348ff3f8bebd582c9bbdcdbbbc05872c8685a493", + "png": "9613f7a59130dc1203f9e588e9b75ff5749d392ad30784fa3f13a196cbd2deec", + "text": [ + "Document AAA Before After" + ] + } + }, + { + "name": "style-contents", + "kind": "hidden", + "sourceHtml": "

Document AAA

Before

After

", + "controlHtml": "

Document AAA

Before

After

", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "941b22bd5f30bf0f3a46faa6d749ee865eb785dfa623089f3dad0fe7d14a61f7", + "png": "a602dde65015c5ed2945dad9d84381e122adfc7c5577d8ea18bb68e80e471410", + "text": [ + "Document AAA Before After" + ] + } + }, + { + "name": "style-flex", + "kind": "hidden", + "sourceHtml": "

Document AAA

Before

After

", + "controlHtml": "

Document AAA

Before

After

", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "f868c1d73ebb61bec2211c83351302c6aaa8f3de925e36d0fdf037babfcc1216", + "png": "b8afb7c6cd5ff169df66e658abc333b7de40307f4070924b9ff80c8a04c72ca1", + "text": [ + "Document AAA Before After" + ] + } + }, + { + "name": "style-grid", + "kind": "hidden", + "sourceHtml": "

Document AAA

Before

After

", + "controlHtml": "

Document AAA

Before

After

", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "3b1b0bbeb64dc0eb2b168ff4be2084f3c2f222e741a77ea8572c9511d30f6911", + "png": "4afde76d0b10d3bf469feb88de809609df596f426083586a06caad6c73f56dcf", + "text": [ + "Document AAA Before After" + ] + } + }, + { + "name": "style-table-cell", + "kind": "hidden", + "sourceHtml": "

Document AAA

Before After
", + "controlHtml": "

Document AAA

Before After
", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "3b81e86ebbada8ae848562469d42d7e0d8b9703b235c23ef2c524ab13551d7d9", + "png": "52cb5ae6ffdba415ad630fdb2bc5b56f59cf48800f3d91fb69a7f311407469c2", + "text": [ + "Document AAA Before After" + ] + } + }, + { + "name": "style-empty-table-cell", + "kind": "hidden", + "sourceHtml": "

Document AAA

Before

After

", + "controlHtml": "

Document AAA

Before

After

", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "690fef7da0f3008f0708198ed90b9ab713b9a7a83ce7c38692ea52af3edf365a", + "png": "a602dde65015c5ed2945dad9d84381e122adfc7c5577d8ea18bb68e80e471410", + "text": [ + "Document AAA Before After" + ] + } + }, + { + "name": "style-caption", + "kind": "hidden", + "sourceHtml": "

Document AAA

Before After
Cell
", + "controlHtml": "

Document AAA

Before After
Cell
", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": false, + "expected": { + "pdf": "1249da015de7e7e3665b3b02235f26be4869f10a2176695d67923f2feca3931b", + "png": "52cf159fef02af27e765c56fb05036c3b5ba45a81488ff14d9ddd92a33a99462", + "text": [ + "Document AAA Before After Cell" + ] + } + }, + { + "name": "style-css-caption", + "kind": "hidden", + "sourceHtml": "

Document AAA

Before After
Cell
", + "controlHtml": "

Document AAA

Before After
Cell
", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": false, + "expected": { + "pdf": "1249da015de7e7e3665b3b02235f26be4869f10a2176695d67923f2feca3931b", + "png": "52cf159fef02af27e765c56fb05036c3b5ba45a81488ff14d9ddd92a33a99462", + "text": [ + "Document AAA Before After Cell" + ] + } + }, + { + "name": "style-list", + "kind": "hidden", + "sourceHtml": "

Document AAA

", + "controlHtml": "

Document AAA

", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": false, + "expected": { + "pdf": "cc91d796958dc8df00956918bc1922e030ef796595a39dfc9ddc9b1ad1ab6b49", + "png": "e1d51f4165e613fe8358416f58c77db8adae82ba676489059ff402ea6ab5a756", + "text": [ + "Document AAA \u2022 Before After" + ] + } + }, + { + "name": "style-pre", + "kind": "hidden", + "sourceHtml": "

Document AAA

Before  After
", + "controlHtml": "

Document AAA

Before  After
", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": false, + "expected": { + "pdf": "f97d20870474202e71dace2f2d246f5704a8305cf21f0e579edcf72b43664887", + "png": "f9c59793360f5f60b27dc4130156814133fb96fcc24385d71e9653fbc75591f2", + "text": [ + "Document AAA Before After" + ] + } + }, + { + "name": "style-inline-paragraph", + "kind": "visible", + "sourceHtml": "

Document AAA

Before After

", + "controlHtml": "

Document AAA

Before SOURCE_SENTINEL After

", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n.probe {display:inline}", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "0ff638f747481f71797696ff4b0c191b4505ca6bb9bf13fb073822d0c59f2947", + "png": "4c9806bbd8cc2dc9121683bdfdc761c414aad2c83d6eb0f3ff39150e631d26b7", + "text": [ + "Document AAA Before SOURCE_SENTINEL After" + ] + } + }, + { + "name": "style-inline-pre", + "kind": "visible", + "sourceHtml": "

Document AAA

Before  After
", + "controlHtml": "

Document AAA

Before SOURCE_SENTINEL After
", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n.probe {display:inline}", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "0ff638f747481f71797696ff4b0c191b4505ca6bb9bf13fb073822d0c59f2947", + "png": "4c9806bbd8cc2dc9121683bdfdc761c414aad2c83d6eb0f3ff39150e631d26b7", + "text": [ + "Document AAA Before SOURCE_SENTINEL After" + ] + } + }, + { + "name": "style-inline-caption", + "kind": "visible", + "sourceHtml": "

Document AAA

Before After
Cell
", + "controlHtml": "

Document AAA

Before SOURCE_SENTINEL After
Cell
", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n.probe {display:inline}", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "5b10a1e7d1b570481d2f9c64f57af1f373b0d2b1c421695ceb32880356253f5a", + "png": "20286bef12b5dfdf7b152ea3101927425fb5d14db2dd1fb6f4449492d6321b82", + "text": [ + "Document AAA Before SOURCE_SENTINEL After Cell" + ] + } + }, + { + "name": "style-inline-list", + "kind": "visible", + "sourceHtml": "

Document AAA

", + "controlHtml": "

Document AAA

", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n.probe {display:inline}", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "1c847135162b5a9f207a0a4d7a7972ce796ff307680cfacd9bb5929191e42ce3", + "png": "47b4f7d2779d8b2036dc988dbc0a88f9a6e21a9a8b1ef80e3a0d7a0d4975defb", + "text": [ + "Document AAA \u2022 Before SOURCE_SENTINEL After" + ] + } + }, + { + "name": "style-block-paragraph", + "kind": "visible", + "sourceHtml": "

Document AAA

Before After

", + "controlHtml": "

Document AAA

Before SOURCE_SENTINEL After

", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n.probe {display:block}", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": false, + "expected": { + "pdf": "9f544031ce1ca7ec3bf0f22724c6eb27922a4763deb948316ab568e7c9e8a329", + "png": "1c0368d02c106d934801f456cb1343425331b7830ee4a811d7c3449d7cd77c2e", + "text": [ + "Document AAA Before SOURCE_SENTINEL After" + ] + } + }, + { + "name": "style-block-pre", + "kind": "visible", + "sourceHtml": "

Document AAA

Before  After
", + "controlHtml": "

Document AAA

Before SOURCE_SENTINEL After
", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n.probe {display:block}", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "0ff638f747481f71797696ff4b0c191b4505ca6bb9bf13fb073822d0c59f2947", + "png": "4c9806bbd8cc2dc9121683bdfdc761c414aad2c83d6eb0f3ff39150e631d26b7", + "text": [ + "Document AAA Before SOURCE_SENTINEL After" + ] + } + }, + { + "name": "style-block-caption", + "kind": "visible", + "sourceHtml": "

Document AAA

Before After
Cell
", + "controlHtml": "

Document AAA

Before SOURCE_SENTINEL After
Cell
", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n.probe {display:block}", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "5b10a1e7d1b570481d2f9c64f57af1f373b0d2b1c421695ceb32880356253f5a", + "png": "20286bef12b5dfdf7b152ea3101927425fb5d14db2dd1fb6f4449492d6321b82", + "text": [ + "Document AAA Before SOURCE_SENTINEL After Cell" + ] + } + }, + { + "name": "style-block-list", + "kind": "visible", + "sourceHtml": "

Document AAA

", + "controlHtml": "

Document AAA

", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n.probe {display:block}", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": false, + "expected": { + "pdf": "f58b8fc1c5cbda2ce13b72b67744ab1c742a2510c4a71a9caf01cac4d01617ab", + "png": "d668ab0f0b0cb89a76dac3c38b9779d8bc481e1bcd8944a9b53955ea971e1b1a", + "text": [ + "Document AAA \u2022 Before SOURCE_SENTINEL After" + ] + } + }, + { + "name": "style-contents-paragraph", + "kind": "visible", + "sourceHtml": "

Document AAA

Before After

", + "controlHtml": "

Document AAA

Before SOURCE_SENTINEL After

", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n.probe {display:contents}", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "0ff638f747481f71797696ff4b0c191b4505ca6bb9bf13fb073822d0c59f2947", + "png": "4c9806bbd8cc2dc9121683bdfdc761c414aad2c83d6eb0f3ff39150e631d26b7", + "text": [ + "Document AAA Before SOURCE_SENTINEL After" + ] + } + }, + { + "name": "style-contents-pre", + "kind": "visible", + "sourceHtml": "

Document AAA

Before  After
", + "controlHtml": "

Document AAA

Before SOURCE_SENTINEL After
", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n.probe {display:contents}", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "0ff638f747481f71797696ff4b0c191b4505ca6bb9bf13fb073822d0c59f2947", + "png": "4c9806bbd8cc2dc9121683bdfdc761c414aad2c83d6eb0f3ff39150e631d26b7", + "text": [ + "Document AAA Before SOURCE_SENTINEL After" + ] + } + }, + { + "name": "style-contents-caption", + "kind": "visible", + "sourceHtml": "

Document AAA

Before After
Cell
", + "controlHtml": "

Document AAA

Before SOURCE_SENTINEL After
Cell
", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n.probe {display:contents}", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "5b10a1e7d1b570481d2f9c64f57af1f373b0d2b1c421695ceb32880356253f5a", + "png": "20286bef12b5dfdf7b152ea3101927425fb5d14db2dd1fb6f4449492d6321b82", + "text": [ + "Document AAA Before SOURCE_SENTINEL After Cell" + ] + } + }, + { + "name": "style-contents-list", + "kind": "visible", + "sourceHtml": "

Document AAA

", + "controlHtml": "

Document AAA

", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n.probe {display:contents}", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "1c847135162b5a9f207a0a4d7a7972ce796ff307680cfacd9bb5929191e42ce3", + "png": "47b4f7d2779d8b2036dc988dbc0a88f9a6e21a9a8b1ef80e3a0d7a0d4975defb", + "text": [ + "Document AAA \u2022 Before SOURCE_SENTINEL After" + ] + } + }, + { + "name": "style-initial-paragraph", + "kind": "visible", + "sourceHtml": "

Document AAA

Before After

", + "controlHtml": "

Document AAA

Before SOURCE_SENTINEL After

", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n.probe {display:initial}", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "0ff638f747481f71797696ff4b0c191b4505ca6bb9bf13fb073822d0c59f2947", + "png": "4c9806bbd8cc2dc9121683bdfdc761c414aad2c83d6eb0f3ff39150e631d26b7", + "text": [ + "Document AAA Before SOURCE_SENTINEL After" + ] + } + }, + { + "name": "style-initial-pre", + "kind": "visible", + "sourceHtml": "

Document AAA

Before  After
", + "controlHtml": "

Document AAA

Before SOURCE_SENTINEL After
", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n.probe {display:initial}", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "0ff638f747481f71797696ff4b0c191b4505ca6bb9bf13fb073822d0c59f2947", + "png": "4c9806bbd8cc2dc9121683bdfdc761c414aad2c83d6eb0f3ff39150e631d26b7", + "text": [ + "Document AAA Before SOURCE_SENTINEL After" + ] + } + }, + { + "name": "style-initial-caption", + "kind": "visible", + "sourceHtml": "

Document AAA

Before After
Cell
", + "controlHtml": "

Document AAA

Before SOURCE_SENTINEL After
Cell
", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n.probe {display:initial}", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "5b10a1e7d1b570481d2f9c64f57af1f373b0d2b1c421695ceb32880356253f5a", + "png": "20286bef12b5dfdf7b152ea3101927425fb5d14db2dd1fb6f4449492d6321b82", + "text": [ + "Document AAA Before SOURCE_SENTINEL After Cell" + ] + } + }, + { + "name": "style-initial-list", + "kind": "visible", + "sourceHtml": "

Document AAA

", + "controlHtml": "

Document AAA

", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n.probe {display:initial}", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "1c847135162b5a9f207a0a4d7a7972ce796ff307680cfacd9bb5929191e42ce3", + "png": "47b4f7d2779d8b2036dc988dbc0a88f9a6e21a9a8b1ef80e3a0d7a0d4975defb", + "text": [ + "Document AAA \u2022 Before SOURCE_SENTINEL After" + ] + } + }, + { + "name": "style-unset-paragraph", + "kind": "visible", + "sourceHtml": "

Document AAA

Before After

", + "controlHtml": "

Document AAA

Before SOURCE_SENTINEL After

", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n.probe {display:unset}", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "0ff638f747481f71797696ff4b0c191b4505ca6bb9bf13fb073822d0c59f2947", + "png": "4c9806bbd8cc2dc9121683bdfdc761c414aad2c83d6eb0f3ff39150e631d26b7", + "text": [ + "Document AAA Before SOURCE_SENTINEL After" + ] + } + }, + { + "name": "style-unset-pre", + "kind": "visible", + "sourceHtml": "

Document AAA

Before  After
", + "controlHtml": "

Document AAA

Before SOURCE_SENTINEL After
", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n.probe {display:unset}", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "0ff638f747481f71797696ff4b0c191b4505ca6bb9bf13fb073822d0c59f2947", + "png": "4c9806bbd8cc2dc9121683bdfdc761c414aad2c83d6eb0f3ff39150e631d26b7", + "text": [ + "Document AAA Before SOURCE_SENTINEL After" + ] + } + }, + { + "name": "style-unset-caption", + "kind": "visible", + "sourceHtml": "

Document AAA

Before After
Cell
", + "controlHtml": "

Document AAA

Before SOURCE_SENTINEL After
Cell
", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n.probe {display:unset}", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "5b10a1e7d1b570481d2f9c64f57af1f373b0d2b1c421695ceb32880356253f5a", + "png": "20286bef12b5dfdf7b152ea3101927425fb5d14db2dd1fb6f4449492d6321b82", + "text": [ + "Document AAA Before SOURCE_SENTINEL After Cell" + ] + } + }, + { + "name": "style-unset-list", + "kind": "visible", + "sourceHtml": "

Document AAA

", + "controlHtml": "

Document AAA

", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n.probe {display:unset}", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "1c847135162b5a9f207a0a4d7a7972ce796ff307680cfacd9bb5929191e42ce3", + "png": "47b4f7d2779d8b2036dc988dbc0a88f9a6e21a9a8b1ef80e3a0d7a0d4975defb", + "text": [ + "Document AAA \u2022 Before SOURCE_SENTINEL After" + ] + } + }, + { + "name": "style-inline-attribute", + "kind": "visible", + "sourceHtml": "

Document AAA

Before After

", + "controlHtml": "

Document AAA

Before SOURCE_SENTINEL After

", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "0ff638f747481f71797696ff4b0c191b4505ca6bb9bf13fb073822d0c59f2947", + "png": "4c9806bbd8cc2dc9121683bdfdc761c414aad2c83d6eb0f3ff39150e631d26b7", + "text": [ + "Document AAA Before SOURCE_SENTINEL After" + ] + } + }, + { + "name": "style-revert", + "kind": "hidden", + "sourceHtml": "

Document AAA

Before

After

", + "controlHtml": "

Document AAA

Before

After

", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n.probe {display:block;display:revert}", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "941b22bd5f30bf0f3a46faa6d749ee865eb785dfa623089f3dad0fe7d14a61f7", + "png": "a602dde65015c5ed2945dad9d84381e122adfc7c5577d8ea18bb68e80e471410", + "text": [ + "Document AAA Before After" + ] + } + }, + { + "name": "style-hidden-parent", + "kind": "hidden", + "sourceHtml": "

Document AAA

Before After

", + "controlHtml": "

Document AAA

Before After

", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n.probe {display:inline}", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": false, + "expected": { + "pdf": "17cd57e1c75e11561e6a2328aed0ec906a2e6994397cdde064ab56bc14c90f2e", + "png": "4deab8e90ee41a7be394e225a4b419e922f290d4501446750a892ba99aafd9a3", + "text": [ + "Document AAA Before After" + ] + } + }, + { + "name": "style-hidden-decoration", + "kind": "hidden", + "sourceHtml": "

Document AAA

Before

After

", + "controlHtml": "

Document AAA

Before

After

", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n.probe {padding:30px;border:4px solid red;margin:20px} .probe::before {content:\"UNEXPECTED\"}", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "941b22bd5f30bf0f3a46faa6d749ee865eb785dfa623089f3dad0fe7d14a61f7", + "png": "a602dde65015c5ed2945dad9d84381e122adfc7c5577d8ea18bb68e80e471410", + "text": [ + "Document AAA Before After" + ] + } + }, + { + "name": "style-leading-space", + "kind": "visible", + "sourceHtml": "

Document AAA

After
", + "controlHtml": "

Document AAA

SOURCE_SENTINEL After
", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n.probe {display:inline;color:#751b38} .decorated::before {content:\"Prefix\"} .decorated::after {content:\"Suffix\"}", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "89d52efd9e29c4070ee1804a9342f6cdf3c8c9eeb325acf4d81e70bdb49f6243", + "png": "e12cf00ce6bc040c2aa0ad5a22bbe05ac09f5719d96351ddb157b6023e9d3c36", + "text": [ + "Document AAA Prefix SOURCE_SENTINEL AfterSuffix" + ] + } + }, + { + "name": "style-trailing-space", + "kind": "visible", + "sourceHtml": "

Document AAA

Before
", + "controlHtml": "

Document AAA

Before SOURCE_SENTINEL
", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n.probe {display:inline;color:#751b38} .decorated::before {content:\"Prefix\"} .decorated::after {content:\"Suffix\"}", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "82648411357033aedf6e2659f4d3d2848d61aeeb0776dc78eeebe4e30eeec0b0", + "png": "de90aba95e05b72658a3490a96ce25d6b681aef57ecfb7e0fa1db6106e215e51", + "text": [ + "Document AAA Prefix Before SOURCE_SENTINELSuffix" + ] + } + }, + { + "name": "style-pseudo-space", + "kind": "visible", + "sourceHtml": "

Document AAA

After

", + "controlHtml": "

Document AAA

SOURCE_SENTINEL After

", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n.probe {display:inline;color:#751b38} .decorated::before {content:\"Prefix\"} .decorated::after {content:\"Suffix\"}", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "e46ff578f9e91b4793d7c8cf7803b7499fb3851419e1a7a1bcb266fcc03a6051", + "png": "b300120859a09897a0a7791afdf753b1c3e833021684be4183af709680ad5a7c", + "text": [ + "Document AAA Prefix SOURCE_SENTINEL After Suffix" + ] + } + }, + { + "name": "style-literal-source", + "kind": "visible", + "sourceHtml": "

Document AAA

Before After

", + "controlHtml": "

Document AAA

Before if (1 < 2) & "quoted"; After

", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n.probe {display:inline}", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "4a153ab4eabd901c9b77be59df59e1b2f3df53d7d316cf1c9f041ece3886fcdd", + "png": "4e303f73337fd8bf79b568b1e68148a8b5f780759f69b8b5d73afd2a92ba683c", + "text": [ + "Document AAA Before if (1 < 2) & \"quoted\"; After" + ] + } + }, + { + "name": "hidden-span-paragraph", + "kind": "hidden", + "sourceHtml": "

Document AAA

Before HIDDEN_SENTINEL
LEAK
After

", + "controlHtml": "

Document AAA

Before After

", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "17cd57e1c75e11561e6a2328aed0ec906a2e6994397cdde064ab56bc14c90f2e", + "png": "4deab8e90ee41a7be394e225a4b419e922f290d4501446750a892ba99aafd9a3", + "text": [ + "Document AAA Before After" + ] + } + }, + { + "name": "hidden-span-pre", + "kind": "hidden", + "sourceHtml": "

Document AAA

Before HIDDEN_SENTINEL
LEAK
After
", + "controlHtml": "

Document AAA

Before  After
", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "f97d20870474202e71dace2f2d246f5704a8305cf21f0e579edcf72b43664887", + "png": "f9c59793360f5f60b27dc4130156814133fb96fcc24385d71e9653fbc75591f2", + "text": [ + "Document AAA Before After" + ] + } + }, + { + "name": "hidden-span-caption", + "kind": "hidden", + "sourceHtml": "

Document AAA

Before HIDDEN_SENTINEL
LEAK
After
Cell
", + "controlHtml": "

Document AAA

Before After
Cell
", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "1249da015de7e7e3665b3b02235f26be4869f10a2176695d67923f2feca3931b", + "png": "52cf159fef02af27e765c56fb05036c3b5ba45a81488ff14d9ddd92a33a99462", + "text": [ + "Document AAA Before After Cell" + ] + } + }, + { + "name": "hidden-span-list", + "kind": "hidden", + "sourceHtml": "

Document AAA

", + "controlHtml": "

Document AAA

", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "cc91d796958dc8df00956918bc1922e030ef796595a39dfc9ddc9b1ad1ab6b49", + "png": "e1d51f4165e613fe8358416f58c77db8adae82ba676489059ff402ea6ab5a756", + "text": [ + "Document AAA \u2022 Before After" + ] + } + }, + { + "name": "hidden-span-css-caption", + "kind": "hidden", + "sourceHtml": "

Document AAA

Before HIDDEN_SENTINEL
LEAK
After
Cell
", + "controlHtml": "

Document AAA

Before After
Cell
", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "1249da015de7e7e3665b3b02235f26be4869f10a2176695d67923f2feca3931b", + "png": "52cf159fef02af27e765c56fb05036c3b5ba45a81488ff14d9ddd92a33a99462", + "text": [ + "Document AAA Before After Cell" + ] + } + }, + { + "name": "hidden-span-table-cell", + "kind": "hidden", + "sourceHtml": "

Document AAA

Before HIDDEN_SENTINEL
LEAK
After
", + "controlHtml": "

Document AAA

Before After
", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "3b81e86ebbada8ae848562469d42d7e0d8b9703b235c23ef2c524ab13551d7d9", + "png": "52cb5ae6ffdba415ad630fdb2bc5b56f59cf48800f3d91fb69a7f311407469c2", + "text": [ + "Document AAA Before After" + ] + } + }, + { + "name": "hidden-span-empty-table-cell", + "kind": "hidden", + "sourceHtml": "

Document AAA

Before

HIDDEN_SENTINEL
LEAK

After

", + "controlHtml": "

Document AAA

Before

After

", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "690fef7da0f3008f0708198ed90b9ab713b9a7a83ce7c38692ea52af3edf365a", + "png": "a602dde65015c5ed2945dad9d84381e122adfc7c5577d8ea18bb68e80e471410", + "text": [ + "Document AAA Before After" + ] + } + }, + { + "name": "descendant-selector", + "kind": "hidden", + "sourceHtml": "

Document AAA

Before HIDDEN_SENTINEL After

", + "controlHtml": "

Document AAA

Before After

", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\np.owner > span > .probe {display:none}", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "17cd57e1c75e11561e6a2328aed0ec906a2e6994397cdde064ab56bc14c90f2e", + "png": "4deab8e90ee41a7be394e225a4b419e922f290d4501446750a892ba99aafd9a3", + "text": [ + "Document AAA Before After" + ] + } + }, + { + "name": "hidden-break", + "kind": "hidden", + "sourceHtml": "

Document AAA

Before
After

", + "controlHtml": "

Document AAA

BeforeAfter

", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "cc3e864abc271a1b6a3b5a991736c71777951fefc5e3528ed23c4572e15e5dfd", + "png": "5cbf12604a4352d5ccd68c07497b9b6f5caf5cb38e1511c210ab1c29e7678048", + "text": [ + "Document AAA BeforeAfter" + ] + } + }, + { + "name": "json-script", + "kind": "hidden", + "sourceHtml": "

Document AAA

Before

After

", + "controlHtml": "

Document AAA

Before

After

", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "941b22bd5f30bf0f3a46faa6d749ee865eb785dfa623089f3dad0fe7d14a61f7", + "png": "a602dde65015c5ed2945dad9d84381e122adfc7c5577d8ea18bb68e80e471410", + "text": [ + "Document AAA Before After" + ] + } + }, + { + "name": "external-script", + "kind": "hidden", + "sourceHtml": "

Document AAA

Before

After

", + "controlHtml": "

Document AAA

Before

After

", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "941b22bd5f30bf0f3a46faa6d749ee865eb785dfa623089f3dad0fe7d14a61f7", + "png": "a602dde65015c5ed2945dad9d84381e122adfc7c5577d8ea18bb68e80e471410", + "text": [ + "Document AAA Before After" + ] + } + }, + { + "name": "escaped-literal-control", + "kind": "control", + "sourceHtml": "

Document AAA

<script>VISIBLE_LITERAL</script>
", + "controlHtml": "

Document AAA

<script>VISIBLE_LITERAL</script>
", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": false, + "expected": { + "pdf": "266c1ada69da597c0092e06822e81b827a3b7bbf921e8acb3ffd2362bf341dae", + "png": "1276c1268c80ab9398f7b0c1f7b7fec7e3a8e61eb53e5c6e8744251f28da2e90", + "text": [ + "Document AAA " + ] + } + }, + { + "name": "full-document", + "kind": "hidden", + "sourceHtml": "Visibility probe

Document AAA

Before

After

", + "controlHtml": "Visibility probe

Document AAA

Before

After

", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "941b22bd5f30bf0f3a46faa6d749ee865eb785dfa623089f3dad0fe7d14a61f7", + "png": "a602dde65015c5ed2945dad9d84381e122adfc7c5577d8ea18bb68e80e471410", + "text": [ + "Document AAA Before After" + ] + } + }, + { + "name": "head-source", + "kind": "hidden", + "sourceHtml": "Visibility probe

Document AAA

Before

After

", + "controlHtml": "Visibility probe

Document AAA

Before

After

", + "css": "@page {size:500px 300px;margin:20px}\nhtml,body {margin:0;padding:0;color:black;background:white}\nbody {font-family:Helvetica;font-size:16px;line-height:24px}\nh1 {font-size:18px;line-height:26px;margin:0 0 12px}\np,pre {margin:0} table {border-collapse:collapse} td {padding:0}\n.flex {display:flex;gap:12px} .grid {display:grid;grid-template-columns:1fr 1fr}\n.contents {display:contents} .decorated {color:#173b6b}\n", + "previewDpi": 96, + "pages": 1, + "affectedInPreviousCore": true, + "expected": { + "pdf": "941b22bd5f30bf0f3a46faa6d749ee865eb785dfa623089f3dad0fe7d14a61f7", + "png": "a602dde65015c5ed2945dad9d84381e122adfc7c5577d8ea18bb68e80e471410", + "text": [ + "Document AAA Before After" + ] + } + } + ] +} diff --git a/test/hidden-text.mjs b/test/hidden-text.mjs new file mode 100644 index 0000000..90e7bba --- /dev/null +++ b/test/hidden-text.mjs @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: MIT +import { readFile } from 'node:fs/promises'; + +export const hiddenTextSource = JSON.parse(await readFile(new URL('./fixtures/hidden-text-2.5.24.json', import.meta.url), 'utf8')); +export const hiddenTextFixtures = hiddenTextSource.cases.flatMap(({ name, sourceHtml, controlHtml, css, previewDpi, pages }) => + [['source', sourceHtml], ['control', controlHtml]].map(([variant, html]) => ({ + name: `visibility-${name}-${variant}`, html, css, previewDpi, pages, defaultStyles: false, + }))); diff --git a/tools/browser-requirements.txt b/tools/browser-requirements.txt index 45d854d..d5087e3 100644 --- a/tools/browser-requirements.txt +++ b/tools/browser-requirements.txt @@ -2,3 +2,4 @@ playwright==1.63.0 pypdf==6.19.0 pypdfium2==5.14.0 pillow==12.3.0 +fonttools==4.65.0 diff --git a/tools/prepare-browser-check.mjs b/tools/prepare-browser-check.mjs index 93125af..5682c03 100644 --- a/tools/prepare-browser-check.mjs +++ b/tools/prepare-browser-check.mjs @@ -8,6 +8,7 @@ import { pathToFileURL } from 'node:url'; import { inlineFixtures } from '../test/inline-wrapping.mjs'; import { standardFontFixtures } from '../test/standard-fonts.mjs'; import { engineRegressionFixtures } from '../test/engine-regressions.mjs'; +import { hiddenTextFixtures, hiddenTextSource } from '../test/hidden-text.mjs'; const root = resolve('.'); const out = join(root, 'output/browser-verification'); @@ -42,6 +43,7 @@ const inputs = { invoice: await fixture('invoice'), report: await fixture('repor ...Object.fromEntries(inlineFixtures.map(({ name, pages, ...input }) => [name, { ...input, previewDpi: 96 }])), ...Object.fromEntries(standardFontFixtures.map(({ name, font, pages, ...input }) => [name, { ...input, previewDpi: 72 }])), ...Object.fromEntries(engineRegressionFixtures.map(({ name, pages, ...input }) => [name, input])), + ...Object.fromEntries(hiddenTextFixtures.map(({ name, pages, ...input }) => [name, input])), custom: { html: '

Local asset test

Integral: ⨌

', css: "body {font-family:Inter,'Noto Sans Math'}", previewDpi: 72, fontFiles: ['NotoSansMath-Regular.ttf'], @@ -73,6 +75,14 @@ for (const { name, engine, legacy } of styleFixtures.cases) { assert.deepEqual(actual.previews, [expected.png], name + ': independent preview baseline'); } } +assert.equal(pkg.fullbleed.engineVersion, hiddenTextSource.engineVersion); +for (const { name, expected } of hiddenTextSource.cases) { + for (const variant of ['source', 'control']) { + const actual = baselines[`visibility-${name}-${variant}`]; + assert.equal(actual.pdf, expected.pdf, name + ': core visible-content PDF'); + assert.deepEqual(actual.previews, [expected.png], name + ': core finalized preview'); + } +} for (const name of ['index.html', 'harness.js']) await copyFile('test/browser/' + name, join(site, name)); const record = { package: pkg.version, engine: pkg.fullbleed.engineVersion, installedTarballSha256: pack.tarballSha256, ssrImportSafe: true, copyCommand: JSON.parse(copy.stdout), baselines }; diff --git a/tools/render-engine-regressions.mjs b/tools/render-engine-regressions.mjs index 75fb8b2..db0d675 100644 --- a/tools/render-engine-regressions.mjs +++ b/tools/render-engine-regressions.mjs @@ -8,7 +8,8 @@ import { parseArgs } from 'node:util'; import { loadEngineRegressionFixtures } from '../test/engine-regressions.mjs'; const root = fileURLToPath(new URL('../', import.meta.url)); -const { values } = parseArgs({ options: { 'package-root': { type: 'string' }, out: { type: 'string' } } }); +const { values } = parseArgs({ options: { 'package-root': { type: 'string' }, out: { type: 'string' }, + 'decoration-only': { type: 'boolean', default: false } } }); let packageRoot = values['package-root'], packed; if (!packageRoot) { packed = JSON.parse(await readFile(join(root, 'output/pack-verification/verification.json'), 'utf8')); @@ -27,9 +28,10 @@ await mkdir(join(out, '..'), { recursive: true }); await mkdir(out); const report = { schema: 'fullbleed.javascript.engine-regressions.renders.v1', ok: false, packageVersion: version, engineVersion, node: process.version, platform: process.platform, + decorationOnly: values['decoration-only'], wasmSha256: build.files['dist/engine.wasm'].sha256, installedTarballSha256: packed?.tarballSha256 ?? null, cases: [] }; try { - const fixtures = await loadEngineRegressionFixtures(); + const fixtures = await loadEngineRegressionFixtures({ decorationOnly: values['decoration-only'] }); for (const isolation of ['worker', 'process']) { for (const { name, pages, ...input } of fixtures) { const directory = isolation + '-' + name; diff --git a/tools/render-hidden-text.mjs b/tools/render-hidden-text.mjs new file mode 100644 index 0000000..6f5c6bf --- /dev/null +++ b/tools/render-hidden-text.mjs @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: MIT +import assert from 'node:assert/strict'; +import { createHash } from 'node:crypto'; +import { mkdir, readFile, writeFile } from 'node:fs/promises'; +import { join, resolve } from 'node:path'; +import { fileURLToPath, pathToFileURL } from 'node:url'; +import { parseArgs } from 'node:util'; +import { hiddenTextSource } from '../test/hidden-text.mjs'; + +const root = fileURLToPath(new URL('../', import.meta.url)); +const { values } = parseArgs({ options: { + 'package-root': { type: 'string' }, out: { type: 'string' }, + 'negative-control': { type: 'boolean', default: false }, +} }); +let packageRoot = values['package-root'], packed; +if (!packageRoot) { + packed = JSON.parse(await readFile(join(root, 'output/pack-verification/verification.json'), 'utf8')); + assert(packed.ok); + packageRoot = join(root, 'output/pack-verification', packed.installDirectory, 'node_modules/fullbleed'); +} +packageRoot = resolve(packageRoot); +const { renderPdf, version, engineVersion } = await import(pathToFileURL(join(packageRoot, 'src/index.js')).href); +const build = JSON.parse(await readFile(join(packageRoot, 'dist/build.json'), 'utf8')); +const hash = bytes => createHash('sha256').update(bytes).digest('hex'); +assert.equal(build.packageVersion, version); +assert.equal(build.engineVersion, engineVersion); +assert.equal(hash(await readFile(join(packageRoot, 'dist/engine.wasm'))), build.files['dist/engine.wasm'].sha256); +if (values['negative-control']) { + assert.equal(version, '0.5.0'); + assert.equal(engineVersion, '2.5.22'); +} else { + assert.equal(engineVersion, hiddenTextSource.engineVersion); +} +const out = resolve(values.out ?? join(root, 'output/hidden-text')); +await mkdir(join(out, '..'), { recursive: true }); +await mkdir(out); +const report = { schema: 'fullbleed.javascript.hidden-text.renders.v1', ok: false, + packageVersion: version, engineVersion, node: process.version, platform: process.platform, + negativeControl: values['negative-control'], sourceCommit: hiddenTextSource.sourceCommit, + fixtureSha256: hash(await readFile(join(root, 'test/fixtures/hidden-text-2.5.24.json'))), + wasmSha256: build.files['dist/engine.wasm'].sha256, + installedTarballSha256: packed?.tarballSha256 ?? null, cases: [], failures: [] }; +try { + for (const isolation of ['worker', 'process']) { + for (const fixture of hiddenTextSource.cases) { + const folder = join(out, isolation + '-' + fixture.name); + await mkdir(folder); + await writeFile(join(folder, 'input.css'), fixture.css); + const row = { name: fixture.name, isolation, directory: isolation + '-' + fixture.name, + affectedInPreviousCore: fixture.affectedInPreviousCore, variants: {} }; + for (const [variant, html] of [['source', fixture.sourceHtml], ['control', fixture.controlHtml]]) { + await writeFile(join(folder, variant + '.html'), html); + const options = { html, css: fixture.css, previewDpi: fixture.previewDpi, defaultStyles: false, isolation }; + const result = await renderPdf(options); + assert.equal(result.engineVersion, engineVersion); + assert.equal(result.pages, fixture.pages); + assert.equal(result.missingGlyphs, 0); + assert.equal(result.previews.length, fixture.pages); + const repeated = await renderPdf(options); + assert.deepEqual(repeated.pdf, result.pdf, fixture.name + ': repeated PDF'); + assert.deepEqual(repeated.previews, result.previews, fixture.name + ': repeated previews'); + await writeFile(join(folder, variant + '.pdf'), result.pdf); + await writeFile(join(folder, variant + '.png'), result.previews[0]); + row.variants[variant] = { pdf: hash(result.pdf), png: hash(result.previews[0]), + pages: result.pages, repeatPdfAndPreviewBytesIdentical: true }; + } + row.controlMatchesCore = row.variants.control.pdf === fixture.expected.pdf + && row.variants.control.png === fixture.expected.png; + row.sourceMatchesControl = row.variants.source.pdf === row.variants.control.pdf + && row.variants.source.png === row.variants.control.png; + row.ok = row.controlMatchesCore && (values['negative-control'] + ? row.sourceMatchesControl === !fixture.affectedInPreviousCore : row.sourceMatchesControl); + if (isolation === 'process') { + const worker = report.cases.find(item => item.name === row.name && item.isolation === 'worker'); + assert.deepEqual(row.variants, worker.variants, row.name + ': worker/process output'); + } + report.cases.push(row); + if (!row.ok) report.failures.push({ name: row.name, isolation, + controlMatchesCore: row.controlMatchesCore, sourceMatchesControl: row.sourceMatchesControl }); + if (report.cases.length % 20 === 0) { + console.log(JSON.stringify({ completed: report.cases.length, isolation, lastCase: row.name })); + } + } + } + assert.equal(report.cases.length, hiddenTextSource.cases.length * 2); + report.ok = report.failures.length === 0; +} finally { + await writeFile(join(out, 'renders.json'), JSON.stringify(report, null, 2) + '\n'); +} +console.log(JSON.stringify({ ok: report.ok, packageVersion: version, engineVersion, + negativeControl: report.negativeControl, cases: report.cases.length, failures: report.failures })); +assert(report.ok, 'Hidden-text output differs from the independently retained controls'); diff --git a/tools/verify-engine-regressions.py b/tools/verify-engine-regressions.py index df0e080..33540ab 100644 --- a/tools/verify-engine-regressions.py +++ b/tools/verify-engine-regressions.py @@ -13,13 +13,34 @@ from PIL import Image, ImageChops, ImageFilter from pypdf import PdfReader +from pypdf.generic import ContentStream +from fontTools.ttLib import TTFont import pypdfium2 as pdfium ROOT = Path(__file__).resolve().parents[1] SOURCE = ROOT / 'test/fixtures/engine-2.5.22.json' +DECORATION_SOURCE = ROOT / 'test/fixtures/decoration-thickness.json' COLORS = {'initial': (188, 108, 37), 'border': (224, 64, 32), 'background': (255, 224, 128)} +def decoration_widths(page, reader): + widths=[] + def visit(stream, resources, width=1.0, color=None, depth=0): + assert depth < 12 + stack=[] + for args,op in ContentStream(stream,reader).operations: + if op==b'q':stack.append((width,color)) + elif op==b'Q':width,color=stack.pop() + elif op==b'w':width=float(args[0]) + elif op==b'RG':color=tuple(map(float,args)) + elif op in (b'S',b's') and color and color[0]>.7 and color[1:]==(0.,0.):widths.append(width) + elif op==b'Do': + obj=resources['/XObject'][args[0]].get_object() + if obj.get('/Subtype')=='/Form':visit(obj,obj.get('/Resources',resources),width,color,depth+1) + visit(page.get_contents(),page['/Resources']) + return widths + + def color_bounds(image, color): difference = ImageChops.difference(image.convert('RGB'), Image.new('RGB', image.size, color)) channels = [channel.point(lambda value: 255 if value < 24 else 0) for channel in difference.split()] @@ -99,6 +120,20 @@ def check(name, ok, **details): delta = max(abs(a-b) for a,b in zip(bounds,target)) if bounds is not None and target is not None else 0 if bounds == target else None check(mode+' Chrome '+name+' bounds', delta is not None and delta <= 1.5, page=index, actualPt=bounds, expectedPt=target, maximumDeltaPt=delta) + elif case['family'] == 'decoration': + with TTFont(ROOT/'assets/fonts/Inter-Variable.ttf') as font: + preferred=font['post'].underlineThickness + units=font['head'].unitsPerEm + size=expected['fontSizePx'] + wanted=max(.75,int(size*.1)*.75) if expected['variant']=='auto' else max(.75,round(size*.75*preferred/units,3)) + check('OpenType metric expectation', wanted==expected['widthPt']) + widths=decoration_widths(reader.pages[0],reader) + check('colored decoration stroke count',len(widths)==expected['strokes'],actual=widths) + check('font-selected or explicit stroke widths',all(abs(value-wanted)<=.002 for value in widths),actual=widths,expectedPt=wanted) + check('decoration text',' '.join(reader.pages[0].extract_text().split())==expected['text']) + for mode,images in [('preview',native),('pdfium',independent)]: + data=images[0].tobytes() + check(mode+' has red decoration',any(r>150 and g<60 and b<60 for r,g,b in zip(data[0::3],data[1::3],data[2::3]))) else: raise ValueError('Unknown fixture family: '+case['family']) finally: @@ -112,33 +147,44 @@ def main(): parser.add_argument('out',type=Path,nargs='?',default=ROOT/'output/engine-regressions') parser.add_argument('--browser',action='store_true',help='Read actual downloads and the browser verification record in OUT') parser.add_argument('--negative-control',action='store_true',help='Require public npm 0.4.0 to fail at least one case in each fix family') + parser.add_argument('--negative-decoration-control',action='store_true',help='Require public npm 0.5.0 to fail the large from-font cases and preserve all explicit/automatic controls') args=parser.parse_args() - assert not (args.browser and args.negative_control) + assert not (args.browser and (args.negative_control or args.negative_decoration_control)) + assert not (args.negative_control and args.negative_decoration_control) source=json.loads(SOURCE.read_text(encoding='utf-8')) font=source['font'] assert sha256((ROOT/'test/fonts'/font['file']).read_bytes()).hexdigest()==font['sha256'] assert sha256((ROOT/'test/fonts'/font['license']).read_bytes()).hexdigest()==font['licenseSha256'] - cases={case['name']:case for case in source['cases']} + decoration=json.loads(DECORATION_SOURCE.read_text(encoding='utf-8')) + assert sha256((ROOT/'assets/fonts'/decoration['font']['file']).read_bytes()).hexdigest()==decoration['font']['sha256'] + cases={case['name']:case for case in [*source['cases'],*decoration['cases']]} out=args.out.resolve() if args.browser: origin=json.loads((out/'verification.json').read_text(encoding='utf-8')) assert origin['ok'] + downloads={item['file']:item for item in origin['documents']} + assert len(downloads)==len(origin['documents']) records=[dict(name=name,isolation='browser',directory=name,pdfFile=name+'.pdf',previewFiles=[name+f'-{i+1}.png' for i in range(case['pages'])]) for name,case in cases.items()] package,engine=origin['package'],origin['engine'] else: origin=json.loads((out/'renders.json').read_text(encoding='utf-8')) assert origin['ok'] + if origin.get('decorationOnly'): + cases={name:case for name,case in cases.items() if case['family']=='decoration'} records=origin['cases'] assert {(r['name'],r['isolation']) for r in records}=={(name,mode) for name in cases for mode in ('worker','process')} assert len(records)==len(cases)*2 package,engine=origin['packageVersion'],origin['engineVersion'] if args.negative_control: assert (package,engine)==('0.4.0','2.5.11') + if args.negative_decoration_control: + assert (package,engine)==('0.5.0','2.5.22') and origin.get('decorationOnly') report=dict(ok=False,checkedAt=datetime.now(timezone.utc).isoformat(),packageVersion=package,engineVersion=engine, sourceSha256=sha256(SOURCE.read_bytes()).hexdigest(),installedTarballSha256=origin.get('installedTarballSha256'), - negativeControl=args.negative_control,browser=args.browser, - versions={name:version(name) for name in ('pypdf','pypdfium2','pillow')},cases=[], - scope='Only retained emitted labels, interior color probes and Chrome geometry. Browser equality follows the separate browser report; no general CSS or standards certification.') + decorationSourceSha256=sha256(DECORATION_SOURCE.read_bytes()).hexdigest(), + negativeControl=args.negative_control,negativeDecorationControl=args.negative_decoration_control,browser=args.browser, + versions={name:version(name) for name in ('pypdf','pypdfium2','pillow','fonttools')},cases=[], + scope='Retained emitted labels, interior colors, Chrome geometry, and independently measured font-decoration strokes. Browser equality follows the separate browser report; no general CSS or standards certification.') try: for record in records: case=cases[record['name']] @@ -146,6 +192,11 @@ def main(): destination.mkdir(exist_ok=True) pdf=out/record['pdfFile'] if args.browser else destination/'document.pdf' previews=[out/file for file in record['previewFiles']] if args.browser else [destination/f'preview-{i+1}.png' for i in range(record['pages'])] + if args.browser: + for path in [pdf,*previews]: + assert path.name in downloads, 'Missing browser download: '+path.name + receipt=downloads[path.name] + assert path.stat().st_size==receipt['bytes'] and sha256(path.read_bytes()).hexdigest()==receipt['sha256'] if not args.browser: assert sha256(pdf.read_bytes()).hexdigest()==record['pdfSha256'] assert [sha256(p.read_bytes()).hexdigest() for p in previews]==record['previewsSha256'] @@ -156,12 +207,19 @@ def main(): report['cases'].append(dict(name=case['name'],family=case['family'],isolation=record['isolation'],pdfSha256=sha256(pdf.read_bytes()).hexdigest(),**result)) failed={r['family'] for r in report['cases'] if not r['ok']} report['failedFamilies']=sorted(failed) - report['ok']=(failed=={c['family'] for c in cases.values()} and not any('error' in r for r in report['cases'])) if args.negative_control else not failed + if args.negative_decoration_control: + expected_failed={(name,mode) for name,c in cases.items() + if c['expected']['variant']=='font' and c['expected']['fontSizePx']==40 + for mode in ('worker','process')} + observed_failed={(r['name'],r['isolation']) for r in report['cases'] if not r['ok']} + report['ok']=observed_failed==expected_failed and not any('error' in r for r in report['cases']) + else: + report['ok']=(failed=={c['family'] for c in cases.values()} and not any('error' in r for r in report['cases'])) if args.negative_control else not failed assert report['ok'], '; '.join(r['isolation']+'/'+r['name'] for r in report['cases'] if not r['ok']) finally: filename='engine-regressions.json' if args.browser else 'verification.json' (out/filename).write_text(json.dumps(report,indent=2)+'\n',encoding='utf-8') - print(json.dumps(dict(ok=report['ok'],packageVersion=package,engineVersion=engine,cases=len(report['cases']),negativeControl=args.negative_control,failedFamilies=report['failedFamilies']))) + print(json.dumps(dict(ok=report['ok'],packageVersion=package,engineVersion=engine,cases=len(report['cases']),negativeControl=args.negative_control,negativeDecorationControl=args.negative_decoration_control,failedFamilies=report['failedFamilies']))) if __name__=='__main__': diff --git a/tools/verify-hidden-text.py b/tools/verify-hidden-text.py new file mode 100644 index 0000000..82beabd --- /dev/null +++ b/tools/verify-hidden-text.py @@ -0,0 +1,97 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: MIT +"""Inspect installed-package or actual browser downloads for hidden HTML text.""" +from datetime import datetime, timezone +from hashlib import sha256 +from importlib.metadata import version +from pathlib import Path +import argparse +import json + +from PIL import Image +from pypdf import PdfReader +import pypdfium2 as pdfium + +ROOT=Path(__file__).resolve().parents[1] + + +def inspect(pdf, preview, destination): + reader=PdfReader(pdf) + text=[' '.join(page.extract_text().split()) for page in reader.pages] + sizes=[[float(page.mediabox.width),float(page.mediabox.height)] for page in reader.pages] + with Image.open(preview) as image: + rgb=image.convert('RGB') + native={'size':list(rgb.size),'sha256':sha256(rgb.tobytes()).hexdigest()} + rgb.close() + rasters=[] + with pdfium.PdfDocument(pdf) as document: + for index,page in enumerate(document): + bitmap=page.render(scale=1) + image=bitmap.to_pil().convert('RGB') + rasters.append({'size':list(image.size),'sha256':sha256(image.tobytes()).hexdigest()}) + image.save(destination/f'{pdf.stem}-pdfium-{index+1}.png') + image.close();bitmap.close();page.close() + return {'pdf':sha256(pdf.read_bytes()).hexdigest(),'png':sha256(preview.read_bytes()).hexdigest(), + 'text':text,'page_sizes':sizes,'native':native,'pdfium':rasters} + + +def main(): + parser=argparse.ArgumentParser(description=__doc__) + parser.add_argument('out',type=Path,nargs='?',default=ROOT/'output/hidden-text') + parser.add_argument('--browser',action='store_true') + args=parser.parse_args() + out=args.out.resolve() + fixture=ROOT/'test/fixtures/hidden-text-2.5.24.json' + source=json.loads(fixture.read_text(encoding='utf-8')) + origin=json.loads((out/('verification.json' if args.browser else 'renders.json')).read_text(encoding='utf-8')) + assert origin['ok'] + if args.browser: + downloads={item['file']:item for item in origin['documents']} + assert len(downloads)==len(origin['documents']) + else: + assert origin['fixtureSha256']==sha256(fixture.read_bytes()).hexdigest(), 'Rendered fixture identity changed' + expected_cases={(case['name'],mode) for case in source['cases'] for mode in ('worker','process')} + actual_cases={(case['name'],case['isolation']) for case in origin['cases']} + assert actual_cases==expected_cases and len(origin['cases'])==len(expected_cases), 'Incomplete rendered case coverage' + negative=origin.get('negativeControl',False) + assert not (negative and args.browser) + if negative:assert (origin['packageVersion'],origin['engineVersion'])==('0.5.0','2.5.22') + else:assert origin['engine' if args.browser else 'engineVersion']==source['engineVersion'] + report={'ok':False,'checked_at':datetime.now(timezone.utc).isoformat(),'negativeControl':negative, + 'browser':args.browser,'fixture_sha256':sha256(fixture.read_bytes()).hexdigest(), + 'versions':{name:version(name) for name in ('pypdf','pypdfium2','pillow')},'cases':[],'failures':[], + 'scope':'Independent text, page size, and PNG decoding; PDFium source/control pixels are compared within this platform only.'} + for isolation in (['browser'] if args.browser else ['worker','process']): + for case in source['cases']: + destination=out/('visibility-'+case['name'] if args.browser else isolation+'-'+case['name']) + destination.mkdir(exist_ok=True) + row={'name':case['name'],'isolation':isolation,'variants':{}} + for variant in ['source','control']: + stem='visibility-'+case['name']+'-'+variant if args.browser else variant + parent=out if args.browser else destination + pdf=parent/(stem+'.pdf') + preview=parent/(stem+('-1.png' if args.browser else '.png')) + if args.browser: + for path in [pdf,preview]: + assert path.name in downloads, 'Missing browser download: '+path.name + receipt=downloads[path.name] + assert path.stat().st_size==receipt['bytes'] and sha256(path.read_bytes()).hexdigest()==receipt['sha256'] + actual=inspect(pdf,preview,destination) + expected=case['expected'] + actual['matches_expected']=(actual['pdf']==expected['pdf'] and actual['png']==expected['png'] + and actual['text']==expected['text'] and actual['page_sizes']==[[375.0,225.0]] + and actual['native']['size']==[500,300]) + row['variants'][variant]=actual + a,b=row['variants']['source'],row['variants']['control'] + row['source_control_pixels_match']=a['pdfium']==b['pdfium'] and a['native']==b['native'] + row['ok']=b['matches_expected'] and (negative and case['affectedInPreviousCore'] + or a['matches_expected'] and row['source_control_pixels_match']) + if not row['ok']:report['failures'].append({'name':row['name'],'isolation':isolation}) + report['cases'].append(row) + report['ok']=not report['failures'] + (out/'hidden-text-verification.json').write_text(json.dumps(report,indent=2)+'\n',encoding='utf-8') + print(json.dumps({key:value for key,value in report.items() if key!='cases'})) + assert report['ok'],report['failures'] + + +if __name__=='__main__':main() diff --git a/tools/verify-native.mjs b/tools/verify-native.mjs index 6589fd6..6407da3 100644 --- a/tools/verify-native.mjs +++ b/tools/verify-native.mjs @@ -10,6 +10,7 @@ import { gradientFixtures } from '../test/gradients.mjs'; import { inlineFixtures } from '../test/inline-wrapping.mjs'; import { standardFontFixtures } from '../test/standard-fonts.mjs'; import { loadEngineRegressionFixtures } from '../test/engine-regressions.mjs'; +import { hiddenTextFixtures } from '../test/hidden-text.mjs'; const root = fileURLToPath(new URL('../', import.meta.url)); const engine = join(root, 'engine'); @@ -26,6 +27,7 @@ fixtures.push(...gradientFixtures); fixtures.push(...inlineFixtures); fixtures.push(...standardFontFixtures); fixtures.push(...await loadEngineRegressionFixtures()); +fixtures.push(...hiddenTextFixtures); for (const [name, pages] of [['invoice', 1], ['report', 3]]) { fixtures.push({ name, pages, html: await readFile(join(root, 'examples', name + '.html'), 'utf8'), css: await readFile(join(root, 'examples', name + '.css'), 'utf8') }); } @@ -47,11 +49,12 @@ for (const fixture of fixtures) { await writeFile(target, bytes); } await writeFile(join(directory, 'input.html'), fixture.html); - await writeFile(join(directory, 'style.css'), 'body { font-family: Inter; }\n' + fixture.css); + await writeFile(join(directory, 'style.css'), (fixture.defaultStyles === false ? '' : 'body { font-family: Inter; }\n') + fixture.css); const dpi = fixture.previewDpi ?? 96; const native = spawnSync(executable, ['1000', String(dpi), 'reject', ...names], { cwd: directory, encoding: 'utf8', timeout: 60000 }); assert.equal(native.status, 0, native.stderr); - const options = { html: fixture.html, css: fixture.css, previewDpi: dpi, fonts: fixture.fonts, assets: fixture.assets }; + const options = { html: fixture.html, css: fixture.css, previewDpi: dpi, fonts: fixture.fonts, assets: fixture.assets, + ...(fixture.defaultStyles === undefined ? {} : { defaultStyles: fixture.defaultStyles }) }; const wasi = await renderPdf(options); assert.equal(wasi.pages, fixture.pages); assert.equal(wasi.missingGlyphs, 0); From 1f266703ba0c3ccd259b96e55eeaf5db6583f48c Mon Sep 17 00:00:00 2001 From: krflol Date: Wed, 14 Oct 2026 22:53:56 -0500 Subject: [PATCH 2/3] Require hidden-text and decoration evidence before npm publication --- docs/engine-2.5.24.md | 5 +++ test/test_release.py | 84 +++++++++++++++++++++++++++++++++++++ tools/verify-hidden-text.py | 3 ++ tools/verify-release.py | 42 ++++++++++++++++++- 4 files changed, 133 insertions(+), 1 deletion(-) diff --git a/docs/engine-2.5.24.md b/docs/engine-2.5.24.md index b3bd8f6..4a034d8 100644 --- a/docs/engine-2.5.24.md +++ b/docs/engine-2.5.24.md @@ -48,6 +48,11 @@ package's defects, use `render-hidden-text.mjs --negative-control`, or render engine regressions with `--decoration-only` and verify them with `verify-engine-regressions.py --negative-decoration-control`. +The publication gate requires both Windows and Linux evidence for the complete +fixture sets, all three browser reports, and native/WASI comparisons for every +source and control. It rejects stale fixture hashes, a different tarball, +negative-control reports, and incomplete coverage. + Independent PDFium source/control pixels are compared within each platform. These checks do not claim identical PDFium rasterization across operating systems, general browser pixel parity, or PDF standards conformance. diff --git a/test/test_release.py b/test/test_release.py index 569fb92..cec6078 100644 --- a/test/test_release.py +++ b/test/test_release.py @@ -160,6 +160,90 @@ def test_engine_release_rejects_browser_or_native_evidence_gaps(self): with self.assertRaisesRegex(ValueError, 'Native/WASI'): self.verify(version='0.4.1') + def hidden_text_release(self): + self.engine_release() + self.release['tag_name'] = 'v0.5.1' + decoration = release_check.DECORATION_SOURCE.read_bytes() + decoration_sha = hashlib.sha256(decoration).hexdigest() + decoration_cases = json.loads(decoration)['cases'] + hidden = release_check.HIDDEN_TEXT_SOURCE.read_bytes() + hidden_sha = hashlib.sha256(hidden).hexdigest() + hidden_fixture = json.loads(hidden) + engine = hidden_fixture['engineVersion'] + self.report.update(package_version='0.5.1', engine_version=engine, package_sha256='d' * 64) + for item in self.report['engine_regressions']: + item.update(package_version='0.5.1', engine_version=engine, + cases=item['cases'] + 2 * len(decoration_cases), + families=sorted(set(item['families']) | {'decoration'}), + decoration_source_sha256=decoration_sha, tarball_sha256='d' * 64, negative_control=False) + self.report['hidden_text'] = [dict(platform=platform, ok=True, package_version='0.5.1', + engine_version=engine, source_sha256=hidden_sha, cases=2 * len(hidden_fixture['cases']), + tarball_sha256='d' * 64, negative_control=False) for platform in ['win32', 'linux']] + for browser in self.report['browser_matrix']: + browser.update(package_version='0.5.1', checks=816, + engine_regression_cases=browser['engine_regression_cases'] + len(decoration_cases), + decoration_source_sha256=decoration_sha, hidden_text_ok=True, + hidden_text_cases=len(hidden_fixture['cases']), hidden_text_source_sha256=hidden_sha, + hidden_text_negative_control=False) + names = [case['name'] for case in decoration_cases] + names += [f'visibility-{case["name"]}-{variant}' for case in hidden_fixture['cases'] + for variant in ['source', 'control']] + self.report['native_wasi_fixtures'] += [dict(name=name, nativeWasiPdfEqual=True, + nativeWasiPreviewsEqual=True) for name in names] + + def test_hidden_text_release_accepts_complete_current_evidence(self): + self.hidden_text_release() + self.verify(version='0.5.1') + + def test_hidden_text_release_rejects_missing_platform_or_wrong_evidence(self): + for change in [{'ok': False}, {'cases': 94}, {'source_sha256': '0' * 64}, + {'engine_version': '2.5.22'}, {'package_version': '0.5.0'}, + {'tarball_sha256': '0' * 64}, {'negative_control': True}, {'platform': 'linux'}]: + with self.subTest(change=change): + self.hidden_text_release() + self.report['hidden_text'][0].update(change) + with self.assertRaisesRegex(ValueError, 'hidden-text|Hidden-text'): + self.verify(version='0.5.1') + self.hidden_text_release() + self.report['hidden_text'].pop() + with self.assertRaisesRegex(ValueError, 'Windows and Linux hidden-text'): + self.verify(version='0.5.1') + + def test_hidden_text_release_rejects_browser_or_native_gaps(self): + for change in [{'hidden_text_ok': False}, {'hidden_text_cases': 0}, + {'hidden_text_source_sha256': '0' * 64}, {'hidden_text_negative_control': True}]: + with self.subTest(change=change): + self.hidden_text_release() + self.report['browser_matrix'][0].update(change) + with self.assertRaisesRegex(ValueError, 'Browser hidden-text'): + self.verify(version='0.5.1') + for mutation in ['missing', 'changed', 'duplicate']: + with self.subTest(mutation=mutation): + self.hidden_text_release() + fixtures = self.report['native_wasi_fixtures'] + if mutation == 'missing': fixtures.pop() + elif mutation == 'changed': fixtures[-1]['nativeWasiPreviewsEqual'] = False + else: fixtures.append(dict(fixtures[-1])) + with self.assertRaisesRegex(ValueError, 'Native/WASI hidden-text'): + self.verify(version='0.5.1') + + def test_hidden_text_release_rejects_stale_decoration_or_browser_coverage(self): + for change in [{'decoration_source_sha256': '0' * 64}, {'tarball_sha256': '0' * 64}, + {'negative_control': True}]: + with self.subTest(change=change): + self.hidden_text_release() + self.report['engine_regressions'][0].update(change) + with self.assertRaisesRegex(ValueError, 'Decoration evidence'): + self.verify(version='0.5.1') + self.hidden_text_release() + self.report['browser_matrix'][0]['decoration_source_sha256'] = '0' * 64 + with self.assertRaisesRegex(ValueError, 'Browser decoration'): + self.verify(version='0.5.1') + self.hidden_text_release() + self.report['browser_matrix'][0]['checks'] = 180 + with self.assertRaisesRegex(ValueError, 'Browser verification is incomplete'): + self.verify(version='0.5.1') + if __name__ == '__main__': unittest.main() diff --git a/tools/verify-hidden-text.py b/tools/verify-hidden-text.py index 82beabd..d1a9da6 100644 --- a/tools/verify-hidden-text.py +++ b/tools/verify-hidden-text.py @@ -58,6 +58,9 @@ def main(): if negative:assert (origin['packageVersion'],origin['engineVersion'])==('0.5.0','2.5.22') else:assert origin['engine' if args.browser else 'engineVersion']==source['engineVersion'] report={'ok':False,'checked_at':datetime.now(timezone.utc).isoformat(),'negativeControl':negative, + 'packageVersion':origin['package' if args.browser else 'packageVersion'], + 'engineVersion':origin['engine' if args.browser else 'engineVersion'], + 'installedTarballSha256':origin.get('installedTarballSha256'), 'browser':args.browser,'fixture_sha256':sha256(fixture.read_bytes()).hexdigest(), 'versions':{name:version(name) for name in ('pypdf','pypdfium2','pillow')},'cases':[],'failures':[], 'scope':'Independent text, page size, and PNG decoding; PDFium source/control pixels are compared within this platform only.'} diff --git a/tools/verify-release.py b/tools/verify-release.py index 628cb3f..c344b28 100644 --- a/tools/verify-release.py +++ b/tools/verify-release.py @@ -15,6 +15,8 @@ REPO = 'fullbleed-engine/fullbleed-node' ENGINE_REGRESSION_SOURCE = Path(__file__).resolve().parents[1] / 'test/fixtures/engine-2.5.22.json' +DECORATION_SOURCE = ENGINE_REGRESSION_SOURCE.with_name('decoration-thickness.json') +HIDDEN_TEXT_SOURCE = ENGINE_REGRESSION_SOURCE.with_name('hidden-text-2.5.24.json') def require(condition, message): @@ -49,7 +51,7 @@ def verify_binding(version, run_id, run, release, tag_commit, report): require(len(browsers) == 3 and {item.get('browser') for item in browsers} == {'chrome', 'firefox', 'webkit'}, 'Release does not retain the required browser matrix') release_version = tuple(map(int, version.split('.'))) - required_browser_checks = 144 if release_version >= (0, 4, 1) else 105 if release_version >= (0, 3, 2) else 66 if release_version >= (0, 3, 1) else 30 + required_browser_checks = 816 if release_version >= (0, 5, 1) else 144 if release_version >= (0, 4, 1) else 105 if release_version >= (0, 3, 2) else 66 if release_version >= (0, 3, 1) else 30 require(all(item.get('ok') is True and item.get('package_version') == version and item.get('checks', 0) >= required_browser_checks for item in browsers), 'Browser verification is incomplete or tests another package') if release_version >= (0, 4, 0): @@ -60,6 +62,10 @@ def verify_binding(version, run_id, run, release, tag_commit, report): source = ENGINE_REGRESSION_SOURCE.read_bytes() source_sha = hashlib.sha256(source).hexdigest() cases = json.loads(source)['cases'] + if release_version >= (0, 5, 1): + decoration = DECORATION_SOURCE.read_bytes() + decoration_sha = hashlib.sha256(decoration).hexdigest() + cases += json.loads(decoration)['cases'] names = {case['name'] for case in cases} families = {case['family'] for case in cases} regressions = report.get('engine_regressions', []) @@ -77,6 +83,40 @@ def verify_binding(version, run_id, run, release, tag_commit, report): require(names.issubset(native) and all(native[name].get('nativeWasiPdfEqual') is True and native[name].get('nativeWasiPreviewsEqual') is True for name in names), 'Native/WASI engine regression comparison is incomplete') + if release_version >= (0, 5, 1): + require(all(item.get('decoration_source_sha256') == decoration_sha + and item.get('tarball_sha256') == report.get('package_sha256') + and item.get('negative_control') is False for item in regressions), + 'Decoration evidence targets another fixture, tarball, or negative control') + require(all(item.get('decoration_source_sha256') == decoration_sha for item in browsers), + 'Browser decoration evidence targets another fixture') + hidden_source = HIDDEN_TEXT_SOURCE.read_bytes() + hidden_sha = hashlib.sha256(hidden_source).hexdigest() + hidden_fixture = json.loads(hidden_source) + hidden_cases = hidden_fixture['cases'] + require(report.get('engine_version') == hidden_fixture['engineVersion'], + 'Hidden-text fixtures target another engine') + hidden = report.get('hidden_text', []) + require(len(hidden) == 2 and {item.get('platform') for item in hidden} == {'win32', 'linux'}, + 'Windows and Linux hidden-text evidence is required') + require(all(item.get('ok') is True and item.get('package_version') == version + and item.get('engine_version') == report.get('engine_version') + and item.get('source_sha256') == hidden_sha and item.get('cases') == 2 * len(hidden_cases) + and item.get('tarball_sha256') == report.get('package_sha256') + and item.get('negative_control') is False for item in hidden), + 'Hidden-text evidence is incomplete or targets another input') + require(all(item.get('hidden_text_ok') is True + and item.get('hidden_text_cases') == len(hidden_cases) + and item.get('hidden_text_source_sha256') == hidden_sha + and item.get('hidden_text_negative_control') is False for item in browsers), + 'Browser hidden-text evidence is incomplete or targets another input') + hidden_names = {f'visibility-{case["name"]}-{variant}' for case in hidden_cases + for variant in ('source', 'control')} + require(len(native) == len(report['native_wasi_fixtures']) + and hidden_names.issubset(native) + and all(native[name].get('nativeWasiPdfEqual') is True + and native[name].get('nativeWasiPreviewsEqual') is True for name in hidden_names), + 'Native/WASI hidden-text comparison is incomplete or duplicated') def verify_tarball(data, info, report): From 40376ca4171edc5881ad2076d087d3ccebbdc26c Mon Sep 17 00:00:00 2001 From: krflol Date: Wed, 14 Oct 2026 23:24:36 -0500 Subject: [PATCH 3/3] Update Node and browser packages to Fullbleed 2.5.24 --- .github/workflows/ci.yml | 4 +- README.md | 14 +++-- docs/engine-2.5.24.md | 6 +-- engine/Cargo.lock | 6 +-- engine/Cargo.toml | 4 +- package-lock.json | 4 +- package.json | 4 +- test/fixtures/default-styles.json | 2 +- test/fixtures/inline-layout-2.5.24.json | 71 +++++++++++++++++++++++++ 9 files changed, 97 insertions(+), 18 deletions(-) create mode 100644 test/fixtures/inline-layout-2.5.24.json diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 62371f4..394a5de 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -64,7 +64,7 @@ jobs: npm run render:fonts -- --package-root output/font-baseline-consumer/node_modules/fullbleed --out output/font-baseline npm run verify:fonts -- --evidence-root output/font-baseline --allow-legacy-metadata npm run render:fonts - npm run verify:fonts -- --baseline output/font-baseline --reviewed-layout test/fixtures/inline-layout-2.5.22.json + npm run verify:fonts -- --baseline output/font-baseline --reviewed-layout test/fixtures/inline-layout-2.5.24.json - name: Verify installed custom font families against explicit faces run: | npm run render:families @@ -148,7 +148,7 @@ jobs: run: | python -m pip install pypdf==6.19.0 pypdfium2==5.14.0 fonttools==4.65.0 pillow==12.3.0 npm run render:fonts - npm run verify:fonts -- --reviewed-layout test/fixtures/inline-layout-2.5.22.json + npm run verify:fonts -- --reviewed-layout test/fixtures/inline-layout-2.5.24.json npm run render:families npm run verify:families npm run render:inline diff --git a/README.md b/README.md index 1febb15..6da983b 100644 --- a/README.md +++ b/README.md @@ -14,8 +14,8 @@ also includes a ZIP for local development. Using Vue? The [Vue and TypeScript starter](examples/vue) includes editable templates, automatic previews, downloads, and a reusable `usePdfPreview` composable. -This is an optional integration around the published **Fullbleed 2.5.22** engine. -The package has its own version, **0.5.0**. Node applications can use a shared +This is an optional integration around the published **Fullbleed 2.5.24** engine. +The package has its own version, **0.5.1**. Node applications can use a shared render queue to bound active renders and waiting requests, include queue time in deadlines, cancel waiting jobs, and stop rendering cleanly during shutdown. The browser entry renders in a Web Worker, and the Node API supports worker and @@ -25,6 +25,14 @@ process isolation. See the [browser guide](docs/browser.md), [font-family checks](https://github.com/fullbleed-engine/fullbleed-node/blob/main/docs/font-families.md), and [embedded-font checks](https://github.com/fullbleed-engine/fullbleed-node/blob/main/docs/font-subsets.md) for the retained fixtures. +Version 0.5.1 brings the engine's hidden-text and font-decoration fixes to both +JavaScript entries. Script/style elements and `display:none` descendants stay +hidden, while explicit author display overrides remain supported. Font-selected +decoration thickness uses the selected face's metrics. The +[engine update checks](https://github.com/fullbleed-engine/fullbleed-node/blob/main/docs/engine-2.5.24.md) +cover source/control PDFs, font metrics, and browser downloads. Embedded +stylesheets are still ignored; supply template CSS through the `css` option. + Version 0.5.0 adds `defaultStyles: false` to both JavaScript entries. Use it to preserve a template's original CSS, including font inheritance from `html`, low-specificity selectors, and cascade layers. Existing calls keep the Inter @@ -43,7 +51,7 @@ Use Node.js 22 or newer. Install the [npm package](https://www.npmjs.com/package npm install fullbleed ``` -For a version-pinned installation, use `npm install --save-exact fullbleed@0.5.0`. +For a version-pinned installation, use `npm install --save-exact fullbleed@0.5.1`. Package archives and retained PDF/PNG evidence are attached to each [GitHub release](https://github.com/fullbleed-engine/fullbleed-node/releases). The [installation verification records](https://github.com/fullbleed-engine/fullbleed-node/tree/main/verification) diff --git a/docs/engine-2.5.24.md b/docs/engine-2.5.24.md index 4a034d8..c7598d0 100644 --- a/docs/engine-2.5.24.md +++ b/docs/engine-2.5.24.md @@ -1,8 +1,8 @@ # Engine 2.5.24 update checks -The engine update is being prepared. Publishing it requires the unmodified -published 2.5.24 crate, a locked WebAssembly build, an isolated npm-tarball -installation, and the Node and browser checks below. +Version 0.5.1 wraps the unmodified published 2.5.24 crate. The checks below +cover the locked WebAssembly build, an isolated npm-tarball installation, +independent PDF readers, and actual browser downloads. The update carries two fixes: diff --git a/engine/Cargo.lock b/engine/Cargo.lock index e8c1a50..75a2aeb 100644 --- a/engine/Cargo.lock +++ b/engine/Cargo.lock @@ -4,9 +4,9 @@ version = 4 [[package]] name = "fullbleed" -version = "2.5.22" +version = "2.5.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "103bc98750b704511b10460a91d6a05e8138648eaecac6c7dc96415aab75b455" +checksum = "667e051d5273d2c2818fcd6a0bad49d655df65fb9442988e46e6ef15376d406f" dependencies = [ "fullbleed_audit_contract", "subsetter", @@ -15,7 +15,7 @@ dependencies = [ [[package]] name = "fullbleed-node-adapter" -version = "0.5.0" +version = "0.5.1" dependencies = [ "fullbleed", ] diff --git a/engine/Cargo.toml b/engine/Cargo.toml index db65089..8a8b3f4 100644 --- a/engine/Cargo.toml +++ b/engine/Cargo.toml @@ -1,12 +1,12 @@ [package] name = "fullbleed-node-adapter" -version = "0.5.0" +version = "0.5.1" edition = "2024" license = "MIT" publish = false [dependencies] -fullbleed = { version = "=2.5.22", features = ["svg_raster"] } +fullbleed = { version = "=2.5.24", features = ["svg_raster"] } [profile.release] lto = true diff --git a/package-lock.json b/package-lock.json index 92ec06c..0ade332 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "fullbleed", - "version": "0.5.0", + "version": "0.5.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "fullbleed", - "version": "0.5.0", + "version": "0.5.1", "license": "MIT", "dependencies": { "@bjorn3/browser_wasi_shim": "0.4.2" diff --git a/package.json b/package.json index 90a3ae0..03376de 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "fullbleed", - "version": "0.5.0", + "version": "0.5.1", "description": "Generate PDFs from HTML and CSS in Node.js or a browser worker with the Fullbleed Rust engine, bundled fonts, and optional PNG previews.", "type": "module", "main": "./src/index.cjs", @@ -77,7 +77,7 @@ "access": "public" }, "fullbleed": { - "engineVersion": "2.5.22" + "engineVersion": "2.5.24" }, "devDependencies": { "@types/node": "22.20.5", diff --git a/test/fixtures/default-styles.json b/test/fixtures/default-styles.json index e557e91..ed06d6c 100644 --- a/test/fixtures/default-styles.json +++ b/test/fixtures/default-styles.json @@ -1,5 +1,5 @@ { - "engineVersion": "2.5.22", + "engineVersion": "2.5.24", "legacyPackage": "0.4.1", "baseline": "Playground WASI renderer with unchanged HTML/CSS; legacy hashes from the published fullbleed@0.4.1 package.", "wasmSha256": "2fc9dc5548df93a1d8bac7a7f898781ae7a46c4112911294a07756123d895c40", diff --git a/test/fixtures/inline-layout-2.5.24.json b/test/fixtures/inline-layout-2.5.24.json new file mode 100644 index 0000000..73fddf2 --- /dev/null +++ b/test/fixtures/inline-layout-2.5.24.json @@ -0,0 +1,71 @@ +{ + "schema": "fullbleed.reviewed_inline_layout.v1", + "engineVersion": "2.5.24", + "source": "https://crates.io/crates/fullbleed/2.5.24", + "reason": "The exact reviewed 2.5.22 invoice/report PDFs, previews, independent pixels, text, assets, and inputs remain the expected upgrade controls. No expected output hashes have been changed; the candidate must pass the comparison.", + "fonts": [ + { + "name": "Inter-Variable.ttf", + "path": "fonts/Inter-Variable.ttf", + "bytes": 876576, + "sha256": "29160a80ff49ddcab2c97711247e08b1fab27a484a329ce8b813d820dc559031" + }, + { + "name": "DMSerifDisplay-Regular.ttf", + "path": "fonts/DMSerifDisplay-Regular.ttf", + "bytes": 76580, + "sha256": "8cc3643535edf039aa5d95440a8542735e9197e4f4b8d9303e980fefbf5ab616" + }, + { + "name": "DMSerifDisplay-Italic.ttf", + "path": "fonts/DMSerifDisplay-Italic.ttf", + "bytes": 71208, + "sha256": "df74c0ac387baeaeb0fe4f2324e1668e6a3ed8c09cd9796fe162c71753e19e45" + }, + { + "name": "BebasNeue-Regular.ttf", + "path": "fonts/BebasNeue-Regular.ttf", + "bytes": 61400, + "sha256": "08e4623805102d819f58601e46e345648846075e363b2ceb23313c2d1c83ec73" + }, + { + "name": "NotoSansMath-Regular.ttf", + "path": "fonts/NotoSansMath-Regular.ttf", + "bytes": 590904, + "sha256": "80b61fd613d3519197e64fff6f7e71fdc7f3e6526440ea4115b554ef7fd59af7" + } + ], + "fixtures": { + "invoice": { + "htmlSha256": "195725eeeb32c521e19b47dbacbb55048bebb6df8566ab135cdc41fbf33fe57a", + "cssSha256": "cda22b39ab1f7a1eeac7cd76d162b05cd3568c2430c099123ba30a2fbd58a296", + "pages": 1, + "text": "Northstar\nStudio\nD E S I G N W I T H D I R E C T I O N\nB R A N D\nD I G I T A L\nE X P E R I E N C E S\nT H A T M A T T E R .\n42 Cedar Avenue\nPortland, OR 97205\nhello@example.invalid\nnorthstar.example.invalid\nINVOICE\nGOOD WORK.\nCLEAR DETAILS.\nLET'S MAKE\nWHAT'S NEXT.\nP R E P A R E D F O R\nMaple & Finch\nAttn. Avery Morgan\nPortland, Oregon\nT H E P R O J E C T\nAutumn launch\nBrand workshop, digital\ndesign & handoff\nI N V O I C E N S - 1 0 4 2\nIssued \u00b7 01 October 2026\nDue \u00b7 31 October 2026\nCurrency \u00b7 USD\nDESCRIPTION HOURS RATE AMOUNT\nDiscovery & brand workshop\nPositioning, priorities, and a clear creative direction\n8 $125.00 $1,000.00\nDigital design & implementation\nA cohesive visual system, from layout to launch\n6 $95.00 $570.00\nReview & studio handoff\nFinal refinements, documentation, and delivery\n2 $150.00 $300.00\nA little clarity.\nA lot of possibility.\nThank you for inviting us\nto help shape your next chapter.\nSubtotal $1,870.00\nTax \u00b7 0% in this sample $0.00\nT O T A L D U E \u00b7 U S D\n$1,870.00\nP A Y M E N T D E T A I L S\nReference NS-1042 with your payment.\nPayment instructions are supplied separately.\nA N O T E F R O M T H E S T U D I O\nQuestions about a line item?\nWrite to hello@example.invalid.\nNORTHSTAR STUDIO / INVOICE NS-1042 FICTIONAL SAMPLE \u00b7 01 / 01", + "pdfSha256": "3078d155f30536f5ad721f7be3c241cd78661ae41249131fc0fd65f37f23e408", + "previewsSha256": [ + "72420749ccc3deaa8025daf7baffcdf79be716b6e8e93ed605effebed01e86c7" + ], + "pdfiumPixelSha256": [ + "d71293741974bef9abffbdf5d88b0827b48fe6523f8d9baf46f46fe1000895a5" + ] + }, + "report": { + "htmlSha256": "46b0e975d43914c8b4492218feae09ea0ad684091a9f9ee3b18f66a87202e521", + "cssSha256": "519591ae75c239ec602324f008dd99108ebeae0b9306df7215fa5f0442905473", + "pages": 3, + "text": "Common Ground\nC O M M U N I T Y T R U S T\nP E O P L E\nP L A C E S\nP O S S I B I L I T I E S\nCOMMON\nGROUND\nStronger places.\nMore room\nfor everyone.\n2026\nC O M M U N I T Y R E V I E W\nA Y E A R\nI N C O M M O N\n0 2 / O U R R E A C H\nSmall actions,\nshared outcomes.\n0 3 / O U R R E S O U R C E S\nWhere the\nsupport goes.\nCOMMON GROUND / COMMUNITY REVIEW FICTIONAL DATA \u00b7 01 / 03Common Ground 0 2 / O U R R E A C H\nA P L A C E T O B E G I N\nSmall actions.\nShared outcomes.\nAn open door, a working kitchen, a garden plot. This year, our fictional neighborhood\nprograms connected more people with practical spaces to learn, make, and belong.\n1,240\nhouseholds took part\nacross four neighborhoods\n4,860\nvolunteer hours\nshared across our programs\n12\ncommunity spaces\nkept open and active\nMore neighbors, each quarter.\nP A R T I C I P A T I N G H O U S E H O L D S\n420\n560\n790\n980\nQ1 Q2 Q3 Q4\nQuarterly participation counts; a household can participate in more than one quarter.\n\u201cBelonging starts\nwith somewhere\nto show up.\u201d\nO U R P R O G R A M P R I N C I P L E\nBuilt around daily life.\nEvening workshops, shared meals, and weekend garden\nsessions made participation easier for people with different\nschedules. The next step is a longer opening window at\neach neighborhood space.\nILLUSTRATIVE ANNUAL REVIEW / NOT AN ACTUAL ORGANIZATION 02 / 03Common Ground 0 3 / O U R R E S O U R C E S\nF O L L O W T H E S U P P O R T\nGood intentions.\nClear allocations.\nA practical view of this fictional operating budget. Every category supports the same\naim: useful places, open to more people.\nPROGRAM AREA ALLOCATION SHARE\nNeighborhood spaces $168,000.00 40%\nLearning & workshops $126,000.00 30%\nShared meals & gardens $84,000.00 20%\nAccess & transport $42,000.00 10%\nT O T A L O P E R A T I N G A L L O C A T I O N \u00b7 U S D $420,000.00\nWhat we count.\nHouseholds are deduplicated within the annual program\nregister. Volunteer hours include recorded event and\nmaintenance time. Space counts refer to year-round\nlocations, not individual events.\nQuarterly counts show participation within each period and\nmust not be added to estimate unique annual reach.\nWhat comes next.\nExtend evening access. Improve step-free entrances.\nPublish a simple monthly view of program activity and\nspending.\nA shared tomorrow\nis built in the everyday.\nCOMMON GROUND / METHODS & ALLOCATIONS FICTIONAL DATA \u00b7 03 / 03", + "pdfSha256": "139df89824e6a73c13ff62f1237bb8eb9f71e790dcbd051b1bb713ed2bc47802", + "previewsSha256": [ + "33ee3398d59f6836368cdab0f3c725c3cf12cbf2f9f3a3ac6a6b168f6c1886f5", + "c8fe5102e9219962ea068201be0a6730cb37de797649f4b5f44478433e79b018", + "a3e8acbc7ba4147c56d45b606d3974ec4aecbda1b5d7362451e5e8890491dc7d" + ], + "pdfiumPixelSha256": [ + "c7dc8db8fc08a78f74c4477629e0f68f3b34aa37b9b9eba46a977805933d3f03", + "c5e9d15bae4f208ecb0466ba3521e22c1f98c83aeab0e1112deec2fe20d8a711", + "05d2ebd36749c7421db665a5bba2ab36559e691b5bc5570ebc095d81af2585b7" + ] + } + }, + "baselineSource": "https://github.com/fullbleed-engine/fullbleed-official/releases/tag/v2.5.22" +}