Skip to content

Commit 5645620

Browse files
committed
Add mermaid-scale option to resize Mermaid images
Mermaid diagrams rendered as png or svg images are sized at their natural size at 96 dpi, so their 16px text prints at 12pt and wide diagrams can run past the page. fig-width scales each diagram by a different factor, which gives every diagram a different text size. mermaid-scale takes a percentage, such as 75%, and multiplies the natural width and height by it, so all diagrams keep the same text size. It can be set in the document front matter or on a single cell, where it overrides the document value. An explicit fig-width or fig-height still takes precedence. With mermaid-format: js the browser sizes the diagram, so the option has no effect there. Both the cell and front matter schemas validate the value, so a bare number or other string fails with a clear error instead of a crash.
1 parent 5b23163 commit 5645620

12 files changed

Lines changed: 208 additions & 21 deletions

File tree

‎news/changelog-1.11.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ All changes included in 1.11:
5252
- ([#14772](https://github.com/quarto-dev/quarto-cli/issues/14772)): Custom `language` keys now reach templates through `$quarto.language.<key>$`. A key that Quarto does not ship passed schema validation and was merged over the defaults, then discarded before templates saw it, so it expanded to an empty string with no warning. (author: @mcanouil)
5353
- ([#14882](https://github.com/quarto-dev/quarto-cli/issues/14882)): Fix brand font families with a word starting with a digit, such as `Source Sans 3`, not applying in `html` and `revealjs` output. The same names in `mainfont` now also apply in `revealjs`. (author: @Masad791)
5454
- ([#14891](https://github.com/quarto-dev/quarto-cli/issues/14891)): Fix a brand file that holds only comments, such as a commented-out `_brand.yml` template, stopping the render with `YAML value is missing`. It now prints a warning and is ignored. (author: @Masad791)
55+
- ([#NNNNN](https://github.com/quarto-dev/quarto-cli/pull/NNNNN)): Add a `mermaid-scale` option, such as `mermaid-scale: 75%`, that resizes Mermaid diagrams rendered as `png` or `svg` images by a percentage of their natural size, at the document or cell level. An explicit `fig-width` or `fig-height` still takes precedence. (author: @faysou)
5556

5657
### `dashboard`
5758

‎src/config/constants.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export const kFigDpi = "fig-dpi";
2424
export const kFigAsp = "fig-asp";
2525

2626
export const kMermaidFormat = "mermaid-format";
27+
export const kMermaidScale = "mermaid-scale";
2728
export const kDfPrint = "df-print";
2829

2930
export const kCache = "cache";
@@ -170,6 +171,7 @@ export const kExecuteDefaultsKeys = [
170171
kFigDpi,
171172
kFigAsp,
172173
kMermaidFormat,
174+
kMermaidScale,
173175
kDfPrint,
174176
kError,
175177
kEval,

‎src/config/types.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ import {
157157
kMathml,
158158
kMergeIncludes,
159159
kMermaidFormat,
160+
kMermaidScale,
160161
kNavigationBreadcrumbsLabel,
161162
kNavigationSiteLabel,
162163
kNavigationPageLabel,
@@ -544,6 +545,7 @@ export interface FormatExecute {
544545
[kFigDpi]?: number;
545546
[kFigAsp]?: number;
546547
[kMermaidFormat]?: "png" | "svg" | "js";
548+
[kMermaidScale]?: string;
547549
[kDfPrint]?: "default" | "kable" | "tibble" | "paged";
548550
[kCache]?: true | false | "refresh" | null;
549551
[kFreeze]?: true | false | "auto";

‎src/core/handlers/mermaid.ts‎

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import {
3535
kFigResponsive,
3636
kFigWidth,
3737
kMermaidFormat,
38+
kMermaidScale,
3839
} from "../../config/constants.ts";
3940
import { Element } from "../deno-dom.ts";
4041
import { convertFromYaml } from "../lib/yaml-schema/from-yaml.ts";
@@ -59,6 +60,9 @@ object:
5960
properties:
6061
mermaid-format:
6162
enum: [png, svg, js]
63+
mermaid-scale:
64+
string:
65+
pattern: "^[0-9]+(\\\\.[0-9]+)?%$"
6266
theme:
6367
anyOf:
6468
- null
@@ -237,6 +241,25 @@ mermaid.initialize(${JSON.stringify(mermaidOpts)});
237241
const responsive = handlerContext.options.context.format.metadata
238242
?.[kFigResponsive];
239243

244+
// mermaid-scale resizes the diagram's natural size; an explicit
245+
// fig-width or fig-height still takes precedence.
246+
const resolveScaledSize = async (svgText: string) => {
247+
const size = await resolveSize(svgText, cell.options ?? {});
248+
const scale = (options[kMermaidScale] ||
249+
handlerContext.options.format.execute[kMermaidScale]) as
250+
| string
251+
| undefined;
252+
if (!scale || size.explicitWidth || size.explicitHeight) {
253+
return size;
254+
}
255+
const factor = Number(scale.slice(0, -1)) / 100;
256+
return {
257+
...size,
258+
widthInInches: size.widthInInches * factor,
259+
heightInInches: size.heightInInches * factor,
260+
};
261+
};
262+
240263
const makeSvg = async () => {
241264
// Extract and process SVG
242265
let svg = asMappedString(
@@ -350,7 +373,7 @@ mermaid.initialize(${JSON.stringify(mermaidOpts)});
350373
const {
351374
widthInInches,
352375
heightInInches,
353-
} = await resolveSize(svg.value, cell.options ?? {});
376+
} = await resolveScaledSize(svg.value);
354377

355378
return asMappedString(
356379
makeFigLink(sourceName, widthInInches, heightInInches, true),
@@ -364,7 +387,7 @@ mermaid.initialize(${JSON.stringify(mermaidOpts)});
364387
svg,
365388
options,
366389
undefined,
367-
new Set([kFigWidth, kFigHeight, kMermaidFormat]),
390+
new Set([kFigWidth, kFigHeight, kMermaidFormat, kMermaidScale]),
368391
);
369392
}
370393
};
@@ -385,7 +408,7 @@ mermaid.initialize(${JSON.stringify(mermaidOpts)});
385408
const {
386409
widthInInches,
387410
heightInInches,
388-
} = await resolveSize(svgText, cell.options ?? {});
411+
} = await resolveScaledSize(svgText);
389412

390413
if (isMarkdownOutput(handlerContext.options.format, ["gfm"])) {
391414
return asMappedString(makeFigLink(
@@ -405,7 +428,7 @@ mermaid.initialize(${JSON.stringify(mermaidOpts)});
405428
)),
406429
options,
407430
undefined,
408-
new Set([kFigWidth, kFigHeight, kMermaidFormat]),
431+
new Set([kFigWidth, kFigHeight, kMermaidFormat, kMermaidScale]),
409432
);
410433
}
411434
};
@@ -448,7 +471,7 @@ mermaid.initialize(${JSON.stringify(mermaidOpts)});
448471
rawHtmlBlock.mappedString(),
449472
options,
450473
attrs,
451-
new Set([kMermaidFormat]),
474+
new Set([kMermaidFormat, kMermaidScale]),
452475
);
453476
};
454477

‎src/format/formats-shared.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ import {
6161
kLinkExternalNewwindow,
6262
kMergeIncludes,
6363
kMermaidFormat,
64+
kMermaidScale,
6465
kOutput,
6566
kOutputDivs,
6667
kOutputExt,
@@ -206,6 +207,7 @@ function defaultFormat(displayName: string): Format {
206207
[kFigDpi]: 96,
207208
[kFigAsp]: undefined,
208209
[kMermaidFormat]: undefined,
210+
[kMermaidScale]: undefined,
209211
[kDfPrint]: "default",
210212
[kError]: false,
211213
[kEval]: true,

‎src/resources/editor/tools/vs-code.mjs‎

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17492,6 +17492,18 @@ var require_yaml_intelligence_resources = __commonJS({
1749217492
}
1749317493
},
1749417494
description: "Mermaid diagram options"
17495+
},
17496+
{
17497+
name: "mermaid-scale",
17498+
schema: {
17499+
string: {
17500+
pattern: "^[0-9]+(\\.[0-9]+)?%$"
17501+
}
17502+
},
17503+
description: {
17504+
short: "Scale for Mermaid diagrams rendered as images",
17505+
long: "Percentage of their natural size at which Mermaid diagrams rendered\nwith `mermaid-format: png` or `svg` are shown, for example `75%`.\nAn explicit `fig-width` or `fig-height` takes precedence.\n"
17506+
}
1749517507
}
1749617508
],
1749717509
"schema/document-metadata.yml": [
@@ -24397,6 +24409,10 @@ var require_yaml_intelligence_resources = __commonJS({
2439724409
"Automatically generate the contents of a page from a list of Quarto\ndocuments or other custom data.",
2439824410
"Mermaid diagram options",
2439924411
"The mermaid built-in theme to use.",
24412+
{
24413+
short: "Scale for Mermaid diagrams rendered as images",
24414+
long: "Percentage of their natural size at which Mermaid diagrams rendered\nwith <code>mermaid-format: png</code> or <code>svg</code> are shown, for\nexample <code>75%</code>. An explicit <code>fig-width</code> or\n<code>fig-height</code> takes precedence."
24415+
},
2440024416
"List of keywords to be included in the document metadata.",
2440124417
"The document subject",
2440224418
"The subject text.",
@@ -25827,12 +25843,12 @@ var require_yaml_intelligence_resources = __commonJS({
2582725843
mermaid: "%%"
2582825844
},
2582925845
"handlers/mermaid/schema.yml": {
25830-
_internalId: 221295,
25846+
_internalId: 221618,
2583125847
type: "object",
2583225848
description: "be an object",
2583325849
properties: {
2583425850
"mermaid-format": {
25835-
_internalId: 221287,
25851+
_internalId: 221607,
2583625852
type: "enum",
2583725853
enum: [
2583825854
"png",
@@ -25847,8 +25863,14 @@ var require_yaml_intelligence_resources = __commonJS({
2584725863
],
2584825864
exhaustiveCompletions: true
2584925865
},
25866+
"mermaid-scale": {
25867+
_internalId: 221610,
25868+
type: "string",
25869+
pattern: "^[0-9]+(\\.[0-9]+)?%$",
25870+
description: 'be a string that satisfies regex "^[0-9]+(\\.[0-9]+)?%$"'
25871+
},
2585025872
theme: {
25851-
_internalId: 221294,
25873+
_internalId: 221617,
2585225874
type: "anyOf",
2585325875
anyOf: [
2585425876
{
@@ -25869,9 +25891,9 @@ var require_yaml_intelligence_resources = __commonJS({
2586925891
},
2587025892
patternProperties: {},
2587125893
propertyNames: {
25872-
errorMessage: "property ${value} does not match case convention mermaid-format,theme",
25894+
errorMessage: "property ${value} does not match case convention mermaid-format,mermaid-scale,theme",
2587325895
type: "string",
25874-
pattern: "(?!(^mermaid_format$|^mermaidFormat$))",
25896+
pattern: "(?!(^mermaid_format$|^mermaidFormat$|^mermaid_scale$|^mermaidScale$))",
2587525897
tags: {
2587625898
"case-convention": [
2587725899
"dash-case"

‎src/resources/editor/tools/yaml/all-schema-definitions.json‎

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

‎src/resources/editor/tools/yaml/web-worker.js‎

Lines changed: 27 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎src/resources/editor/tools/yaml/yaml-intelligence-resources.json‎

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10460,6 +10460,18 @@
1046010460
}
1046110461
},
1046210462
"description": "Mermaid diagram options"
10463+
},
10464+
{
10465+
"name": "mermaid-scale",
10466+
"schema": {
10467+
"string": {
10468+
"pattern": "^[0-9]+(\\.[0-9]+)?%$"
10469+
}
10470+
},
10471+
"description": {
10472+
"short": "Scale for Mermaid diagrams rendered as images",
10473+
"long": "Percentage of their natural size at which Mermaid diagrams rendered\nwith `mermaid-format: png` or `svg` are shown, for example `75%`.\nAn explicit `fig-width` or `fig-height` takes precedence.\n"
10474+
}
1046310475
}
1046410476
],
1046510477
"schema/document-metadata.yml": [
@@ -17365,6 +17377,10 @@
1736517377
"Automatically generate the contents of a page from a list of Quarto\ndocuments or other custom data.",
1736617378
"Mermaid diagram options",
1736717379
"The mermaid built-in theme to use.",
17380+
{
17381+
"short": "Scale for Mermaid diagrams rendered as images",
17382+
"long": "Percentage of their natural size at which Mermaid diagrams rendered\nwith <code>mermaid-format: png</code> or <code>svg</code> are shown, for\nexample <code>75%</code>. An explicit <code>fig-width</code> or\n<code>fig-height</code> takes precedence."
17383+
},
1736817384
"List of keywords to be included in the document metadata.",
1736917385
"The document subject",
1737017386
"The subject text.",
@@ -18795,12 +18811,12 @@
1879518811
"mermaid": "%%"
1879618812
},
1879718813
"handlers/mermaid/schema.yml": {
18798-
"_internalId": 221295,
18814+
"_internalId": 221618,
1879918815
"type": "object",
1880018816
"description": "be an object",
1880118817
"properties": {
1880218818
"mermaid-format": {
18803-
"_internalId": 221287,
18819+
"_internalId": 221607,
1880418820
"type": "enum",
1880518821
"enum": [
1880618822
"png",
@@ -18815,8 +18831,14 @@
1881518831
],
1881618832
"exhaustiveCompletions": true
1881718833
},
18834+
"mermaid-scale": {
18835+
"_internalId": 221610,
18836+
"type": "string",
18837+
"pattern": "^[0-9]+(\\.[0-9]+)?%$",
18838+
"description": "be a string that satisfies regex \"^[0-9]+(\\.[0-9]+)?%$\""
18839+
},
1881818840
"theme": {
18819-
"_internalId": 221294,
18841+
"_internalId": 221617,
1882018842
"type": "anyOf",
1882118843
"anyOf": [
1882218844
{
@@ -18837,9 +18859,9 @@
1883718859
},
1883818860
"patternProperties": {},
1883918861
"propertyNames": {
18840-
"errorMessage": "property ${value} does not match case convention mermaid-format,theme",
18862+
"errorMessage": "property ${value} does not match case convention mermaid-format,mermaid-scale,theme",
1884118863
"type": "string",
18842-
"pattern": "(?!(^mermaid_format$|^mermaidFormat$))",
18864+
"pattern": "(?!(^mermaid_format$|^mermaidFormat$|^mermaid_scale$|^mermaidScale$))",
1884318865
"tags": {
1884418866
"case-convention": [
1884518867
"dash-case"

‎src/resources/schema/document-mermaid.yml‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,14 @@
88
enum: ["default", "dark", "forest", "neutral"]
99
description: The mermaid built-in theme to use.
1010
description: Mermaid diagram options
11+
12+
- name: mermaid-scale
13+
schema:
14+
string:
15+
pattern: "^[0-9]+(\\.[0-9]+)?%$"
16+
description:
17+
short: "Scale for Mermaid diagrams rendered as images"
18+
long: |
19+
Percentage of their natural size at which Mermaid diagrams rendered
20+
with `mermaid-format: png` or `svg` are shown, for example `75%`.
21+
An explicit `fig-width` or `fig-height` takes precedence.

0 commit comments

Comments
 (0)