diff --git a/news/changelog-1.4.md b/news/changelog-1.4.md index 89bdcdb83c6..98e061df8d8 100644 --- a/news/changelog-1.4.md +++ b/news/changelog-1.4.md @@ -2,6 +2,7 @@ - ([#9550](https://github.com/quarto-dev/quarto-cli/issues/9550)): Don't crash when subcaptions are incorrectly specified with `fig-subcap: true` but no embedded subcaptions. - ([#9593](https://github.com/quarto-dev/quarto-cli/issues/9593)): Fix regression with `column-margin` when used in spans in a paragraph. +- ([#9602](https://github.com/quarto-dev/quarto-cli/issues/9602)): Fix regression with parsed HTML tables not being correcty included in various format like pptx and typst. ## Fixed in previous releases diff --git a/src/resources/filters/normalize/parsehtml.lua b/src/resources/filters/normalize/parsehtml.lua index 9bc36b94ade..46da275410a 100644 --- a/src/resources/filters/normalize/parsehtml.lua +++ b/src/resources/filters/normalize/parsehtml.lua @@ -126,7 +126,7 @@ function parse_html_tables() blocks:insert(result) end end - return pandoc.Div(blocks) + return make_scaffold(pandoc.Div, blocks) end end return el diff --git a/tests/docs/smoke-all/2024/04/26/9365.qmd b/tests/docs/smoke-all/2024/04/26/9365.qmd new file mode 100644 index 00000000000..d5b36bef45c --- /dev/null +++ b/tests/docs/smoke-all/2024/04/26/9365.qmd @@ -0,0 +1,38 @@ +--- +title: Parsed HTML table in PPTX +keep-typ: true +_quarto: + tests: + pptx: + ensurePptxXpath: + - 2 + - ['//a:tbl'] + - [] + typst: + ensureTypstFileRegexMatches: + - ['[\s]*#figure\('] + - ['[\s]*#block\[[\s]*#figure\('] +--- + +# Test table {#test-table} + +```{=html} + + + + + + + + + + + + + + + + + +
Sepal.LengthSepal.Width
5.13.5
4.93.0
+``` \ No newline at end of file diff --git a/tests/smoke/smoke-all.test.ts b/tests/smoke/smoke-all.test.ts index 04f91d49113..54e58383f72 100644 --- a/tests/smoke/smoke-all.test.ts +++ b/tests/smoke/smoke-all.test.ts @@ -28,6 +28,7 @@ import { fileExists, noErrors, noErrorsOrWarnings, + ensurePptxXpath, } from "../verify.ts"; import { readYaml, readYamlFromMarkdown } from "../../src/core/yaml.ts"; import { outputForInput } from "../utils.ts"; @@ -98,6 +99,7 @@ function resolveTestSpecs( ensureOdtXpath, ensureJatsXpath, ensurePptxRegexMatches, + ensurePptxXpath, ensureSnapshotMatches }; diff --git a/tests/verify.ts b/tests/verify.ts index 751d6763d5c..5d6dc076312 100644 --- a/tests/verify.ts +++ b/tests/verify.ts @@ -22,25 +22,55 @@ import { isWindows } from "../src/core/platform.ts"; import { execProcess } from "../src/core/process.ts"; import { canonicalizeSnapshot, checkSnapshot } from "./verify-snapshot.ts"; -export const withDocxContent = async (file: string, - k: (xml: string) => Promise) => { - const [_dir, stem] = dirAndStem(file); - const temp = await Deno.makeTempDir(); - try { - // Move the docx to a temp dir and unzip it - const zipFile = join(temp, stem + ".zip"); - await Deno.copyFile(file, zipFile); - await unzip(zipFile); - - // Open the core xml document and match the matches - const docXml = join(temp, "word", "document.xml"); - const xml = await Deno.readTextFile(docXml); - const result = await k(xml); - return result; - } finally { - await Deno.remove(temp, { recursive: true }); - } - }; +export const withDocxContent = async ( + file: string, + k: (xml: string) => Promise +) => { + const [_dir, stem] = dirAndStem(file); + const temp = await Deno.makeTempDir(); + try { + // Move the docx to a temp dir and unzip it + const zipFile = join(temp, stem + ".zip"); + await Deno.copyFile(file, zipFile); + await unzip(zipFile); + + // Open the core xml document and match the matches + const docXml = join(temp, "word", "document.xml"); + const xml = await Deno.readTextFile(docXml); + const result = await k(xml); + return result; + } finally { + await Deno.remove(temp, { recursive: true }); + } +}; + +export const withPptxContent = async ( + file: string, + slideNumber: number, + k: (xml: string) => Promise +) => { + const [_dir, stem] = dirAndStem(file); + const temp = await Deno.makeTempDir(); + try { + // Move the pptx to a temp dir and unzip it + const zipFile = join(temp, stem + ".zip"); + await Deno.copyFile(file, zipFile); + await unzip(zipFile); + + // Open the core xml document and match the matches + const slidePath = join(temp, "ppt", "slides"); + const slideFile = join(slidePath, `slide${slideNumber}.xml`); + assert( + existsSync(slideFile), + `Slide number ${slideNumber} is not in the Pptx`, + ); + const xml = await Deno.readTextFile(slideFile); + const result = await k(xml); + return result; + } finally { + await Deno.remove(temp, { recursive: true }); + } +}; export const noErrors: Verify = { name: "No Errors", @@ -375,6 +405,18 @@ export const verifyDocXDocument = ( }); }; +export const verifyPptxDocument = ( + callback: (doc: string) => Promise, + name?: string, +): (file: string, slideNumber: number) => Verify => { + return (file: string, slideNumber: number) => ({ + name: name ?? "Inspecting Pptx", + verify: async (_output: ExecuteOutput[]) => { + return await withPptxContent(file, slideNumber, callback); + }, + }); +}; + const xmlChecker = ( selectors: string[], noMatchSelectors?: string[], @@ -436,6 +478,18 @@ export const ensureDocxXpath = ( )(file); }; +export const ensurePptxXpath = ( + file: string, + slideNumber: number, + selectors: string[], + noMatchSelectors?: string[], +): Verify => { + return verifyPptxDocument( + xmlChecker(selectors, noMatchSelectors), + "Inspecting Pptx for XPath selectors", + )(file, slideNumber); +}; + export const ensureDocxRegexMatches = ( file: string, regexes: (string | RegExp)[],