From e30160bcb6fabcbc746b827c684b9ae3d46a92fc Mon Sep 17 00:00:00 2001 From: Christophe Dervieux Date: Tue, 6 Oct 2026 16:19:41 +0200 Subject: [PATCH 1/8] Resolve revealjs and dashboard logos relative to the document Fixes #14946. Since v1.8.22 (#13279 for #11982), revealjs and dashboard prefixed every logo path with `/` whenever a brand was active and the document sat in a project subdirectory. That assumed all logo paths were project-relative, but only brand logos are: front matter, `_quarto.yml`, `metadata-files`, `_metadata.yml` and extension format logos all arrive already relative to the document. Turning `../_extensions/x/logo.png` into `/../_extensions/x/logo.png` broke extension logos in websites (never copied to `site_libs`), and every other non-brand source broke too. In default projects even brand logos rendered as an unresolvable `/img/...`. Brand logo paths are now rebased onto the document directory before they are merged with the document logo, the same split #14075 made for Typst, so every source ends up document-relative in any project type. The `/` helper stays for the website navbar and sidebar, which have no single input document. Since v1.8.22, a project-relative front-matter or `_metadata.yml` logo in a subdirectory worked by accident, only in a website with a brand. Public projects rely on it, so it still resolves there with a deprecation warning. In default projects it never worked, so no fallback is added. --- news/changelog-1.11.md | 1 + src/core/brand/brand.ts | 73 +++++++++++++++++- src/format/dashboard/format-dashboard.ts | 17 ++-- src/format/reveal/format-reveal.ts | 28 ++++--- .../.gitignore | 2 + .../_brand.yml | 2 + .../_quarto.yml | 2 + .../img/top.png | Bin 0 -> 11810 bytes .../sub/projrel.qmd | 15 ++++ .../sub/slides.qmd | 14 ++++ .../extension-format-logo-subdir/.gitignore | 3 + .../extension-format-logo-subdir/_brand.yml | 2 + .../_extensions/repro/_extension.yml | 9 +++ .../_extensions/repro/logo.png | Bin 0 -> 11810 bytes .../extension-format-logo-subdir/_quarto.yml | 3 + .../docrel/slides.qmd | 15 ++++ .../extension-format-logo-subdir/img/top.png | Bin 0 -> 11810 bytes .../extension-format-logo-subdir/index.qmd | 14 ++++ .../meta/_metadata.yml | 1 + .../meta/slides.qmd | 14 ++++ .../projrel-meta/_metadata.yml | 1 + .../projrel-meta/slides.qmd | 17 ++++ .../projrel/dashboard.qmd | 18 +++++ .../projrel/slides.qmd | 18 +++++ .../sub/dashboard.qmd | 16 ++++ .../sub/slides.qmd | 14 ++++ 26 files changed, 277 insertions(+), 22 deletions(-) create mode 100644 tests/docs/smoke-all/brand/logo/brand-logo-subdir-default-project/.gitignore create mode 100644 tests/docs/smoke-all/brand/logo/brand-logo-subdir-default-project/_brand.yml create mode 100644 tests/docs/smoke-all/brand/logo/brand-logo-subdir-default-project/_quarto.yml create mode 100644 tests/docs/smoke-all/brand/logo/brand-logo-subdir-default-project/img/top.png create mode 100644 tests/docs/smoke-all/brand/logo/brand-logo-subdir-default-project/sub/projrel.qmd create mode 100644 tests/docs/smoke-all/brand/logo/brand-logo-subdir-default-project/sub/slides.qmd create mode 100644 tests/docs/smoke-all/brand/logo/extension-format-logo-subdir/.gitignore create mode 100644 tests/docs/smoke-all/brand/logo/extension-format-logo-subdir/_brand.yml create mode 100644 tests/docs/smoke-all/brand/logo/extension-format-logo-subdir/_extensions/repro/_extension.yml create mode 100644 tests/docs/smoke-all/brand/logo/extension-format-logo-subdir/_extensions/repro/logo.png create mode 100644 tests/docs/smoke-all/brand/logo/extension-format-logo-subdir/_quarto.yml create mode 100644 tests/docs/smoke-all/brand/logo/extension-format-logo-subdir/docrel/slides.qmd create mode 100644 tests/docs/smoke-all/brand/logo/extension-format-logo-subdir/img/top.png create mode 100644 tests/docs/smoke-all/brand/logo/extension-format-logo-subdir/index.qmd create mode 100644 tests/docs/smoke-all/brand/logo/extension-format-logo-subdir/meta/_metadata.yml create mode 100644 tests/docs/smoke-all/brand/logo/extension-format-logo-subdir/meta/slides.qmd create mode 100644 tests/docs/smoke-all/brand/logo/extension-format-logo-subdir/projrel-meta/_metadata.yml create mode 100644 tests/docs/smoke-all/brand/logo/extension-format-logo-subdir/projrel-meta/slides.qmd create mode 100644 tests/docs/smoke-all/brand/logo/extension-format-logo-subdir/projrel/dashboard.qmd create mode 100644 tests/docs/smoke-all/brand/logo/extension-format-logo-subdir/projrel/slides.qmd create mode 100644 tests/docs/smoke-all/brand/logo/extension-format-logo-subdir/sub/dashboard.qmd create mode 100644 tests/docs/smoke-all/brand/logo/extension-format-logo-subdir/sub/slides.qmd diff --git a/news/changelog-1.11.md b/news/changelog-1.11.md index eab91d51c80..ef1ecb27470 100644 --- a/news/changelog-1.11.md +++ b/news/changelog-1.11.md @@ -6,6 +6,7 @@ All changes included in 1.11: - ([#14731](https://github.com/quarto-dev/quarto-cli/pull/14731)): Fix `QUARTO_VENDOR_BINARIES=false` builds silently succeeding with missing binaries. The `configure` step's "is it on PATH?" check for `typst-gather`, `typst`, `pandoc` and `esbuild` compared an unawaited `Promise` against `undefined`, so the guard never fired. A build with `typst-gather` neither vendored nor on PATH reported success and produced a Quarto tree without it, surfacing only at render time as `typst-gather analyze failed; staging all packages as fallback`. The missing-binary error message now also names the path that was checked and the `QUARTO_TYPST_GATHER` environment variable. - ([#14741](https://github.com/quarto-dev/quarto-cli/issues/14741)): Fix PDF rendering of a cross-referenced table failing with `! Extra }, or forgotten \endgroup.` when the `endfloat` package defers `longtable` environments (`\DeclareDelayedFloatFlavor*{longtable}{table}`). - ([#14857](https://github.com/quarto-dev/quarto-cli/issues/14857)): Fix `quarto install chrome-headless-shell` (and `quarto install chromium`) failing with a 404 on Linux arm64 due to a stale Playwright CDN URL. +- ([#14946](https://github.com/quarto-dev/quarto-cli/issues/14946)): Fix `revealjs` and `dashboard` logos resolving to broken paths in documents in a project subdirectory when the project has a brand (regression introduced in v1.8.22). Logo paths are now always relative to the document; a project-relative logo path in a website with a brand still works but prints a deprecation warning. - ([#14993](https://github.com/quarto-dev/quarto-cli/issues/14993)): Fix Typst output dropping a brand font from a `font-family` list when another document in the same directory or project was rendered first with different brand fonts. ## Accessibility diff --git a/src/core/brand/brand.ts b/src/core/brand/brand.ts index af3063e85ff..701f19cb9d7 100644 --- a/src/core/brand/brand.ts +++ b/src/core/brand/brand.ts @@ -29,7 +29,14 @@ import { } from "../../resources/types/zod/schema-types.ts"; import { InternalError } from "../lib/error.ts"; -import { dirname, join, relative, resolve } from "../../deno_ral/path.ts"; +import { + dirname, + isAbsolute, + join, + relative, + resolve, +} from "../../deno_ral/path.ts"; +import { existsSync } from "../../deno_ral/fs.ts"; import { warnOnce } from "../log.ts"; import { isCssColorName } from "../css/color-names.ts"; import { isExternalPath } from "../url.ts"; @@ -439,6 +446,66 @@ export function logoAddLeadingSlashes( // left untouched. export function brandWithAbsoluteLogoPaths( brand: LightDarkBrand | undefined, +): LightDarkBrand | undefined { + return brandWithLogoPaths(brand, ensureLeadingSlashIfNotExternal); +} + +// Resolve the logo of an HTML document so that every path is relative to the +// input document. Brand logo paths are project-relative and get rebased onto +// the input directory before merging with the document logo, whose paths +// (front matter, _quarto.yml, _metadata.yml, extensions) are already relative +// to the input. +export function resolveInputRelativeLogo( + brand: LightDarkBrand | undefined, + spec: LogoLightDarkSpecifier | undefined, + order: BrandNamedLogo[], + input: string, + isWebsite: boolean, +): NormalizedLogoLightDarkSpecifier | undefined { + const inputDir = dirname(resolve(input)); + const inputRelativeBrand = brandWithLogoPaths( + brand, + (path, projectDir) => + isExternalPath(path) + ? path + : pathWithForwardSlashes(relative(inputDir, join(projectDir, path))), + ); + const logo = resolveLogo(inputRelativeBrand, spec, order); + const projectDir = brand?.light?.projectDir; + if (!logo || !projectDir || !isWebsite) { + return logo; + } + // Document logo paths that only exist relative to the project directory + // are deprecated: in a website with an active brand they still resolve, + // with a warning. + const projectRelativeFallback = (options: LogoOptions | undefined) => { + if ( + !options || isExternalPath(options.path) || isAbsolute(options.path) || + existsSync(join(inputDir, options.path)) + ) { + return options; + } + const projectPath = join(projectDir, options.path); + if (!existsSync(projectPath)) { + return options; + } + const path = pathWithForwardSlashes(relative(inputDir, projectPath)); + warnOnce( + `Logo path '${options.path}' for ${ + pathWithForwardSlashes(relative(projectDir, input)) + } was resolved relative to the project directory. This is deprecated: logo paths are relative to the document, use '${path}' instead.`, + ); + return { ...options, path }; + }; + return { + light: projectRelativeFallback(logo.light), + dark: projectRelativeFallback(logo.dark), + }; +} + +function brandWithLogoPaths( + brand: LightDarkBrand | undefined, + transformPath: (path: string, projectDir: string) => string, ): LightDarkBrand | undefined { if (!brand) { return brand; @@ -451,14 +518,14 @@ export function brandWithAbsoluteLogoPaths( if (oldLogo[size]) { logo[size] = { ...oldLogo[size], - path: ensureLeadingSlashIfNotExternal(oldLogo[size]!.path), + path: transformPath(oldLogo[size]!.path, b.projectDir), }; } } for (const [key, value] of Object.entries(oldLogo.images)) { logo.images[key] = { ...value, - path: ensureLeadingSlashIfNotExternal(value.path), + path: transformPath(value.path, b.projectDir), }; } const copy = Object.create(b) as Brand; diff --git a/src/format/dashboard/format-dashboard.ts b/src/format/dashboard/format-dashboard.ts index e9c66dfd88c..30d1fbf1bbc 100644 --- a/src/format/dashboard/format-dashboard.ts +++ b/src/format/dashboard/format-dashboard.ts @@ -67,7 +67,7 @@ import { processToolbars } from "./format-dashboard-toolbar.ts"; import { processDatatables } from "./format-dashboard-tables.ts"; import { assert } from "testing/asserts"; import { brandBootstrapSassBundles } from "../../core/sass/brand.ts"; -import { logoAddLeadingSlashes, resolveLogo } from "../../core/brand/brand.ts"; +import { resolveInputRelativeLogo } from "../../core/brand/brand.ts"; const kDashboardClz = "quarto-dashboard"; @@ -131,14 +131,13 @@ export function dashboardFormat() { alt: format.metadata[kLogoAlt] as string, }; } - let logo = resolveLogo(brand, logoSpec, [ - "small", - "medium", - "large", - ]); - logo = logoAddLeadingSlashes(logo, brand, input); - - format.metadata[kLogo] = logo; + format.metadata[kLogo] = resolveInputRelativeLogo( + brand, + logoSpec, + ["small", "medium", "large"], + input, + isWebsiteProject, + ); const extras: FormatExtras = await baseHtmlFormat.formatExtras( input, markdown, diff --git a/src/format/reveal/format-reveal.ts b/src/format/reveal/format-reveal.ts index 24a8494987f..4fd9d48aa94 100644 --- a/src/format/reveal/format-reveal.ts +++ b/src/format/reveal/format-reveal.ts @@ -77,10 +77,11 @@ import { } from "./constants.ts"; import { revealMetadataFilter } from "./metadata.ts"; import { ProjectContext } from "../../project/types.ts"; +import { projectIsWebsite } from "../../project/project-shared.ts"; import { titleSlidePartial } from "./format-reveal-title.ts"; import { registerWriterFormatHandler } from "../format-handlers.ts"; import { pandocNativeStr } from "../../core/pandoc/codegen.ts"; -import { logoAddLeadingSlashes, resolveLogo } from "../../core/brand/brand.ts"; +import { resolveInputRelativeLogo } from "../../core/brand/brand.ts"; export function revealResolveFormat(format: Format) { format.metadata = revealMetadataFilter(format.metadata); @@ -301,7 +302,9 @@ export function revealjsFormat() { theme["text-highlighting-mode"], ), ], - [kMarkdownAfterBody]: [revealMarkdownAfterBody(format, input)], + [kMarkdownAfterBody]: [ + revealMarkdownAfterBody(format, input, project), + ], }, }, ); @@ -392,7 +395,11 @@ export function revealjsFormat() { ); } -function revealMarkdownAfterBody(format: Format, input: string) { +function revealMarkdownAfterBody( + format: Format, + input: string, + project: ProjectContext, +) { let brandMode: "light" | "dark" = "light"; if (format.metadata[kBrandMode] === "dark") { brandMode = "dark"; @@ -401,14 +408,15 @@ function revealMarkdownAfterBody(format: Format, input: string) { lines.push("::: {.quarto-auto-generated-content style='display: none;'}\n"); const revealLogo = format .metadata[kSlideLogo] as (string | { path: string } | undefined); - let logo = resolveLogo(format.render.brand, revealLogo, [ - "small", - "medium", - "large", - ]); + const logo = resolveInputRelativeLogo( + format.render.brand, + revealLogo, + ["small", "medium", "large"], + input, + projectIsWebsite(project), + ); if (logo && logo[brandMode]) { - logo = logoAddLeadingSlashes(logo, format.render.brand, input); - const modeLogo = logo![brandMode]!; + const modeLogo = logo[brandMode]!; const altText = modeLogo.alt ? `alt="${modeLogo.alt}" ` : ""; lines.push( ``, diff --git a/tests/docs/smoke-all/brand/logo/brand-logo-subdir-default-project/.gitignore b/tests/docs/smoke-all/brand/logo/brand-logo-subdir-default-project/.gitignore new file mode 100644 index 00000000000..ad293093b07 --- /dev/null +++ b/tests/docs/smoke-all/brand/logo/brand-logo-subdir-default-project/.gitignore @@ -0,0 +1,2 @@ +/.quarto/ +**/*.quarto_ipynb diff --git a/tests/docs/smoke-all/brand/logo/brand-logo-subdir-default-project/_brand.yml b/tests/docs/smoke-all/brand/logo/brand-logo-subdir-default-project/_brand.yml new file mode 100644 index 00000000000..31497aa34ef --- /dev/null +++ b/tests/docs/smoke-all/brand/logo/brand-logo-subdir-default-project/_brand.yml @@ -0,0 +1,2 @@ +logo: + small: img/top.png diff --git a/tests/docs/smoke-all/brand/logo/brand-logo-subdir-default-project/_quarto.yml b/tests/docs/smoke-all/brand/logo/brand-logo-subdir-default-project/_quarto.yml new file mode 100644 index 00000000000..b8bae5830fa --- /dev/null +++ b/tests/docs/smoke-all/brand/logo/brand-logo-subdir-default-project/_quarto.yml @@ -0,0 +1,2 @@ +project: + type: default diff --git a/tests/docs/smoke-all/brand/logo/brand-logo-subdir-default-project/img/top.png b/tests/docs/smoke-all/brand/logo/brand-logo-subdir-default-project/img/top.png new file mode 100644 index 0000000000000000000000000000000000000000..616d17b92cb7f142afa346bb27bb505ad26ae908 GIT binary patch literal 11810 zcmai)MN}MJ8=xB}IH7?6L4&)yyF+k-LvVM81_Hrd8+Z2r!8N!$jk~-1@cpx!&D5gm zF6zAZTzUNoQ&yBhMZ!k{005{m(&8!r0F>~@SOF3Kyi1Xhf3G2>vJ8gB)dd`FN@1V z@%oLlCsyJy)Osp-=B#pnm%oqpnZsPuyN5wr@~^{J8|#Dy&yZyYkBBXtls;7Zg-Cvq zriQ9~>9|u=BPVEWFH!~%aGHog|62*1a#Mrpu?4RhR2J9kP)VI!vESB9K*d7PKdEQw zUjhjjULuP;sYzbyR$c20LgV5woHME-iQCUf?1M3wEEA{q2i6-O)(1`B1y0kS zJ67WsR0{_05J~xv(=?!R-~;&vn&t5&;1&hd7CK^wb8avuqaD|XZNr10U6f`D*keI7 zqbSfpRsgu6rvc@4$LZwuGO*DowtYsKeoJ zLH_0(Gqvraw~kfd#xICOW+Hz#^|xSd4UR)2Wi4ebs0ouYW_6H*@*R4OuMdz=AH4Y8 z#M+?sb{Nx_7)EL9&z`8D(PtBSEtpvL)E>N_>s7TxFAZUxPpj=I?}ob} zQZnA5tY`BQud_yx_jXA18CLjS7V$gS>fT165ZFJ$GdqAlp-`35qfgET?|TnvOqz}B zVG%%@x&}PX!~T3gH1ixbFi|xlK3bF!b=bE(*~h#u6w|JE8>tE~u-5V*9r*r@^1ZU? zxw>JJ7YT-4CZ~v^Y*SqPjp_vW>Y#hNl|@J;{&D?z!=dKTpZYwKgI-lj{A8_cKjf7R?vfl+DW_6V0``(-DYycGZB9mvU8I9Au;v;N}L`6 z!gN>_Rf$!0CJog{W1z}4kVqWT5Uv=d$>WAKv9z4$%3J##&*5B+m$n2kK10lKd26stk<~1c)%E;b z-`7p9*xZ-KL77`y#n`?MhU0)~+WNewx*2=PMkl&c?n61)Hq4crTT~>Wfxv|cj2v_S z`_IYg_NIvwK)L!?Rd#YB@CNgM2>wRRS^YCx3)FAy*=*dR3LJ#mIRZ4WFyre0WIMRGLS!2&a?vysna)2;)l+J=ZKEYQLVY-^=9R>|YyS>F%YRP}HdzUyGOeI5-6)2hr0!McOYg zE!9@ijoP~lk(Qu1^|eBOWdq0sK1j%hK|7k*Tf$R&_O%|K=#={R5AxTXOFQG~p;6f` z0RFXS415R`E9CNHW6$w3?x)A;rE?>yw4PTZxPA0s-Lk>IWcHwJasUWwLI+C^PZBr$fiA~RnPV*_ zoOtT+7!}ISSa?Qk4PC#SI|S8}eTloovfUKYw{{%spB|;7$xo>9?=-cDm0MIBzK&Zo zG75~dXBXIpEQ)J*53@RJ9kdj`bJ$#+4AjN>8~cs%O7Od%4PM-Ssy82C)TO2-_zC9c z@OU|CZy#jCAh5Ce4S4B2Rh`fav0Q&m)wNXtb=&;h$+Q0*`}aksrhF=lir%!^w z-FtcSG(nnRz8*K8_-gyQ>nK5%`H641?<*<>Z=Z~q!5?9$vlh9cKSUG;ImT3MqQ641 zzAh5oLj&@tkKlgyPN~I^Fqb5(PD;Xgto%C6945cT&PBobn-A^m8x;x8Ru6o%LhNnv zwgq9*09Gv^3aAL+gl$+Kv@Cx-UeEb9&y;8{v)60b?(R5ge^i-{KlEBMYEOztUB$G* zEI0XsP$)7pqt5rN1LGDdpn;h7(l#n{6G!m>~O9vH`oJWe_IF&oT(>XB9*nR)bG2#+6s#QYYAuuk024AT(Wd;eci!*rjxC`0M^ z5c0uz93=p#Ix6!K&)KU*6%QZy91oAk35NORTm+_|fG33)&q?A92aMwKrFIW08sACa zqsIZ!2{;|+8E%IZ+yxwV@ooVQ`=;iCydGD#)!s71Bxac?V61wHCOyHUqy~e(sEcH9 zT6)i-Wg?ei`>TGWOK1eOT7EqAaYAzgX{~^jnYCj6W-l*oPR(~)rYm0#zC-~1ueL2L z1h#Uv4Oe^vE{Kz|hOn3~Ng#~sA7!|GFThGte+Sa47{ERU@Q}g1gqpneXe|VGK5b9Q zXuceol$9UlEfY-KLTd5h_UsuAk`a>ZGD318Ln%VS3;;vzu*OzH)d5L8V|HlChT@5s z>C~;brcWz+1ahZS%vRF{uhhwZlkK`6XK2qpwp|y!8%Yv89~>5yCC2wN|MAi=-}8Wy z7-y>|dW91or03her$#Gw7*p|-{u@*bTbu( z^BLn8?J=C21WM)lpTxkyt&sTX3pKfF>&1dJDJ;K8coY>89yKHQP;OXtnC8Tug;5(C z)N}mDI85!bPlHY@ZodZ^WtT@1eF+PP<*(_u>r}?zr=A6O^V&bM4ePx&gUBCn00sE2 zs~9;pt9mE9IOqzw7tD^n7_DQeMKl)muEpTtci#7oU~~hn5`*-N)@<>FUH7750_&P~ zFasebg8J*=-~hn85A&UKV8J(Z4Oym&aeQ{4w&jk!!f#}^tffFN{sT>MD81UVrXg@($@+f0I`MlRRfHY&%{2!b1ps$QEYqlWz}hb4A6F^$gy~SN3K{ z#1SC&oMHO&HW2Dlgub*z8OC?qoa zL#qFi8AU)?E%0rHI>nc7=u)4Bqt0|>jWA#{!F~-6EDz1BrkFQdvUHg2pV1L6A-i~;{vXXFlD1*UGj4{PbAh^bGx>ka!1woH!AB5eHo zNOL+M9Gp}2N_PT;f4c6-u+ z;~An1bxz~-<`0R?*2X&+@aPSL6ugMTB55m{vnll2pSQWFsTv+f;>0ZPYtPZvy zhi)diR5uf89&xey&K0Q7Zxw%N+;yG$+Zb4u7tGgYzsr>wzgvG{gn58BB5_gTj->3- zIDS~2YWvR_J5G7RPJ8;Nbwv`F1ZV5e`p$hUz5*A*`W6Td7$Qu`xJJ*u_#C<_FFQU< zLK!4~67h&*YuktNOD=8D57y93ZuDk)oMQ9oPR%d4hyOS4l}`T)LWw6@U(ZBBf4}vf zo|{{G-jllmpZVBnN`@x_!|dc=VO>Ah1TOl5YFq;Umqi16wbM(PKeAA7Id{QGK`7m5 zc`u+gCT6D&XYz+1xdUgA!Yex1-95w*5Fd?fW&{Wd=LXmPtWUY2x=@oIi&bukBFD_L za1U%D=tP?uIhK7I0EdsViCe1nNW2f^CT$b=BKWwc*S8}IP%nC_<(Y(k*~==I#I->? z`+IT%7q;tXWyK_?Y8GS#&HG+BP5pX1A2~-0r-do?topMPqj7y>ph%f56{0!O>1NcO z9PwXdq|5Orm|1S4==|NV(2DhRd}Mru`dfSDXs4la@@;A_h8=?5%vDD(bhA#9P2&l& z_)U@-UHW+U=o@}Qr%f~(PW&>H(6j3=2Ry@wU*fq!tS)FE!C0-Kj3SB$&KSz2O;Mgd zJY7=zhMyp|XlE@aD+oqjQPcSId(J&7%#UX>33e-`h1(z73?IYvG?;Q`p%EMci+0u( zPqbL6Y?3UoSx@$MZEYaym~+&o7Nh8nee{)p^&B@}W0(9bn3SGkxo~cqb08G6A=+#m zlWNzqP^n~TIJR#ANJePkyuxyuLB;-y;Hv+}CJ7=(xm2LRy7yxfm(@ly1MAP1QQ3SS zgaYd<7-6Ab_2m$2G-+_5wzV1hG>1+A)YeWHyQ!hvfaRRd7PVAmrP^WuvpRElnfysk z52{r!O@-F8N?9fF-%T@agFRbNZ@%dS%hdA08ynE@Rj}Nht+qOq)8B%BHXb2<)y?;> zz=Yg%TcJJ6B3UDIcbmjEFM@?fhwQ5m5~l@UeqXoZOLxoLS6E+?Rx{UD{oely17CVv zf;c#J0t!4-1sYmfS|pf+Bi7?F8|37o8ne%Y-#Tv{x1?KwkXhz0P3&oZZ*n6m{8QL6 zDvS9$eN~Li@<#PtRW)B^$Gu2H>g<7TgL?6MV2PBo^0=A_{EOQgT(4-P>sz%haJIR%7-oOxo_t7uD(Hw|832G;h|W%+UE!}pf513 zt0Z$62z6H|O+xGLnRY34O}!NlJDZ2z`-KN2o8JnLs8n!uZBRZzo>3DIlpZZ_Q#en ziAcgrh_#~a?!T6&@SWnpB0LbF75k@@SRS#4C_>cHw5o)nU9DW*x$zRmzfA@qxq?2 z;8XL26_(+#Zmg_~;V?z9?(cF7<&KsaURpoVcN6TnSGW|STGFE@fg2d73p_@FlpgKh zOjG%setuy!npXi1LnvnhZMpm?@mmAvE$zq?&X3Irc@eb3*15K2xz+ZF;oztlAh4DH zkeEM3MGU*qdY73Q4!D2sIGTN9g7Ik1pJ*+dob+5pP$mi0^b-5|r3i$_;H&sn zQ$?yt#G_d^Kg%5SMexLeXLJ+hQQT(lM3BqNcp&t;*fXXko~Tsl``b&uOzCRhvp z?ptS(|7ce<_4F;ZZaYDnVHmFtGHYGltF(?eIg=JvF(+UK(*C_}&DQsTJ_nM#v`()l zBbEos{^mqlwBD5+b2Y2;TSuS%QNZ-00x*}Sr?3Kdi+VfJ;#LuSeaKocZ^#1D0sCtX z9PGIKf{8ydYcjQX_RRl0o3gvcQBME?3r^22IE5ESGZA74ozVca`gp~pn_@rcsAE@&+-Jc?`BlE*?vRt%~=AKHnTACqWQ z)13*h6{2E`3LWY+5rCr(5c@YmbGihfA!bP$kY1UbukCZW<&6-|tzn#-o14xZ*#M0@ zOlPf^RYtoXdMU(9=9RXxz{M7LOO22kHoYsqBAl<#icP+ZaS6lbIY!7$A$4Er;Sa~% zW~Q(6AaxlMH|#7XBZ$a4om1SFIFGwGy2;mY;3LxMy}Ogkl9X6X^y%{k?jU4O=2!en zb+#VyS3~`Eaz%zl4Ray$`i2yFS5#osqL&zNhk41D#=F3A3nkIQ;l2=}y&j!tBzzl0 zwDiv#Sccbg7b0gAyf+tU|A*V<_Ncs&I9@kd z>3GuZIRNjkNP8yceAd1VCPjtN9;vR*DNt$nO%T_@YaWBJrZ|41KCeDya8xfDWb0ppQyLQ14 zOYoOgipVdjI#xwe_Lp*LNKiXoJnZmN<Q@$>5xtr5DmHT_cKg4DB2eAt*%lVsG(SX?eTHOaV8lfkb^^DZ8F>Dv zLq^qzmofA8fC0fC>d}zA^gGLnA2T&tcsMfk6Vm_=G`#P6%cR}0{VuQOeNxZMIA?nr z8g)v^At4R2LnvW5RjT75+#=Nsl?6FVLiC?VJH53)llM;6N1eKcm|Qad8Ezi&6@THe zO|#sv%T2RN!{qEB=}A&&2dCTfmLTqq=95Gq37Q<8m{q>Q7MDbM7BBB(Ye)eUpH^Hu zH+soit7<6nw5xk}wVxJ3Z;->-ZP+JjC;1E=XD!nHck;4wg}#$zpoWbltOpv=FqGC5 z|3n#5z$sf^OQT+eFvr)rLXlt8L;RLF$#pzw##alp{+JxD99jS z&3O_z&&U*eT76S}^&j(jsA?l4eJo}_41OURI70>!&Svcs{ulopaD#usl63a!O`>e> z=LLJpRH}e9vnK0_dianv?&?v6F)czhDM?o}2OTvlEj^|K1<9sgl*x0p4Tsve+G6*$ zvoTHV4vyNM?L+@O7LxDQ?v;SMg(p-}ee);BPfJ*yB^>>-8P-*U_9XnRyEXRibC@wI z9QsnLW76lUW9zDv_MiS4PKC1hs7Din?wA&#B8hk+)Xt?GbQoSep0?@cLTj> z)XT*4rRx1JTt+D)X1T3Y`to$gi_5SXbY}lM!3~`;SU>Gf!Jp4*S zVB4ikSeY}lZJp48YBH)v431ES6yfrk^}>)I^o|kxpngJm$lw(^OT26X&id=>=`qM` zgH?5Rq=IlN$Do4e@NT%St6OvadHXay<&C(n-J3?8>hI^z6SbU*1lGV?q^b|@+Ot8o zu8TUkJMZXnbd92gEGCjB?4$gF{<8)R%EcW&I;p2H_udd?s~nWdzGTg5X1HA_qNUaU z)+1KcJXJu0xFkUFmG2``)wfOZ{3rnq*I&wG`FP3&ag2?2+9}Tmz8eHFSlLB>f|c6u zEW}~N=QYweZya78tqH3~S3URtISEw;#rztIg4{$GBy6jfKTT=PkWhX;pG_fI#uvO0Y3?J@ikUuRmH2Eah%+K@~+i40fuOIT!qn87{*GOW8u_ZHGE9b41@? zN5VG~%>Bnk%kWNsv=ZVuv`z76=ZU8SOm=Hb)>Y_SU*vs0k-e(u3@D_izbc5OLj5qg zE*mf`ROZ1AdwobG9^+|bNW&sw=1{D%JLRm`Dd^TX zpSvH*akM4X32Qj(QcPLQ_99 zFgkeH35g^>TaERBefi+&)Hgl#r?Gy&9qgGX^1eefP7AnxZjE_A>ki!?n6eJ7S;2!D zSpJMzYJUX(pV*o#r1wBJKUI4K)yh%0N2upU++}^;AKq7dE<($#<%E3u_`gxgpR@m* zKAG6B+r_0dMG6jra&~}L<)TX?| zis@*1k@ITzUi9(feXPQ2gy!Bx+Jq>vZo-Tb?0iBlkA0H&s`)Czzn1NH1u4 zC5(hj5ooIke3qDvnB^Zz-sihQT0Aa2)zu-%iD?_@Kut4zVKF4*frugV*v0KAx})qb zf|m0mA}i_gJ6B%Yx-1_P}`)^pe zvy9>yYyJIt>O8~e@}`tgFFtPrm`XP^5#Sk4P=fZ{o5%FL5>6HJYH=qANRiwL@5^2L zmeSW83YMK_6wj1zz(r|?`U>Cj4Y+BT>owyuql$^}&42|}^W;T@-(K$QAI|crX4J|3 z)?XpUwjZf2V6x1{VtEfo8jG(&3}p-Yo{*{27C8dIXy;kr$1D}E%9}9FkS?I5EOE4AP$*Nb`@3n4% z`rr!gEp05G1dHG1zVIU3x>G#kIbg6a5j-N`!e$|?PSBGRhh7WSG8oiWo372CGvRUC zJ(=E6SZ|!L=bo41Umda}>IoA__NGMD_2DIhd&4szR%5Jm@i2i*VundR2(Nz zwbL(Blm-@nDz{iywQAOb1rjWjJ331!;$T1yYu(~gH`t& zbWD2HhSC|u<>H*Ub)~H=ynF&lwT1B^j`>cx{qbK* zDF;4Wshl*T;>@RFCd6zYB&;9MQX}tlZK+TskzlUmtM2GNLnZ6j^`NEoiR`=8-QeWc zGrDnoW{>G8)vvpqB;1M{eAV7LOPPt)h&{r>8hE?kVAzbU;EOg3ROWb*IWi3>y@VMM zF3u-^K9$|X^Np~1`>;sZ#5OnV#_VNkM(VTsT-{jWtJo`Ziw;ht{Y|t4%#!@X4hRD9 zAtH5=W0au)V9`Lz>20Nh&qH9@_O+nHYz9TM@pN9R?jK=X4&-T#{2Y>;h`-_rLJC$F zW@?jG&Jsq=hYt9*d`ty%OfVpVpi>iLL)8T;Xyp)1UQ>;CG~oyz8+UoW`@&q@RgdPP zRb#3vS1=*`{tWOwVvzI%(O zT*dyywL1gMpdZ*s6V76L&|xiqyIAt z9$E}arKP-2@IeRW3<;GyCU50@kr8K?o7%YMh*q%A;O?TA+rBo=_|%)Iw*5SF-tp&R zxH_$siO*X^S-!Tb9O?z83qKr2JjVn8Wz&3LLx3H-X9a}~yL%yG6rbCXE3iKu|$#odp& zeWb|5r`Uz87Q3A9uIqnt)&$*kvU{VMX@`%rs!)4+l1YVq)$?0uw>$KZC{KR6t`hXN zEUUdqk&(6nq$yT#A&d%fSwa4M`&KA(=?SpjFCNmHOROb5pWM+WWt`)l9c{fidB&Z! zX}*GiSU^E{VHe-(xLgt&Jf>Zj%2N3mPX0_kK}|y+lM~2U;QASV?9b!W>9$_);=JN` z$TLCAv3DdPAlLE=BN78j=@njHMrp-7F}5niXlc-M!=@CM|3MgVKf7Wqs;?$TG!lHV z_+q)vApL2**ztb0UjNH?y=TdNB|1IjD8vdaW0tt1wOqAEb0<C-0km3apF0aGuNViPE8h4&nvfH;Gg91Fh(Ual7T!=Y zO3+E&u{fmHaTY;owB3@z8qkfAeWF;NWJ44&RB0UVkXikvzzgo2m}zo`obKl06yI3u zsd>!NO*rR9;X&3|jNhi;eWyuVA!8p(FcfBD_qVQq-YYOM|Db?rwuPV~u_fz$5hl;0 zgVECBR{!6BMKGv~Vv%h#fo;5Kj16f_cM#mEbzqiSIV)?D0U`SYlp&(5E1vViWSl*% zsS!x<)9|)s=Rq?cV=0X^9lk67cjgD5C(tU{O83(~{M0N&Pvls!W<{JI6(?i&i%Y|&WP;0N z<~AbJgoQnQXS?6${wz&qKM6sdL_&8(1G3~duH8>*4qPg3-?ea+Qdk4az=$>YE+at@ z9pfin((3WnlhLTN*P|;5IXG?LS2Vvr;STR1VX1_E&`PEtGLgbH689F5+@8V|@LSpVdwp-OivcchcFk6`xDrGHp zc#07yXmXIcsYhSUNH5$jJu3C}E9FE~-=Rs|{?)+T6vWn8l`>`~N=Yk5mL^FHIsknX zhc+z~@=SFKQzoDLPZ8v536q&gvJcEG~fDXlg-NFotlbj)hIk1*GAa~R4hG? zGj)CC3@Sd{DP$o{0GSnzjXPU3v_5}NLBQNC*OW+E?=-((e>bVBLS8I>K9>1&-;qk1 z&8n2Et*0X-0`r@dp5BBmkK7h(fhfdOVYE;TIH`p&EA-K4M_r^`VC6%DDJ%Cizo~La zDc1@E6y!qOV&rk-DTPL*6oi)H6q5Unin7Uc0*dy{)Mq9% zayJUU{_jrWP^F|E&J{d!{R<#WV;*aplX67GNVCU(XSXbQm`f_YS=~!A_>UA!b}L

