From 3aebce4aa8037d9c3d8e1cec298ed27bd1e9b69f Mon Sep 17 00:00:00 2001 From: Christophe Dervieux Date: Thu, 9 Jul 2026 15:46:39 +0200 Subject: [PATCH 1/6] Bump Pandoc to 3.10 and Typst to 0.15.0 in configuration Version bump only, so the new binaries can be archived to S3 and downloaded during configure. Template resync, Lua API checks, and test coverage work are tracked separately and land in follow-up commits before this is considered a complete upgrade. --- configuration | 4 ++-- src/command/check/check.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/configuration b/configuration index e70824da12..411bc53bf6 100644 --- a/configuration +++ b/configuration @@ -13,10 +13,10 @@ export DENO=v2.7.14 # TODO figure out where 0.1.41 apple silicon libs are available export DENO_DOM=v0.1.41-alpha-artifacts -export PANDOC=3.8.3 +export PANDOC=3.10 export DARTSASS=1.87.0 export ESBUILD=0.25.10 -export TYPST=0.14.2 +export TYPST=0.15.0 export TYPST_GATHER=0.2.3 export VERAPDF=1.28.2 diff --git a/src/command/check/check.ts b/src/command/check/check.ts index dce5c9c8bb..bf0dec093c 100644 --- a/src/command/check/check.ts +++ b/src/command/check/check.ts @@ -244,10 +244,10 @@ async function checkVersions(conf: CheckConfiguration) { // file is in an awkward format and it is not packaged // with our installers const versionConstraints: [string | undefined, string, string][] = [ - [pandocVersion, "3.8.3", "Pandoc"], + [pandocVersion, "3.10", "Pandoc"], [sassVersion, "1.87.0", "Dart Sass"], [denoVersion, "2.7.14", "Deno"], - [typstVersion, "0.14.2", "Typst"], + [typstVersion, "0.15.0", "Typst"], ]; const checkData: [string | undefined, string, string][] = versionConstraints .map(([version, ver, name]) => [ From f33a6e61a6161bfad231057678782223b85f2323 Mon Sep 17 00:00:00 2001 From: Christophe Dervieux Date: Thu, 9 Jul 2026 18:03:14 +0200 Subject: [PATCH 2/6] Bump Dart Sass to 1.101.0, esbuild to 0.28.1, veraPDF to 1.30.2 Riding along in the same S3-publish branch as the Pandoc/Typst bump, since Carlos is already archiving from this branch. Version bump only, no risk analysis done yet for these three, same as Pandoc and Typst this is prep for archiving, not a verified upgrade. --- configuration | 6 +++--- src/command/check/check.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/configuration b/configuration index 411bc53bf6..9c200155b2 100644 --- a/configuration +++ b/configuration @@ -14,11 +14,11 @@ export DENO=v2.7.14 # TODO figure out where 0.1.41 apple silicon libs are available export DENO_DOM=v0.1.41-alpha-artifacts export PANDOC=3.10 -export DARTSASS=1.87.0 -export ESBUILD=0.25.10 +export DARTSASS=1.101.0 +export ESBUILD=0.28.1 export TYPST=0.15.0 export TYPST_GATHER=0.2.3 -export VERAPDF=1.28.2 +export VERAPDF=1.30.2 # NB: we can't put comments in the same line as export statements because it diff --git a/src/command/check/check.ts b/src/command/check/check.ts index bf0dec093c..2dad545564 100644 --- a/src/command/check/check.ts +++ b/src/command/check/check.ts @@ -245,7 +245,7 @@ async function checkVersions(conf: CheckConfiguration) { // with our installers const versionConstraints: [string | undefined, string, string][] = [ [pandocVersion, "3.10", "Pandoc"], - [sassVersion, "1.87.0", "Dart Sass"], + [sassVersion, "1.101.0", "Dart Sass"], [denoVersion, "2.7.14", "Deno"], [typstVersion, "0.15.0", "Typst"], ]; From 2ef072bed109d15ede8645a54f00da81a556ff2e Mon Sep 17 00:00:00 2001 From: Christophe Dervieux Date: Fri, 10 Jul 2026 16:35:51 +0200 Subject: [PATCH 3/6] Update all templates with pandoc 3.10 changes --- .../formats/beamer/pandoc/beamer.template | 2 +- .../formats/beamer/pandoc/hypersetup.latex | 6 +- src/resources/formats/beamer/pandoc/title.tex | 2 +- src/resources/formats/html/pandoc/html.styles | 10 ++- .../formats/html/pandoc/html.template | 4 +- src/resources/formats/html/pandoc/styles.html | 10 ++- .../formats/html/pandoc/template.html | 2 +- .../formats/pdf/pandoc/hypersetup.latex | 6 +- .../formats/pdf/pandoc/latex.template | 1 + .../formats/revealjs/pandoc/revealjs.template | 25 +++++- .../formats/revealjs/pandoc/template.html | 9 +++ .../formats/typst/pandoc/template.typst | 79 ++++++++++--------- .../formats/typst/pandoc/typst.template | 12 +-- 13 files changed, 105 insertions(+), 63 deletions(-) diff --git a/src/resources/formats/beamer/pandoc/beamer.template b/src/resources/formats/beamer/pandoc/beamer.template index 6e2c0ed80c..bb88d82662 100644 --- a/src/resources/formats/beamer/pandoc/beamer.template +++ b/src/resources/formats/beamer/pandoc/beamer.template @@ -125,7 +125,7 @@ $for(titlegraphic)$ $endfor$} $endif$ $if(logo)$ -\logo{\includegraphics{$logo$}} +\logo{\includegraphics$if(logooptions)$[$for(logooptions)$$logooptions$$sep$, $endfor$]$endif${$logo$}} $endif$ \begin{document} diff --git a/src/resources/formats/beamer/pandoc/hypersetup.latex b/src/resources/formats/beamer/pandoc/hypersetup.latex index ff67655576..610ba2c8fd 100644 --- a/src/resources/formats/beamer/pandoc/hypersetup.latex +++ b/src/resources/formats/beamer/pandoc/hypersetup.latex @@ -1,3 +1,7 @@ +% fallback for those not using the hyperref driver hyperxmp: +\makeatletter +\@ifundefined{xmpquote}{\newcommand{\xmpquote}[1]{#1}}{} +\makeatother \hypersetup{ $if(title-meta)$ pdftitle={$title-meta$}, @@ -12,7 +16,7 @@ $if(subject)$ pdfsubject={$subject$}, $endif$ $if(keywords)$ - pdfkeywords={$for(keywords)$$keywords$$sep$, $endfor$}, + pdfkeywords={$for(keywords)$\xmpquote{$keywords$}$sep$, $endfor$}, $endif$ $if(colorlinks)$ colorlinks=true, diff --git a/src/resources/formats/beamer/pandoc/title.tex b/src/resources/formats/beamer/pandoc/title.tex index a3163a911c..ccbe5198f1 100644 --- a/src/resources/formats/beamer/pandoc/title.tex +++ b/src/resources/formats/beamer/pandoc/title.tex @@ -18,5 +18,5 @@ $endfor$} $endif$ $if(logo)$ -\logo{\includegraphics{$logo$}} +\logo{\includegraphics$if(logooptions)$[$for(logooptions)$$logooptions$$sep$, $endfor$]$endif${$logo$}} $endif$ \ No newline at end of file diff --git a/src/resources/formats/html/pandoc/html.styles b/src/resources/formats/html/pandoc/html.styles index 800f63c7a6..cac6172c5e 100644 --- a/src/resources/formats/html/pandoc/html.styles +++ b/src/resources/formats/html/pandoc/html.styles @@ -106,6 +106,7 @@ div.abstract-title { } $endif$ code { + white-space: pre-wrap; font-family: $if(monofont)$$monofont$$else$Menlo, Monaco, Consolas, 'Lucida Console', monospace$endif$; $if(monobackgroundcolor)$ background-color: $monobackgroundcolor$; @@ -183,10 +184,13 @@ header { text-decoration: none; } $endif$ -code{white-space: pre-wrap;} span.smallcaps{font-variant: small-caps;} -div.columns{display: flex; gap: min(4vw, 1.5em);} -div.column{flex: auto; overflow-x: auto;} +div.columns{display: flex; gap: 1.5em;} +div.column{flex: auto;} +@media screen { +div.columns{gap: min(4vw, 1.5em);} +div.column{overflow-x: auto;} +} div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;} /* The extra [class] is a hack that increases specificity enough to override a similar rule in reveal.js */ diff --git a/src/resources/formats/html/pandoc/html.template b/src/resources/formats/html/pandoc/html.template index 26740d1b0e..2f39bc95ec 100644 --- a/src/resources/formats/html/pandoc/html.template +++ b/src/resources/formats/html/pandoc/html.template @@ -1,8 +1,8 @@ - + - + $for(author-meta)$ diff --git a/src/resources/formats/html/pandoc/styles.html b/src/resources/formats/html/pandoc/styles.html index ebcc014be1..795fd31130 100644 --- a/src/resources/formats/html/pandoc/styles.html +++ b/src/resources/formats/html/pandoc/styles.html @@ -118,6 +118,7 @@ } $endif$ code { + white-space: pre-wrap; font-family: $if(monofont)$$monofont$$else$Menlo, Monaco, Consolas, 'Lucida Console', monospace$endif$; $if(monobackgroundcolor)$ background-color: $monobackgroundcolor$; @@ -190,10 +191,13 @@ text-decoration: none; } $endif$ -code{white-space: pre-wrap;} span.smallcaps{font-variant: small-caps;} -div.columns{display: flex; gap: min(4vw, 1.5em);} -div.column{flex: auto; overflow-x: auto;} +div.columns{display: flex; gap: 1.5em;} +div.column{flex: auto;} +@media screen { +div.columns{gap: min(4vw, 1.5em);} +div.column{overflow-x: auto;} +} div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;} ul.task-list{list-style: none;} ul.task-list li input[type="checkbox"] { diff --git a/src/resources/formats/html/pandoc/template.html b/src/resources/formats/html/pandoc/template.html index 3a7b1b9675..5ae339e921 100644 --- a/src/resources/formats/html/pandoc/template.html +++ b/src/resources/formats/html/pandoc/template.html @@ -1,5 +1,5 @@ - + diff --git a/src/resources/formats/pdf/pandoc/hypersetup.latex b/src/resources/formats/pdf/pandoc/hypersetup.latex index ff67655576..610ba2c8fd 100644 --- a/src/resources/formats/pdf/pandoc/hypersetup.latex +++ b/src/resources/formats/pdf/pandoc/hypersetup.latex @@ -1,3 +1,7 @@ +% fallback for those not using the hyperref driver hyperxmp: +\makeatletter +\@ifundefined{xmpquote}{\newcommand{\xmpquote}[1]{#1}}{} +\makeatother \hypersetup{ $if(title-meta)$ pdftitle={$title-meta$}, @@ -12,7 +16,7 @@ $if(subject)$ pdfsubject={$subject$}, $endif$ $if(keywords)$ - pdfkeywords={$for(keywords)$$keywords$$sep$, $endfor$}, + pdfkeywords={$for(keywords)$\xmpquote{$keywords$}$sep$, $endfor$}, $endif$ $if(colorlinks)$ colorlinks=true, diff --git a/src/resources/formats/pdf/pandoc/latex.template b/src/resources/formats/pdf/pandoc/latex.template index 6c70459554..e993af1d77 100644 --- a/src/resources/formats/pdf/pandoc/latex.template +++ b/src/resources/formats/pdf/pandoc/latex.template @@ -1,3 +1,4 @@ +$document-metadata.latex()$ $passoptions.latex()$ \documentclass[ $for(babel-otherlangs)$ diff --git a/src/resources/formats/revealjs/pandoc/revealjs.template b/src/resources/formats/revealjs/pandoc/revealjs.template index 5278dd1457..0fc4464f17 100644 --- a/src/resources/formats/revealjs/pandoc/revealjs.template +++ b/src/resources/formats/revealjs/pandoc/revealjs.template @@ -29,6 +29,9 @@ $if(theme)$ $else$ $endif$ +$if(highlight-js)$ + +$endif$ $for(css)$ $endfor$ @@ -87,6 +90,9 @@ $body$ $if(mathjax)$ $endif$ +$if(highlight-js)$ + +$endif$ $if(mathjax)$ +$endif$ +$if(highlight-js)$ + $endif$ @@ -313,6 +319,9 @@ plugins: [ $if(mathjax)$ RevealMath, +$endif$ +$if(highlight-js)$ + RevealHighlight, $endif$ RevealNotes, RevealSearch, diff --git a/src/resources/formats/typst/pandoc/template.typst b/src/resources/formats/typst/pandoc/template.typst index 648476620f..1d90b1d976 100644 --- a/src/resources/formats/typst/pandoc/template.typst +++ b/src/resources/formats/typst/pandoc/template.typst @@ -73,47 +73,48 @@ } } - block(below: 1em, width: 100%)[ - #if title != none { - align(center, block[ - #text(weight: "bold", size: 1.5em)[#title #if thanks != none { - footnote(thanks, numbering: "*") - counter(footnote).update(n => n - 1) - }] - #( - if subtitle != none { - parbreak() - text(weight: "bold", size: 1.25em)[#subtitle] - } - )]) - } - - #if authors != none and authors != [] { - let count = authors.len() - let ncols = calc.min(count, 3) - grid( - columns: (1fr,) * ncols, - row-gutter: 1.5em, - ..authors.map(author => align(center)[ - #author.name \ - #author.affiliation \ - #author.email - ]) - ) - } + if title != none { + place(top, float: true, scope: "parent", clearance: 4mm, block(below: 1em, width: 100%)[ + #if title != none { + align(center, block[ + #text(weight: "bold", size: 1.5em, hyphenate: false)[#title #if thanks != none { + footnote(thanks, numbering: "*") + counter(footnote).update(n => n - 1) + }] + #( + if subtitle != none { + parbreak() + text(weight: "bold", size: 1.25em, hyphenate: false)[#subtitle] + } + )]) + } - #if date != none { - align(center)[#block(inset: 1em)[ - #date - ]] - } + #if authors != none and authors != [] { + let count = authors.len() + let ncols = calc.min(count, 3) + grid( + columns: (1fr,) * ncols, + row-gutter: 1.5em, + ..authors.map(author => align(center)[ + #author.name \ + #author.affiliation \ + #author.email + ]) + ) + } - #if abstract != none { - block(inset: 2em)[ - #text(weight: "semibold")[#abstract-title] #h(1em) #abstract - ] - } - ] + #if date != none { + align(center)[#block(inset: 1em)[ + #date + ]] + } + #if abstract != none { + block(inset: 2em)[ + #text(weight: "semibold")[#abstract-title] #h(1em) #abstract + ] + } + ]) + } doc } diff --git a/src/resources/formats/typst/pandoc/typst.template b/src/resources/formats/typst/pandoc/typst.template index a499cd8db9..9b161a4c87 100644 --- a/src/resources/formats/typst/pandoc/typst.template +++ b/src/resources/formats/typst/pandoc/typst.template @@ -1,13 +1,9 @@ #let horizontalrule = line(start: (25%,0%), end: (75%,0%)) -#show terms: it => { - it.children - .map(child => [ - #strong[#child.term] - #block(inset: (left: 1.5em, top: -0.4em))[#child.description] - ]) - .join() -} +#show terms.item: it => block(breakable: false)[ + #text(weight: "bold")[#it.term] + #block(inset: (left: 1.5em, top: -0.4em))[#it.description] +] #set table( inset: 6pt, From 3d812e07c0517c8ac6cac62a9295868074a59fa7 Mon Sep 17 00:00:00 2001 From: Christophe Dervieux Date: Fri, 10 Jul 2026 16:47:41 +0200 Subject: [PATCH 4/6] Fix jog.lua traverser for pandoc 3.10's typed TableBody Pandoc 3.10 upgraded TableBody to a proper typed Lua object (matching TableHead/TableFoot, which jog.lua already handled), so the untyped table fallback branch no longer catches it and every table triggered "Don't know how to traverse TableBody" during render. --- src/resources/filters/modules/jog.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/resources/filters/modules/jog.lua b/src/resources/filters/modules/jog.lua index f3ab5c0730..a3547570c4 100644 --- a/src/resources/filters/modules/jog.lua +++ b/src/resources/filters/modules/jog.lua @@ -118,6 +118,9 @@ local function recurse (element, tp, jogger) elseif tp == 'pandoc TableHead' or tp == 'pandoc TableFoot' or tp == 'TableHead' or tp == 'TableFoot' then element.rows = jogger(element.rows) + elseif tp == 'pandoc TableBody' or tp == 'TableBody' then + element.head = jogger(element.head) + element.body = jogger(element.body) elseif tp == 'Blocks' or tp == 'Inlines' then local expected_itemtype = tp == 'Inlines' and 'Inline' or 'Block' local pos = 0 From 646a0cd83a37982940ae3bef633bd697abdab932 Mon Sep 17 00:00:00 2001 From: Christophe Dervieux Date: Fri, 10 Jul 2026 16:56:04 +0200 Subject: [PATCH 5/6] Update Lua type stubs for pandoc 3.9-3.10 additive API changes pandoc.readers/writers values changed from boolean to string ("text"/"bytestring") in pandoc 3.9; add missing pandoc.TableBody constructor and pandoc.types.Sources/Source stubs. --- src/resources/lua-types/pandoc/components.lua | 10 ++++++++ src/resources/lua-types/pandoc/pandoc.lua | 4 ++-- src/resources/lua-types/pandoc/version.lua | 24 +++++++++++++++++++ 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/src/resources/lua-types/pandoc/components.lua b/src/resources/lua-types/pandoc/components.lua index 7c83870859..06b1e65a77 100644 --- a/src/resources/lua-types/pandoc/components.lua +++ b/src/resources/lua-types/pandoc/components.lua @@ -198,6 +198,16 @@ number of row header columns. pandoc.TableBody = {} +--[[ +Creates a table body +]] +---@param body? pandoc.List List of `Row` +---@param head? pandoc.List Intermediate head (list of `Row`) +---@param row_head_columns? integer Number of columns taken up by the row head of each row of a `TableBody` +---@param attr? pandoc.Attr Table body attributes +---@return pandoc.TableBody +function pandoc.TableBody(body, head, row_head_columns, attr) end + --[[ Make a clone ]] diff --git a/src/resources/lua-types/pandoc/pandoc.lua b/src/resources/lua-types/pandoc/pandoc.lua index 0c33cb5e45..cea7c64e34 100644 --- a/src/resources/lua-types/pandoc/pandoc.lua +++ b/src/resources/lua-types/pandoc/pandoc.lua @@ -3,10 +3,10 @@ ---@module 'pandoc' pandoc = {} ----@type table +---@type table pandoc.readers = {} ----@type table +---@type table pandoc.writers = {} --[[ diff --git a/src/resources/lua-types/pandoc/version.lua b/src/resources/lua-types/pandoc/version.lua index ecf1c5394a..89c8869159 100644 --- a/src/resources/lua-types/pandoc/version.lua +++ b/src/resources/lua-types/pandoc/version.lua @@ -27,6 +27,30 @@ pandoc.Version = {} function pandoc.types.Version(version_specifier) end +--[[ +A named source of text, e.g. a file name paired with its contents. +Used as an item in a `Sources` list. +]] +---@class pandoc.types.Source +---@field name string Name/path the text originated from +---@field text string The source text + +--[[ +A list of `Source` items, pairing input text with information on +where it originated (e.g. a file name). Used by Pandoc's text readers. +]] +---@class pandoc.types.Sources: pandoc.List + +---@alias sources_specifier string|table|pandoc.types.Source[]|pandoc.types.Sources + +--[[ +Creates a new Sources element, i.e., a list of `Source` items. +]] +---@param srcs sources_specifier +---@return pandoc.types.Sources +function pandoc.types.Sources(srcs) end + + --[[ Raise an error message if the version is older than the expected version; does nothing if actual is equal to or newer than the expected version. From 57a2991f479ef1b377f6855728b4448fc4552a4a Mon Sep 17 00:00:00 2001 From: Christophe Dervieux Date: Fri, 10 Jul 2026 17:21:13 +0200 Subject: [PATCH 6/6] Fix mediabag-routed image paths breaking Typst 0.15 compile on Windows Typst 0.15.0 now hard-rejects backslash path separators in image() calls. pandoc.path.join uses the host OS's native separator, producing backslash-joined paths on Windows for any mediabag-routed image (data URI, remote, or resource-path). Normalize to forward slashes before assigning as the Image element's src. --- src/resources/filters/modules/mediabag.lua | 5 +- tests/smoke/lua-unit/lua-unit.test.ts | 1 + tests/unit-lua/mediabag.test.lua | 57 ++++++++++++++++++++++ 3 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 tests/unit-lua/mediabag.test.lua diff --git a/src/resources/filters/modules/mediabag.lua b/src/resources/filters/modules/mediabag.lua index 658b613b15..5662ebeac7 100644 --- a/src/resources/filters/modules/mediabag.lua +++ b/src/resources/filters/modules/mediabag.lua @@ -82,7 +82,10 @@ local function write_mediabag_entry(src) if contents == nil then return nil end local mediabagDir = param("mediabag-dir", nil) - local mediaFile = pandoc.path.join{mediabagDir, src} + -- Always use forward slashes: this path is assigned to el.src and can flow + -- into writers (e.g. Typst 0.15+) that reject backslash path separators, + -- while forward slashes work fine for the actual file write on Windows too. + local mediaFile = pandoc.path.join{mediabagDir, src}:gsub('\\', '/') local file = _quarto.file.write(mediaFile, contents) if not file then diff --git a/tests/smoke/lua-unit/lua-unit.test.ts b/tests/smoke/lua-unit/lua-unit.test.ts index 1553052f60..5cc15696e3 100644 --- a/tests/smoke/lua-unit/lua-unit.test.ts +++ b/tests/smoke/lua-unit/lua-unit.test.ts @@ -25,6 +25,7 @@ import { unitTest } from "../../test.ts"; // Explicit list, relative to tests/unit-lua/. Keep alphabetized. const LUA_TESTS: string[] = [ + "mediabag.test.lua", "typst-css.test.lua", ]; diff --git a/tests/unit-lua/mediabag.test.lua b/tests/unit-lua/mediabag.test.lua new file mode 100644 index 0000000000..8a00d546c2 --- /dev/null +++ b/tests/unit-lua/mediabag.test.lua @@ -0,0 +1,57 @@ +-- Unit tests for src/resources/filters/modules/mediabag.lua +-- +-- Run via the Deno smoke test in tests/smoke/lua-unit/lua-unit.test.ts +-- (which sets LUA_PATH and invokes `quarto run`), or directly with: +-- LUA_PATH="tests/unit-lua/?.lua;src/resources/filters/modules/?.lua;;" \ +-- package/dist/bin/quarto run tests/unit-lua/mediabag.test.lua + +-- Mocks for filter-runtime globals ------------------------------------------- + +local param_values = {} +function param(name, default) + if param_values[name] ~= nil then return param_values[name] end + return default +end + +function warn(_msg) end + +_quarto = { + file = { + write = function(_path, _contents) return true end, + }, +} + +local lu = require('luaunit') +local mediabag = require('mediabag') + +-- Tests ---------------------------------------------------------------------- + +-- Regression test for Typst 0.15.0 hard-rejecting backslash path separators +-- in image() calls (quarto-cli-fpil). pandoc.path.join uses the host OS's +-- native separator, which is a backslash on Windows. Any path handed to +-- el.src must use forward slashes only, since it flows straight into +-- writers (e.g. Typst's image()) that may not accept native separators. +TestWriteMediabagEntry = {} + +function TestWriteMediabagEntry:setUp() + param_values = { ["mediabag-dir"] = "imgtest_files/mediabag" } + pandoc.mediabag.insert("png-base64,abc123.png", "image/png", "fake-bytes") +end + +function TestWriteMediabagEntry:tearDown() + pandoc.mediabag.empty() +end + +function TestWriteMediabagEntry:testResultHasNoBackslash() + local path = mediabag.write_mediabag_entry("png-base64,abc123.png") + lu.assertNotNil(path) + lu.assertNil(path:find('\\', 1, true), + 'write_mediabag_entry returned a path with a backslash: ' .. path) +end + +function TestWriteMediabagEntry:testResultJoinsDirAndFilename() + local path = mediabag.write_mediabag_entry("png-base64,abc123.png") + lu.assertEquals(path, "imgtest_files/mediabag/png-base64,abc123.png") +end + +os.exit(lu.LuaUnit.run())