2y z6(;TAA{R3KcB}wRG4H+QOr+t_LDvIpzj{dzWRSqu(GVmF@Gyj(#fcO)#^Uq(zK>nE zX|dg_q14B;je3ywKyQfUbDPWB4V6Lp$@C*8X|$stF9e7XH=iEp6wBP;v$|J|XEn;j zM3FY}4GBuJ?Hsc`gXA7%?hS2jpeXv}+05HO^FLYsyamDkBB!z=p!n5aH-aY2G;ZG5 zG6)fmM0#BO5%1W^+v#iMn3LnTfCRuxtoUjIQxv1jsdlE=!oZq7;Je04}os{ynlWxjdcZl0%-!;RP>(LvRONf-1}wD4d>A!%64tG>iWsj+WRum0{XrgG8MLI z(c^ck)6M_k%@E=5P-i(7hy*D-IPT1JDmx}X>UaI z10VvJTvm1Gw<^^(bKngP1#c^D-1fI&msaJcPTpT~h~wtP5F0v3TtGgFwDu|fqjj=B oA4&gkm;JGV|Cgq2w0sl##jV&$be9bIfW84T5{lwgqK1M02Ttn$=Kufz literal 0 HcmV?d00001 diff --git a/tests/docs/smoke-all/brand/logo/brand-logo-subdir-default-project/sub/projrel.qmd b/tests/docs/smoke-all/brand/logo/brand-logo-subdir-default-project/sub/projrel.qmd new file mode 100644 index 00000000000..d83c58cdac0 --- /dev/null +++ b/tests/docs/smoke-all/brand/logo/brand-logo-subdir-default-project/sub/projrel.qmd @@ -0,0 +1,15 @@ +--- +title: "Project-relative logo in subdirectory of a default project is not rebased" +format: revealjs +logo: img/top.png +_quarto: + tests: + revealjs: + noErrorsOrWarnings: true + ensureFileRegexMatches: + - + - '~@SOF3Kyi1Xhf3G2>vJ8gB)dd`FN@1V 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

2y z6(;TAA{R3KcB}wRG4H+QOr+t_LDvIpzj{dzWRSqu(GVmF@Gyj(#fcO)#^Uq(zK>nE zX|dg_q14B;je3ywKyQfUbDPWB4V6Lp$@C*8X|$stF9e7XH=iEp6wBP;v$|J|XEn;j zM3FY}4GBuJ?Hsc`gXA7%?hS2jpeXv}+05HO^FLYsyamDkBB!z=p!n5aH-aY2G;ZG5 zG6)fmM0#BO5%1W^+v#iMn3LnTfCRuxtoUjIQxv1jsdlE=!oZq7;Je04}os{ynlWxjdcZl0%-!;RP>(LvRONf-1}wD4d>A!%64tG>iWsj+WRum0{XrgG8MLI z(c^ck)6M_k%@E=5P-i(7hy*D-IPT1JDmx}X>UaI z10VvJTvm1Gw<^^(bKngP1#c^D-1fI&msaJcPTpT~h~wtP5F0v3TtGgFwDu|fqjj=B oA4&gkm;JGV|Cgq2w0sl##jV&$be9bIfW84T5{lwgqK1M02Ttn$=Kufz literal 0 HcmV?d00001 diff --git a/tests/docs/smoke-all/brand/logo/extension-format-logo-subdir/_quarto.yml b/tests/docs/smoke-all/brand/logo/extension-format-logo-subdir/_quarto.yml new file mode 100644 index 00000000000..ec92545f2f8 --- /dev/null +++ b/tests/docs/smoke-all/brand/logo/extension-format-logo-subdir/_quarto.yml @@ -0,0 +1,3 @@ +project: + type: website +brand: _brand.yml diff --git a/tests/docs/smoke-all/brand/logo/extension-format-logo-subdir/docrel/slides.qmd b/tests/docs/smoke-all/brand/logo/extension-format-logo-subdir/docrel/slides.qmd new file mode 100644 index 00000000000..7dec31653c6 --- /dev/null +++ b/tests/docs/smoke-all/brand/logo/extension-format-logo-subdir/docrel/slides.qmd @@ -0,0 +1,15 @@ +--- +title: "Document-relative logo in subdirectory" +format: revealjs +logo: ../img/top.png +_quarto: + tests: + revealjs: + noErrorsOrWarnings: true + ensureFileRegexMatches: + - + - '~@SOF3Kyi1Xhf3G2>vJ8gB)dd`FN@1V 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

2y z6(;TAA{R3KcB}wRG4H+QOr+t_LDvIpzj{dzWRSqu(GVmF@Gyj(#fcO)#^Uq(zK>nE zX|dg_q14B;je3ywKyQfUbDPWB4V6Lp$@C*8X|$stF9e7XH=iEp6wBP;v$|J|XEn;j zM3FY}4GBuJ?Hsc`gXA7%?hS2jpeXv}+05HO^FLYsyamDkBB!z=p!n5aH-aY2G;ZG5 zG6)fmM0#BO5%1W^+v#iMn3LnTfCRuxtoUjIQxv1jsdlE=!oZq7;Je04}os{ynlWxjdcZl0%-!;RP>(LvRONf-1}wD4d>A!%64tG>iWsj+WRum0{XrgG8MLI z(c^ck)6M_k%@E=5P-i(7hy*D-IPT1JDmx}X>UaI z10VvJTvm1Gw<^^(bKngP1#c^D-1fI&msaJcPTpT~h~wtP5F0v3TtGgFwDu|fqjj=B oA4&gkm;JGV|Cgq2w0sl##jV&$be9bIfW84T5{lwgqK1M02Ttn$=Kufz literal 0 HcmV?d00001 diff --git a/tests/docs/smoke-all/brand/logo/extension-format-logo-subdir/index.qmd b/tests/docs/smoke-all/brand/logo/extension-format-logo-subdir/index.qmd new file mode 100644 index 00000000000..fe3978b7753 --- /dev/null +++ b/tests/docs/smoke-all/brand/logo/extension-format-logo-subdir/index.qmd @@ -0,0 +1,14 @@ +--- +title: "Extension logo at project root" +format: repro-revealjs +_quarto: + tests: + repro-revealjs: + ensureFileRegexMatches: + - + - ' Date: Tue, 6 Oct 2026 18:14:32 +0200 Subject: [PATCH 2/8] Keep the project-relative logo fallback for dark-only brands and encoded paths The deprecated fallback that still resolves a project-relative logo in a website subdirectory missed two cases the previous leading-slash behavior handled. A brand configured with only `dark:` has no `light` variant, so the project directory was never found. A URL-encoded path such as `img/my%20logo.png` was checked as a literal filename; the leading-slash path worked because HTML resource processing decodes attributes before resolving them. The existence checks now decode the path the same way, while the emitted path stays as written. --- src/core/brand/brand.ts | 30 ++++++++++++++---- .../brand-dark-only-logo-subdir/.gitignore | 3 ++ .../brand-dark-only-logo-subdir/_brand.yml | 2 ++ .../brand-dark-only-logo-subdir/_quarto.yml | 4 +++ .../brand-dark-only-logo-subdir/img/top.png | Bin 0 -> 11810 bytes .../sub/slides.qmd | 18 +++++++++++ .../img/my logo.png | Bin 0 -> 11810 bytes .../projrel/encoded-slides.qmd | 18 +++++++++++ 8 files changed, 69 insertions(+), 6 deletions(-) create mode 100644 tests/docs/smoke-all/brand/logo/brand-dark-only-logo-subdir/.gitignore create mode 100644 tests/docs/smoke-all/brand/logo/brand-dark-only-logo-subdir/_brand.yml create mode 100644 tests/docs/smoke-all/brand/logo/brand-dark-only-logo-subdir/_quarto.yml create mode 100644 tests/docs/smoke-all/brand/logo/brand-dark-only-logo-subdir/img/top.png create mode 100644 tests/docs/smoke-all/brand/logo/brand-dark-only-logo-subdir/sub/slides.qmd create mode 100644 tests/docs/smoke-all/brand/logo/extension-format-logo-subdir/img/my logo.png create mode 100644 tests/docs/smoke-all/brand/logo/extension-format-logo-subdir/projrel/encoded-slides.qmd diff --git a/src/core/brand/brand.ts b/src/core/brand/brand.ts index 701f19cb9d7..48be2e020cb 100644 --- a/src/core/brand/brand.ts +++ b/src/core/brand/brand.ts @@ -471,7 +471,7 @@ export function resolveInputRelativeLogo( : pathWithForwardSlashes(relative(inputDir, join(projectDir, path))), ); const logo = resolveLogo(inputRelativeBrand, spec, order); - const projectDir = brand?.light?.projectDir; + const projectDir = brand?.light?.projectDir ?? brand?.dark?.projectDir; if (!logo || !projectDir || !isWebsite) { return logo; } @@ -480,16 +480,22 @@ export function resolveInputRelativeLogo( // with a warning. const projectRelativeFallback = (options: LogoOptions | undefined) => { if ( - !options || isExternalPath(options.path) || isAbsolute(options.path) || - existsSync(join(inputDir, options.path)) + !options || isExternalPath(options.path) || isAbsolute(options.path) ) { return options; } - const projectPath = join(projectDir, options.path); - if (!existsSync(projectPath)) { + // Logo paths are URLs; check the files they point to, as HTML resource + // processing does, but keep the path encoded in the output. + const filePath = decodeUriPath(options.path); + if ( + existsSync(join(inputDir, filePath)) || + !existsSync(join(projectDir, filePath)) + ) { return options; } - const path = pathWithForwardSlashes(relative(inputDir, projectPath)); + const path = pathWithForwardSlashes( + relative(inputDir, join(projectDir, options.path)), + ); warnOnce( `Logo path '${options.path}' for ${ pathWithForwardSlashes(relative(projectDir, input)) @@ -503,6 +509,18 @@ export function resolveInputRelativeLogo( }; } +// Malformed escapes are left as-is, like getDecodedAttribute in core/html.ts. +function decodeUriPath(path: string): string { + try { + return decodeURI(path); + } catch (e) { + if (e instanceof URIError) { + return path; + } + throw e; + } +} + function brandWithLogoPaths( brand: LightDarkBrand | undefined, transformPath: (path: string, projectDir: string) => string, diff --git a/tests/docs/smoke-all/brand/logo/brand-dark-only-logo-subdir/.gitignore b/tests/docs/smoke-all/brand/logo/brand-dark-only-logo-subdir/.gitignore new file mode 100644 index 00000000000..ab1fa232cc5 --- /dev/null +++ b/tests/docs/smoke-all/brand/logo/brand-dark-only-logo-subdir/.gitignore @@ -0,0 +1,3 @@ +/.quarto/ +**/*.quarto_ipynb +/_site/ diff --git a/tests/docs/smoke-all/brand/logo/brand-dark-only-logo-subdir/_brand.yml b/tests/docs/smoke-all/brand/logo/brand-dark-only-logo-subdir/_brand.yml new file mode 100644 index 00000000000..35fb7d996a2 --- /dev/null +++ b/tests/docs/smoke-all/brand/logo/brand-dark-only-logo-subdir/_brand.yml @@ -0,0 +1,2 @@ +color: + primary: "#00883A" diff --git a/tests/docs/smoke-all/brand/logo/brand-dark-only-logo-subdir/_quarto.yml b/tests/docs/smoke-all/brand/logo/brand-dark-only-logo-subdir/_quarto.yml new file mode 100644 index 00000000000..99d2f0ecb86 --- /dev/null +++ b/tests/docs/smoke-all/brand/logo/brand-dark-only-logo-subdir/_quarto.yml @@ -0,0 +1,4 @@ +project: + type: website +brand: + dark: _brand.yml diff --git a/tests/docs/smoke-all/brand/logo/brand-dark-only-logo-subdir/img/top.png b/tests/docs/smoke-all/brand/logo/brand-dark-only-logo-subdir/img/top.png new file mode 100644 index 0000000000000000000000000000000000000000..616d17b92cb7f142afa346bb27bb505ad26ae908 GIT binary patch literal 11810 zcmai)MN}MJ8=xB}IH7?6L4&)yyF+k-LvVM81_Hrd8+Z2r!8N!$jk~-1@cpx!&D5gm zF6zAZTzUNoQ&yBhMZ!k{005{m(&8!r0F>~@SOF3Kyi1Xhf3G2>vJ8gB)dd`FN@1V z@%oLlCsyJy)Osp-=B#pnm%oqpnZsPuyN5wr@~^{J8|#Dy&yZyYkBBXtls;7Zg-Cvq zriQ9~>9|u=BPVEWFH!~%aGHog|62*1a#Mrpu?4RhR2J9kP)VI!vESB9K*d7PKdEQw zUjhjjULuP;sYzbyR$c20LgV5woHME-iQCUf?1M3wEEA{q2i6-O)(1`B1y0kS zJ67WsR0{_05J~xv(=?!R-~;&vn&t5&;1&hd7CK^wb8avuqaD|XZNr10U6f`D*keI7 zqbSfpRsgu6rvc@4$LZwuGO*DowtYsKeoJ zLH_0(Gqvraw~kfd#xICOW+Hz#^|xSd4UR)2Wi4ebs0ouYW_6H*@*R4OuMdz=AH4Y8 z#M+?sb{Nx_7)EL9&z`8D(PtBSEtpvL)E>N_>s7TxFAZUxPpj=I?}ob} zQZnA5tY`BQud_yx_jXA18CLjS7V$gS>fT165ZFJ$GdqAlp-`35qfgET?|TnvOqz}B zVG%%@x&}PX!~T3gH1ixbFi|xlK3bF!b=bE(*~h#u6w|JE8>tE~u-5V*9r*r@^1ZU? zxw>JJ7YT-4CZ~v^Y*SqPjp_vW>Y#hNl|@J;{&D?z!=dKTpZYwKgI-lj{A8_cKjf7R?vfl+DW_6V0``(-DYycGZB9mvU8I9Au;v;N}L`6 z!gN>_Rf$!0CJog{W1z}4kVqWT5Uv=d$>WAKv9z4$%3J##&*5B+m$n2kK10lKd26stk<~1c)%E;b z-`7p9*xZ-KL77`y#n`?MhU0)~+WNewx*2=PMkl&c?n61)Hq4crTT~>Wfxv|cj2v_S z`_IYg_NIvwK)L!?Rd#YB@CNgM2>wRRS^YCx3)FAy*=*dR3LJ#mIRZ4WFyre0WIMRGLS!2&a?vysna)2;)l+J=ZKEYQLVY-^=9R>|YyS>F%YRP}HdzUyGOeI5-6)2hr0!McOYg zE!9@ijoP~lk(Qu1^|eBOWdq0sK1j%hK|7k*Tf$R&_O%|K=#={R5AxTXOFQG~p;6f` z0RFXS415R`E9CNHW6$w3?x)A;rE?>yw4PTZxPA0s-Lk>IWcHwJasUWwLI+C^PZBr$fiA~RnPV*_ zoOtT+7!}ISSa?Qk4PC#SI|S8}eTloovfUKYw{{%spB|;7$xo>9?=-cDm0MIBzK&Zo zG75~dXBXIpEQ)J*53@RJ9kdj`bJ$#+4AjN>8~cs%O7Od%4PM-Ssy82C)TO2-_zC9c z@OU|CZy#jCAh5Ce4S4B2Rh`fav0Q&m)wNXtb=&;h$+Q0*`}aksrhF=lir%!^w z-FtcSG(nnRz8*K8_-gyQ>nK5%`H641?<*<>Z=Z~q!5?9$vlh9cKSUG;ImT3MqQ641 zzAh5oLj&@tkKlgyPN~I^Fqb5(PD;Xgto%C6945cT&PBobn-A^m8x;x8Ru6o%LhNnv zwgq9*09Gv^3aAL+gl$+Kv@Cx-UeEb9&y;8{v)60b?(R5ge^i-{KlEBMYEOztUB$G* zEI0XsP$)7pqt5rN1LGDdpn;h7(l#n{6G!m>~O9vH`oJWe_IF&oT(>XB9*nR)bG2#+6s#QYYAuuk024AT(Wd;eci!*rjxC`0M^ z5c0uz93=p#Ix6!K&)KU*6%QZy91oAk35NORTm+_|fG33)&q?A92aMwKrFIW08sACa zqsIZ!2{;|+8E%IZ+yxwV@ooVQ`=;iCydGD#)!s71Bxac?V61wHCOyHUqy~e(sEcH9 zT6)i-Wg?ei`>TGWOK1eOT7EqAaYAzgX{~^jnYCj6W-l*oPR(~)rYm0#zC-~1ueL2L z1h#Uv4Oe^vE{Kz|hOn3~Ng#~sA7!|GFThGte+Sa47{ERU@Q}g1gqpneXe|VGK5b9Q zXuceol$9UlEfY-KLTd5h_UsuAk`a>ZGD318Ln%VS3;;vzu*OzH)d5L8V|HlChT@5s z>C~;brcWz+1ahZS%vRF{uhhwZlkK`6XK2qpwp|y!8%Yv89~>5yCC2wN|MAi=-}8Wy z7-y>|dW91or03her$#Gw7*p|-{u@*bTbu( z^BLn8?J=C21WM)lpTxkyt&sTX3pKfF>&1dJDJ;K8coY>89yKHQP;OXtnC8Tug;5(C z)N}mDI85!bPlHY@ZodZ^WtT@1eF+PP<*(_u>r}?zr=A6O^V&bM4ePx&gUBCn00sE2 zs~9;pt9mE9IOqzw7tD^n7_DQeMKl)muEpTtci#7oU~~hn5`*-N)@<>FUH7750_&P~ zFasebg8J*=-~hn85A&UKV8J(Z4Oym&aeQ{4w&jk!!f#}^tffFN{sT>MD81UVrXg@($@+f0I`MlRRfHY&%{2!b1ps$QEYqlWz}hb4A6F^$gy~SN3K{ z#1SC&oMHO&HW2Dlgub*z8OC?qoa zL#qFi8AU)?E%0rHI>nc7=u)4Bqt0|>jWA#{!F~-6EDz1BrkFQdvUHg2pV1L6A-i~;{vXXFlD1*UGj4{PbAh^bGx>ka!1woH!AB5eHo zNOL+M9Gp}2N_PT;f4c6-u+ z;~An1bxz~-<`0R?*2X&+@aPSL6ugMTB55m{vnll2pSQWFsTv+f;>0ZPYtPZvy zhi)diR5uf89&xey&K0Q7Zxw%N+;yG$+Zb4u7tGgYzsr>wzgvG{gn58BB5_gTj->3- zIDS~2YWvR_J5G7RPJ8;Nbwv`F1ZV5e`p$hUz5*A*`W6Td7$Qu`xJJ*u_#C<_FFQU< zLK!4~67h&*YuktNOD=8D57y93ZuDk)oMQ9oPR%d4hyOS4l}`T)LWw6@U(ZBBf4}vf zo|{{G-jllmpZVBnN`@x_!|dc=VO>Ah1TOl5YFq;Umqi16wbM(PKeAA7Id{QGK`7m5 zc`u+gCT6D&XYz+1xdUgA!Yex1-95w*5Fd?fW&{Wd=LXmPtWUY2x=@oIi&bukBFD_L za1U%D=tP?uIhK7I0EdsViCe1nNW2f^CT$b=BKWwc*S8}IP%nC_<(Y(k*~==I#I->? z`+IT%7q;tXWyK_?Y8GS#&HG+BP5pX1A2~-0r-do?topMPqj7y>ph%f56{0!O>1NcO z9PwXdq|5Orm|1S4==|NV(2DhRd}Mru`dfSDXs4la@@;A_h8=?5%vDD(bhA#9P2&l& z_)U@-UHW+U=o@}Qr%f~(PW&>H(6j3=2Ry@wU*fq!tS)FE!C0-Kj3SB$&KSz2O;Mgd zJY7=zhMyp|XlE@aD+oqjQPcSId(J&7%#UX>33e-`h1(z73?IYvG?;Q`p%EMci+0u( zPqbL6Y?3UoSx@$MZEYaym~+&o7Nh8nee{)p^&B@}W0(9bn3SGkxo~cqb08G6A=+#m zlWNzqP^n~TIJR#ANJePkyuxyuLB;-y;Hv+}CJ7=(xm2LRy7yxfm(@ly1MAP1QQ3SS zgaYd<7-6Ab_2m$2G-+_5wzV1hG>1+A)YeWHyQ!hvfaRRd7PVAmrP^WuvpRElnfysk z52{r!O@-F8N?9fF-%T@agFRbNZ@%dS%hdA08ynE@Rj}Nht+qOq)8B%BHXb2<)y?;> zz=Yg%TcJJ6B3UDIcbmjEFM@?fhwQ5m5~l@UeqXoZOLxoLS6E+?Rx{UD{oely17CVv zf;c#J0t!4-1sYmfS|pf+Bi7?F8|37o8ne%Y-#Tv{x1?KwkXhz0P3&oZZ*n6m{8QL6 zDvS9$eN~Li@<#PtRW)B^$Gu2H>g<7TgL?6MV2PBo^0=A_{EOQgT(4-P>sz%haJIR%7-oOxo_t7uD(Hw|832G;h|W%+UE!}pf513 zt0Z$62z6H|O+xGLnRY34O}!NlJDZ2z`-KN2o8JnLs8n!uZBRZzo>3DIlpZZ_Q#en ziAcgrh_#~a?!T6&@SWnpB0LbF75k@@SRS#4C_>cHw5o)nU9DW*x$zRmzfA@qxq?2 z;8XL26_(+#Zmg_~;V?z9?(cF7<&KsaURpoVcN6TnSGW|STGFE@fg2d73p_@FlpgKh zOjG%setuy!npXi1LnvnhZMpm?@mmAvE$zq?&X3Irc@eb3*15K2xz+ZF;oztlAh4DH zkeEM3MGU*qdY73Q4!D2sIGTN9g7Ik1pJ*+dob+5pP$mi0^b-5|r3i$_;H&sn zQ$?yt#G_d^Kg%5SMexLeXLJ+hQQT(lM3BqNcp&t;*fXXko~Tsl``b&uOzCRhvp z?ptS(|7ce<_4F;ZZaYDnVHmFtGHYGltF(?eIg=JvF(+UK(*C_}&DQsTJ_nM#v`()l zBbEos{^mqlwBD5+b2Y2;TSuS%QNZ-00x*}Sr?3Kdi+VfJ;#LuSeaKocZ^#1D0sCtX z9PGIKf{8ydYcjQX_RRl0o3gvcQBME?3r^22IE5ESGZA74ozVca`gp~pn_@rcsAE@&+-Jc?`BlE*?vRt%~=AKHnTACqWQ z)13*h6{2E`3LWY+5rCr(5c@YmbGihfA!bP$kY1UbukCZW<&6-|tzn#-o14xZ*#M0@ zOlPf^RYtoXdMU(9=9RXxz{M7LOO22kHoYsqBAl<#icP+ZaS6lbIY!7$A$4Er;Sa~% zW~Q(6AaxlMH|#7XBZ$a4om1SFIFGwGy2;mY;3LxMy}Ogkl9X6X^y%{k?jU4O=2!en zb+#VyS3~`Eaz%zl4Ray$`i2yFS5#osqL&zNhk41D#=F3A3nkIQ;l2=}y&j!tBzzl0 zwDiv#Sccbg7b0gAyf+tU|A*V<_Ncs&I9@kd z>3GuZIRNjkNP8yceAd1VCPjtN9;vR*DNt$nO%T_@YaWBJrZ|41KCeDya8xfDWb0ppQyLQ14 zOYoOgipVdjI#xwe_Lp*LNKiXoJnZmN<Q@$>5xtr5DmHT_cKg4DB2eAt*%lVsG(SX?eTHOaV8lfkb^^DZ8F>Dv zLq^qzmofA8fC0fC>d}zA^gGLnA2T&tcsMfk6Vm_=G`#P6%cR}0{VuQOeNxZMIA?nr z8g)v^At4R2LnvW5RjT75+#=Nsl?6FVLiC?VJH53)llM;6N1eKcm|Qad8Ezi&6@THe zO|#sv%T2RN!{qEB=}A&&2dCTfmLTqq=95Gq37Q<8m{q>Q7MDbM7BBB(Ye)eUpH^Hu zH+soit7<6nw5xk}wVxJ3Z;->-ZP+JjC;1E=XD!nHck;4wg}#$zpoWbltOpv=FqGC5 z|3n#5z$sf^OQT+eFvr)rLXlt8L;RLF$#pzw##alp{+JxD99jS z&3O_z&&U*eT76S}^&j(jsA?l4eJo}_41OURI70>!&Svcs{ulopaD#usl63a!O`>e> z=LLJpRH}e9vnK0_dianv?&?v6F)czhDM?o}2OTvlEj^|K1<9sgl*x0p4Tsve+G6*$ zvoTHV4vyNM?L+@O7LxDQ?v;SMg(p-}ee);BPfJ*yB^>>-8P-*U_9XnRyEXRibC@wI z9QsnLW76lUW9zDv_MiS4PKC1hs7Din?wA&#B8hk+)Xt?GbQoSep0?@cLTj> z)XT*4rRx1JTt+D)X1T3Y`to$gi_5SXbY}lM!3~`;SU>Gf!Jp4*S zVB4ikSeY}lZJp48YBH)v431ES6yfrk^}>)I^o|kxpngJm$lw(^OT26X&id=>=`qM` zgH?5Rq=IlN$Do4e@NT%St6OvadHXay<&C(n-J3?8>hI^z6SbU*1lGV?q^b|@+Ot8o zu8TUkJMZXnbd92gEGCjB?4$gF{<8)R%EcW&I;p2H_udd?s~nWdzGTg5X1HA_qNUaU z)+1KcJXJu0xFkUFmG2``)wfOZ{3rnq*I&wG`FP3&ag2?2+9}Tmz8eHFSlLB>f|c6u zEW}~N=QYweZya78tqH3~S3URtISEw;#rztIg4{$GBy6jfKTT=PkWhX;pG_fI#uvO0Y3?J@ikUuRmH2Eah%+K@~+i40fuOIT!qn87{*GOW8u_ZHGE9b41@? zN5VG~%>Bnk%kWNsv=ZVuv`z76=ZU8SOm=Hb)>Y_SU*vs0k-e(u3@D_izbc5OLj5qg zE*mf`ROZ1AdwobG9^+|bNW&sw=1{D%JLRm`Dd^TX zpSvH*akM4X32Qj(QcPLQ_99 zFgkeH35g^>TaERBefi+&)Hgl#r?Gy&9qgGX^1eefP7AnxZjE_A>ki!?n6eJ7S;2!D zSpJMzYJUX(pV*o#r1wBJKUI4K)yh%0N2upU++}^;AKq7dE<($#<%E3u_`gxgpR@m* zKAG6B+r_0dMG6jra&~}L<)TX?| zis@*1k@ITzUi9(feXPQ2gy!Bx+Jq>vZo-Tb?0iBlkA0H&s`)Czzn1NH1u4 zC5(hj5ooIke3qDvnB^Zz-sihQT0Aa2)zu-%iD?_@Kut4zVKF4*frugV*v0KAx})qb zf|m0mA}i_gJ6B%Yx-1_P}`)^pe zvy9>yYyJIt>O8~e@}`tgFFtPrm`XP^5#Sk4P=fZ{o5%FL5>6HJYH=qANRiwL@5^2L zmeSW83YMK_6wj1zz(r|?`U>Cj4Y+BT>owyuql$^}&42|}^W;T@-(K$QAI|crX4J|3 z)?XpUwjZf2V6x1{VtEfo8jG(&3}p-Yo{*{27C8dIXy;kr$1D}E%9}9FkS?I5EOE4AP$*Nb`@3n4% z`rr!gEp05G1dHG1zVIU3x>G#kIbg6a5j-N`!e$|?PSBGRhh7WSG8oiWo372CGvRUC zJ(=E6SZ|!L=bo41Umda}>IoA__NGMD_2DIhd&4szR%5Jm@i2i*VundR2(Nz zwbL(Blm-@nDz{iywQAOb1rjWjJ331!;$T1yYu(~gH`t& zbWD2HhSC|u<>H*Ub)~H=ynF&lwT1B^j`>cx{qbK* zDF;4Wshl*T;>@RFCd6zYB&;9MQX}tlZK+TskzlUmtM2GNLnZ6j^`NEoiR`=8-QeWc zGrDnoW{>G8)vvpqB;1M{eAV7LOPPt)h&{r>8hE?kVAzbU;EOg3ROWb*IWi3>y@VMM zF3u-^K9$|X^Np~1`>;sZ#5OnV#_VNkM(VTsT-{jWtJo`Ziw;ht{Y|t4%#!@X4hRD9 zAtH5=W0au)V9`Lz>20Nh&qH9@_O+nHYz9TM@pN9R?jK=X4&-T#{2Y>;h`-_rLJC$F zW@?jG&Jsq=hYt9*d`ty%OfVpVpi>iLL)8T;Xyp)1UQ>;CG~oyz8+UoW`@&q@RgdPP zRb#3vS1=*`{tWOwVvzI%(O zT*dyywL1gMpdZ*s6V76L&|xiqyIAt z9$E}arKP-2@IeRW3<;GyCU50@kr8K?o7%YMh*q%A;O?TA+rBo=_|%)Iw*5SF-tp&R zxH_$siO*X^S-!Tb9O?z83qKr2JjVn8Wz&3LLx3H-X9a}~yL%yG6rbCXE3iKu|$#odp& zeWb|5r`Uz87Q3A9uIqnt)&$*kvU{VMX@`%rs!)4+l1YVq)$?0uw>$KZC{KR6t`hXN zEUUdqk&(6nq$yT#A&d%fSwa4M`&KA(=?SpjFCNmHOROb5pWM+WWt`)l9c{fidB&Z! zX}*GiSU^E{VHe-(xLgt&Jf>Zj%2N3mPX0_kK}|y+lM~2U;QASV?9b!W>9$_);=JN` z$TLCAv3DdPAlLE=BN78j=@njHMrp-7F}5niXlc-M!=@CM|3MgVKf7Wqs;?$TG!lHV z_+q)vApL2**ztb0UjNH?y=TdNB|1IjD8vdaW0tt1wOqAEb0<C-0km3apF0aGuNViPE8h4&nvfH;Gg91Fh(Ual7T!=Y zO3+E&u{fmHaTY;owB3@z8qkfAeWF;NWJ44&RB0UVkXikvzzgo2m}zo`obKl06yI3u zsd>!NO*rR9;X&3|jNhi;eWyuVA!8p(FcfBD_qVQq-YYOM|Db?rwuPV~u_fz$5hl;0 zgVECBR{!6BMKGv~Vv%h#fo;5Kj16f_cM#mEbzqiSIV)?D0U`SYlp&(5E1vViWSl*% zsS!x<)9|)s=Rq?cV=0X^9lk67cjgD5C(tU{O83(~{M0N&Pvls!W<{JI6(?i&i%Y|&WP;0N z<~AbJgoQnQXS?6${wz&qKM6sdL_&8(1G3~duH8>*4qPg3-?ea+Qdk4az=$>YE+at@ z9pfin((3WnlhLTN*P|;5IXG?LS2Vvr;STR1VX1_E&`PEtGLgbH689F5+@8V|@LSpVdwp-OivcchcFk6`xDrGHp zc#07yXmXIcsYhSUNH5$jJu3C}E9FE~-=Rs|{?)+T6vWn8l`>`~N=Yk5mL^FHIsknX zhc+z~@=SFKQzoDLPZ8v536q&gvJcEG~fDXlg-NFotlbj)hIk1*GAa~R4hG? zGj)CC3@Sd{DP$o{0GSnzjXPU3v_5}NLBQNC*OW+E?=-((e>bVBLS8I>K9>1&-;qk1 z&8n2Et*0X-0`r@dp5BBmkK7h(fhfdOVYE;TIH`p&EA-K4M_r^`VC6%DDJ%Cizo~La zDc1@E6y!qOV&rk-DTPL*6oi)H6q5Unin7Uc0*dy{)Mq9% zayJUU{_jrWP^F|E&J{d!{R<#WV;*aplX67GNVCU(XSXbQm`f_YS=~!A_>UA!b}L

2y z6(;TAA{R3KcB}wRG4H+QOr+t_LDvIpzj{dzWRSqu(GVmF@Gyj(#fcO)#^Uq(zK>nE zX|dg_q14B;je3ywKyQfUbDPWB4V6Lp$@C*8X|$stF9e7XH=iEp6wBP;v$|J|XEn;j zM3FY}4GBuJ?Hsc`gXA7%?hS2jpeXv}+05HO^FLYsyamDkBB!z=p!n5aH-aY2G;ZG5 zG6)fmM0#BO5%1W^+v#iMn3LnTfCRuxtoUjIQxv1jsdlE=!oZq7;Je04}os{ynlWxjdcZl0%-!;RP>(LvRONf-1}wD4d>A!%64tG>iWsj+WRum0{XrgG8MLI z(c^ck)6M_k%@E=5P-i(7hy*D-IPT1JDmx}X>UaI z10VvJTvm1Gw<^^(bKngP1#c^D-1fI&msaJcPTpT~h~wtP5F0v3TtGgFwDu|fqjj=B oA4&gkm;JGV|Cgq2w0sl##jV&$be9bIfW84T5{lwgqK1M02Ttn$=Kufz literal 0 HcmV?d00001 diff --git a/tests/docs/smoke-all/brand/logo/brand-dark-only-logo-subdir/sub/slides.qmd b/tests/docs/smoke-all/brand/logo/brand-dark-only-logo-subdir/sub/slides.qmd new file mode 100644 index 00000000000..9ba42b81ba1 --- /dev/null +++ b/tests/docs/smoke-all/brand/logo/brand-dark-only-logo-subdir/sub/slides.qmd @@ -0,0 +1,18 @@ +--- +title: "Project-relative logo in subdirectory with a dark-only brand (deprecated)" +format: revealjs +logo: img/top.png +_quarto: + tests: + revealjs: + noErrors: true + printsMessage: + level: WARN + regex: 'img/top\.png.*project' + ensureFileRegexMatches: + - + - '~@SOF3Kyi1Xhf3G2>vJ8gB)dd`FN@1V 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

2y z6(;TAA{R3KcB}wRG4H+QOr+t_LDvIpzj{dzWRSqu(GVmF@Gyj(#fcO)#^Uq(zK>nE zX|dg_q14B;je3ywKyQfUbDPWB4V6Lp$@C*8X|$stF9e7XH=iEp6wBP;v$|J|XEn;j zM3FY}4GBuJ?Hsc`gXA7%?hS2jpeXv}+05HO^FLYsyamDkBB!z=p!n5aH-aY2G;ZG5 zG6)fmM0#BO5%1W^+v#iMn3LnTfCRuxtoUjIQxv1jsdlE=!oZq7;Je04}os{ynlWxjdcZl0%-!;RP>(LvRONf-1}wD4d>A!%64tG>iWsj+WRum0{XrgG8MLI z(c^ck)6M_k%@E=5P-i(7hy*D-IPT1JDmx}X>UaI z10VvJTvm1Gw<^^(bKngP1#c^D-1fI&msaJcPTpT~h~wtP5F0v3TtGgFwDu|fqjj=B oA4&gkm;JGV|Cgq2w0sl##jV&$be9bIfW84T5{lwgqK1M02Ttn$=Kufz literal 0 HcmV?d00001 diff --git a/tests/docs/smoke-all/brand/logo/extension-format-logo-subdir/projrel/encoded-slides.qmd b/tests/docs/smoke-all/brand/logo/extension-format-logo-subdir/projrel/encoded-slides.qmd new file mode 100644 index 00000000000..29f046fb7b5 --- /dev/null +++ b/tests/docs/smoke-all/brand/logo/extension-format-logo-subdir/projrel/encoded-slides.qmd @@ -0,0 +1,18 @@ +--- +title: "URL-encoded project-relative logo in subdirectory (deprecated)" +format: revealjs +logo: img/my%20logo.png +_quarto: + tests: + revealjs: + noErrors: true + printsMessage: + level: WARN + regex: 'img/my%20logo\.png.*project' + ensureFileRegexMatches: + - + - ' Date: Tue, 6 Oct 2026 18:25:17 +0200 Subject: [PATCH 3/8] Share URI decoding between HTML attributes and the logo fallback Both decode a path with decodeURI and keep the raw value on a malformed escape. Only the HTML attribute reader warns: the logo path ends up in an HTML attribute too, so warning in the fallback would report it twice. --- src/core/brand/brand.ts | 16 ++-------------- src/core/html.ts | 19 ++++++++----------- src/core/url.ts | 12 ++++++++++++ 3 files changed, 22 insertions(+), 25 deletions(-) diff --git a/src/core/brand/brand.ts b/src/core/brand/brand.ts index 48be2e020cb..a8d6db0f0de 100644 --- a/src/core/brand/brand.ts +++ b/src/core/brand/brand.ts @@ -39,7 +39,7 @@ import { import { existsSync } from "../../deno_ral/fs.ts"; import { warnOnce } from "../log.ts"; import { isCssColorName } from "../css/color-names.ts"; -import { isExternalPath } from "../url.ts"; +import { isExternalPath, tryDecodeURI } from "../url.ts"; import { LogoLightDarkSpecifierPathOptional, LogoOptionsPathOptional, @@ -486,7 +486,7 @@ export function resolveInputRelativeLogo( } // Logo paths are URLs; check the files they point to, as HTML resource // processing does, but keep the path encoded in the output. - const filePath = decodeUriPath(options.path); + const filePath = tryDecodeURI(options.path) ?? options.path; if ( existsSync(join(inputDir, filePath)) || !existsSync(join(projectDir, filePath)) @@ -509,18 +509,6 @@ export function resolveInputRelativeLogo( }; } -// Malformed escapes are left as-is, like getDecodedAttribute in core/html.ts. -function decodeUriPath(path: string): string { - try { - return decodeURI(path); - } catch (e) { - if (e instanceof URIError) { - return path; - } - throw e; - } -} - function brandWithLogoPaths( brand: LightDarkBrand | undefined, transformPath: (path: string, projectDir: string) => string, diff --git a/src/core/html.ts b/src/core/html.ts index 3a425934e01..dfbc87bcf18 100644 --- a/src/core/html.ts +++ b/src/core/html.ts @@ -9,6 +9,7 @@ import { Document, Element } from "./deno-dom.ts"; import { pandocAutoIdentifier } from "./pandoc/pandoc-id.ts"; import { isFileRef } from "./http.ts"; +import { tryDecodeURI } from "./url.ts"; import { cssFileRefs } from "./css.ts"; import { HtmlPostProcessResult } from "../command/render/types.ts"; import { warning } from "../deno_ral/log.ts"; @@ -20,18 +21,14 @@ export function asHtmlId(text: string) { export function getDecodedAttribute(element: Element, attrib: string) { const value = element.getAttribute(attrib); if (value) { - try { - return decodeURI(value); - } catch (e) { - if (e instanceof URIError) { - warning( - `Invalid URI '${value}' in attribute '${attrib}' of element '${element.tagName}'`, - ); - return value; - } else { - throw e; - } + const decoded = tryDecodeURI(value); + if (decoded === undefined) { + warning( + `Invalid URI '${value}' in attribute '${attrib}' of element '${element.tagName}'`, + ); + return value; } + return decoded; } else { return value; } diff --git a/src/core/url.ts b/src/core/url.ts index 2b3c5caf714..a8a91427c30 100644 --- a/src/core/url.ts +++ b/src/core/url.ts @@ -14,6 +14,18 @@ export function isExternalPath(path: string) { return /^\w+:/.test(path); } +// Returns undefined when the value contains a malformed escape sequence. +export function tryDecodeURI(value: string): string | undefined { + try { + return decodeURI(value); + } catch (e) { + if (e instanceof URIError) { + return undefined; + } + throw e; + } +} + export function joinUrl(baseUrl: string, path: string) { const baseHasSlash = baseUrl.endsWith("/"); From acdf0b1bdac8aab7a9ea8e0c2cbf9e4648cba322 Mon Sep 17 00:00:00 2001 From: Christophe Dervieux Date: Tue, 6 Oct 2026 18:26:50 +0200 Subject: [PATCH 4/8] Ignore render scratch files in smoke-all test project directories --- tests/docs/smoke-all/2024/07/09/issue-10251/.gitignore | 2 ++ tests/docs/smoke-all/2024/10/30/issue-10864/.gitignore | 2 ++ tests/docs/smoke-all/brand/test-01/.gitignore | 2 ++ 3 files changed, 6 insertions(+) create mode 100644 tests/docs/smoke-all/2024/07/09/issue-10251/.gitignore create mode 100644 tests/docs/smoke-all/2024/10/30/issue-10864/.gitignore diff --git a/tests/docs/smoke-all/2024/07/09/issue-10251/.gitignore b/tests/docs/smoke-all/2024/07/09/issue-10251/.gitignore new file mode 100644 index 00000000000..ad293093b07 --- /dev/null +++ b/tests/docs/smoke-all/2024/07/09/issue-10251/.gitignore @@ -0,0 +1,2 @@ +/.quarto/ +**/*.quarto_ipynb diff --git a/tests/docs/smoke-all/2024/10/30/issue-10864/.gitignore b/tests/docs/smoke-all/2024/10/30/issue-10864/.gitignore new file mode 100644 index 00000000000..ad293093b07 --- /dev/null +++ b/tests/docs/smoke-all/2024/10/30/issue-10864/.gitignore @@ -0,0 +1,2 @@ +/.quarto/ +**/*.quarto_ipynb diff --git a/tests/docs/smoke-all/brand/test-01/.gitignore b/tests/docs/smoke-all/brand/test-01/.gitignore index 075b2542afb..0e3521a7d0f 100644 --- a/tests/docs/smoke-all/brand/test-01/.gitignore +++ b/tests/docs/smoke-all/brand/test-01/.gitignore @@ -1 +1,3 @@ /.quarto/ + +**/*.quarto_ipynb From 0246569f9288bc66db92f714e91fb76a9dcbcb14 Mon Sep 17 00:00:00 2001 From: Christophe Dervieux Date: Tue, 6 Oct 2026 18:41:27 +0200 Subject: [PATCH 5/8] Ignore query and fragment when checking a project-relative logo path The deprecated project-relative logo fallback checked `img/logo.png?v=1` or `img/logo.svg#id` as literal file names. On Windows `?` is not a valid file name character, so the existence check threw and the render failed, where 1.10 rendered `../img/logo.png?v=1`. Elsewhere the check simply missed and left a broken path. The file checks now use only the path part, and the query and fragment are appended back to the rebased path. --- src/core/brand/brand.ts | 10 ++++++---- .../projrel/fragment-slides.qmd | 18 ++++++++++++++++++ .../projrel/query-slides.qmd | 18 ++++++++++++++++++ 3 files changed, 42 insertions(+), 4 deletions(-) create mode 100644 tests/docs/smoke-all/brand/logo/extension-format-logo-subdir/projrel/fragment-slides.qmd create mode 100644 tests/docs/smoke-all/brand/logo/extension-format-logo-subdir/projrel/query-slides.qmd diff --git a/src/core/brand/brand.ts b/src/core/brand/brand.ts index a8d6db0f0de..72666333350 100644 --- a/src/core/brand/brand.ts +++ b/src/core/brand/brand.ts @@ -485,8 +485,10 @@ export function resolveInputRelativeLogo( return options; } // Logo paths are URLs; check the files they point to, as HTML resource - // processing does, but keep the path encoded in the output. - const filePath = tryDecodeURI(options.path) ?? options.path; + // processing does, but keep the path encoded in the output. A query or + // fragment is not part of the file name. + const [, urlPath, suffix] = options.path.match(/^([^?#]*)(.*)$/)!; + const filePath = tryDecodeURI(urlPath) ?? urlPath; if ( existsSync(join(inputDir, filePath)) || !existsSync(join(projectDir, filePath)) @@ -494,8 +496,8 @@ export function resolveInputRelativeLogo( return options; } const path = pathWithForwardSlashes( - relative(inputDir, join(projectDir, options.path)), - ); + relative(inputDir, join(projectDir, urlPath)), + ) + suffix; warnOnce( `Logo path '${options.path}' for ${ pathWithForwardSlashes(relative(projectDir, input)) diff --git a/tests/docs/smoke-all/brand/logo/extension-format-logo-subdir/projrel/fragment-slides.qmd b/tests/docs/smoke-all/brand/logo/extension-format-logo-subdir/projrel/fragment-slides.qmd new file mode 100644 index 00000000000..ab63b875ab5 --- /dev/null +++ b/tests/docs/smoke-all/brand/logo/extension-format-logo-subdir/projrel/fragment-slides.qmd @@ -0,0 +1,18 @@ +--- +title: "Project-relative logo with a fragment in subdirectory (deprecated)" +format: revealjs +logo: img/top.png#frag +_quarto: + tests: + revealjs: + noErrors: true + printsMessage: + level: WARN + regex: 'img/top\.png#frag.*project' + ensureFileRegexMatches: + - + - ' Date: Tue, 6 Oct 2026 18:54:37 +0200 Subject: [PATCH 6/8] Prefer the document-relative logo when a project file has the same path A document logo path that exists both next to the document and at the project root now resolves to the document's file, silently. Versions 1.8 to 1.10 picked the project-root file in branded projects, only because every logo path got a leading '/'. Document-relative wins in 1.7 and in any project without a brand, so this keeps the documented rule instead of the accidental one; the project-relative fallback only applies when the document-relative file is missing. --- src/core/brand/brand.ts | 2 +- .../collision/img/top.png | Bin 0 -> 11810 bytes .../collision/slides.qmd | 20 ++++++++++++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 tests/docs/smoke-all/brand/logo/extension-format-logo-subdir/collision/img/top.png create mode 100644 tests/docs/smoke-all/brand/logo/extension-format-logo-subdir/collision/slides.qmd diff --git a/src/core/brand/brand.ts b/src/core/brand/brand.ts index 72666333350..cdf8b6a397e 100644 --- a/src/core/brand/brand.ts +++ b/src/core/brand/brand.ts @@ -477,7 +477,7 @@ export function resolveInputRelativeLogo( } // Document logo paths that only exist relative to the project directory // are deprecated: in a website with an active brand they still resolve, - // with a warning. + // with a warning. A file relative to the document always takes precedence. const projectRelativeFallback = (options: LogoOptions | undefined) => { if ( !options || isExternalPath(options.path) || isAbsolute(options.path) diff --git a/tests/docs/smoke-all/brand/logo/extension-format-logo-subdir/collision/img/top.png b/tests/docs/smoke-all/brand/logo/extension-format-logo-subdir/collision/img/top.png new file mode 100644 index 0000000000000000000000000000000000000000..616d17b92cb7f142afa346bb27bb505ad26ae908 GIT binary patch literal 11810 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

2y z6(;TAA{R3KcB}wRG4H+QOr+t_LDvIpzj{dzWRSqu(GVmF@Gyj(#fcO)#^Uq(zK>nE zX|dg_q14B;je3ywKyQfUbDPWB4V6Lp$@C*8X|$stF9e7XH=iEp6wBP;v$|J|XEn;j zM3FY}4GBuJ?Hsc`gXA7%?hS2jpeXv}+05HO^FLYsyamDkBB!z=p!n5aH-aY2G;ZG5 zG6)fmM0#BO5%1W^+v#iMn3LnTfCRuxtoUjIQxv1jsdlE=!oZq7;Je04}os{ynlWxjdcZl0%-!;RP>(LvRONf-1}wD4d>A!%64tG>iWsj+WRum0{XrgG8MLI z(c^ck)6M_k%@E=5P-i(7hy*D-IPT1JDmx}X>UaI z10VvJTvm1Gw<^^(bKngP1#c^D-1fI&msaJcPTpT~h~wtP5F0v3TtGgFwDu|fqjj=B oA4&gkm;JGV|Cgq2w0sl##jV&$be9bIfW84T5{lwgqK1M02Ttn$=Kufz literal 0 HcmV?d00001 diff --git a/tests/docs/smoke-all/brand/logo/extension-format-logo-subdir/collision/slides.qmd b/tests/docs/smoke-all/brand/logo/extension-format-logo-subdir/collision/slides.qmd new file mode 100644 index 00000000000..9f07c497971 --- /dev/null +++ b/tests/docs/smoke-all/brand/logo/extension-format-logo-subdir/collision/slides.qmd @@ -0,0 +1,20 @@ +--- +title: "Logo path existing both relative to the document and to the project" +format: revealjs +logo: img/top.png +_quarto: + tests: + revealjs: + noErrorsOrWarnings: true + fileExists: + outputPath: img/top.png + ensureFileRegexMatches: + - + - ' Date: Tue, 6 Oct 2026 18:54:50 +0200 Subject: [PATCH 7/8] Assert extension logos are copied into site_libs The reported failure includes the logo never being copied, which the HTML src regex alone does not check. Rendered alone on main, both subdirectory documents leave site_libs/quarto-contrib/.../logo.png missing. --- .../smoke-all/brand/logo/extension-format-logo-subdir/index.qmd | 2 ++ .../brand/logo/extension-format-logo-subdir/sub/dashboard.qmd | 2 ++ .../brand/logo/extension-format-logo-subdir/sub/slides.qmd | 2 ++ 3 files changed, 6 insertions(+) diff --git a/tests/docs/smoke-all/brand/logo/extension-format-logo-subdir/index.qmd b/tests/docs/smoke-all/brand/logo/extension-format-logo-subdir/index.qmd index fe3978b7753..8ed1cce4a02 100644 --- a/tests/docs/smoke-all/brand/logo/extension-format-logo-subdir/index.qmd +++ b/tests/docs/smoke-all/brand/logo/extension-format-logo-subdir/index.qmd @@ -4,6 +4,8 @@ format: repro-revealjs _quarto: tests: repro-revealjs: + fileExists: + outputPath: site_libs/quarto-contrib/quarto-project/repro/logo.png ensureFileRegexMatches: - - ' Date: Tue, 6 Oct 2026 19:07:20 +0200 Subject: [PATCH 8/8] Don't fail the render when the logo fallback can't stat a path On Windows, stat throws (os error 123) for a path containing a character that is invalid in a filename, such as '*', so a branded website document in a subdirectory with such a logo path failed to render. The check only serves the deprecated project-relative fallback; treating an unstat-able path as missing leaves it as written, as 1.10 did. --- src/core/brand/brand.ts | 11 +++++++---- .../projrel/invalid-filename-slides.qmd | 17 +++++++++++++++++ 2 files changed, 24 insertions(+), 4 deletions(-) create mode 100644 tests/docs/smoke-all/brand/logo/extension-format-logo-subdir/projrel/invalid-filename-slides.qmd diff --git a/src/core/brand/brand.ts b/src/core/brand/brand.ts index cdf8b6a397e..6e1604512ec 100644 --- a/src/core/brand/brand.ts +++ b/src/core/brand/brand.ts @@ -36,7 +36,6 @@ import { relative, resolve, } from "../../deno_ral/path.ts"; -import { existsSync } from "../../deno_ral/fs.ts"; import { warnOnce } from "../log.ts"; import { isCssColorName } from "../css/color-names.ts"; import { isExternalPath, tryDecodeURI } from "../url.ts"; @@ -46,7 +45,11 @@ import { LogoSpecifier, LogoSpecifierPathOptional, } from "../../resources/types/schema-types.ts"; -import { ensureLeadingSlash, pathWithForwardSlashes } from "../path.ts"; +import { + ensureLeadingSlash, + pathWithForwardSlashes, + safeExistsSync, +} from "../path.ts"; type ProcessedBrandData = { color: Record; @@ -490,8 +493,8 @@ export function resolveInputRelativeLogo( const [, urlPath, suffix] = options.path.match(/^([^?#]*)(.*)$/)!; const filePath = tryDecodeURI(urlPath) ?? urlPath; if ( - existsSync(join(inputDir, filePath)) || - !existsSync(join(projectDir, filePath)) + safeExistsSync(join(inputDir, filePath)) || + !safeExistsSync(join(projectDir, filePath)) ) { return options; } diff --git a/tests/docs/smoke-all/brand/logo/extension-format-logo-subdir/projrel/invalid-filename-slides.qmd b/tests/docs/smoke-all/brand/logo/extension-format-logo-subdir/projrel/invalid-filename-slides.qmd new file mode 100644 index 00000000000..54acfb7bba6 --- /dev/null +++ b/tests/docs/smoke-all/brand/logo/extension-format-logo-subdir/projrel/invalid-filename-slides.qmd @@ -0,0 +1,17 @@ +--- +title: "Logo path that is not a valid Windows filename" +format: revealjs +logo: img/top*.png +_quarto: + tests: + revealjs: + noErrors: true + ensureFileRegexMatches: + - + - '