@imontene/mdrender 2.2.0 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/README.md +12 -3
- package/dist/assets/theme.css +3 -2
- package/dist/assets.d.ts +2 -0
- package/dist/assets.js +7 -1
- package/dist/cli.js +3 -1
- package/dist/convert.js +17 -3
- package/dist/doctor.js +3 -2
- package/dist/options.d.ts +4 -0
- package/dist/options.js +3 -0
- package/dist/render/fonts.d.ts +18 -4
- package/dist/render/fonts.js +46 -13
- package/dist/render/header-footer.d.ts +3 -2
- package/dist/render/header-footer.js +5 -5
- package/dist/render/mermaid.d.ts +3 -1
- package/dist/render/mermaid.js +13 -11
- package/dist/render/template.d.ts +3 -0
- package/dist/render/template.js +1 -1
- package/package.json +4 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Cambios
|
|
2
2
|
|
|
3
|
+
## 2.3.0 — 2026-10-16
|
|
4
|
+
|
|
5
|
+
- **Manrope es la nueva tipografía por defecto** del texto, el encabezado, el pie y los
|
|
6
|
+
diagramas Mermaid. `--font inter` (o `font: inter`) conserva Inter, la de versiones
|
|
7
|
+
anteriores. Manrope no tiene cursivas (el navegador inclina la normal) y toma de Inter los
|
|
8
|
+
símbolos que le faltan (`→`, `✓`, `≤`…).
|
|
9
|
+
|
|
3
10
|
## 2.2.0 — 2026-10-15
|
|
4
11
|
|
|
5
12
|
- **Autoajuste de diagramas de flujo Mermaid** (`flowchart`/`graph`): cada diagrama se dibuja
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
**Convierte archivos Markdown (`.md`) en PDF y HTML desde la línea de comandos, en Windows y
|
|
4
4
|
Linux**, con ecuaciones LaTeX, diagramas Mermaid y la tipografía
|
|
5
|
-
[Inter](https://fonts.google.com/specimen/Inter).
|
|
5
|
+
[Manrope](https://fonts.google.com/specimen/Manrope) (o [Inter](https://fonts.google.com/specimen/Inter)).
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
8
|
mdrender informe.md # → informe.pdf
|
|
@@ -15,7 +15,8 @@ Ejemplo: [`examples/ejemplo.md`](examples/ejemplo.md) → [`examples/ejemplo.pdf
|
|
|
15
15
|
- 📄 **Markdown → PDF o HTML autocontenido** (fuentes, imágenes y diagramas incluidos).
|
|
16
16
|
- ➗ **Ecuaciones** `$…$` y `$$…$$` con KaTeX, dibujadas al convertir (sin JavaScript en la salida).
|
|
17
17
|
- 📊 **Diagramas Mermaid**: flujo, secuencia, clases, estados, ER, Gantt, torta, mapa mental…
|
|
18
|
-
- 🔤 **
|
|
18
|
+
- 🔤 **Manrope** para el texto (o **Inter** con `--font inter`) y **JetBrains Mono** para el
|
|
19
|
+
código, incrustadas en el PDF.
|
|
19
20
|
- 🧩 **GFM**: tablas, listas de tareas, tachado, notas al pie, alertas `> [!NOTE]`,
|
|
20
21
|
resaltado de código para ~35 lenguajes.
|
|
21
22
|
- 🖨️ Tamaño de página, márgenes, orientación, encabezado/pie con números de página,
|
|
@@ -151,6 +152,7 @@ equipo); `--port` fija el puerto y `--no-open` no abre el navegador.
|
|
|
151
152
|
| `--header <texto>` | Encabezado de página (ver abajo) | — |
|
|
152
153
|
| `--footer <texto>` | Pie de página (ver abajo) | `{page} / {pages}` |
|
|
153
154
|
| `--no-page-numbers` | Sin el pie con números de página | — |
|
|
155
|
+
| `--font <manrope\|inter>` | Tipografía del texto (ver abajo) | `manrope` |
|
|
154
156
|
| `--density <densidad>` | `normal` o `compact` (menos espacio entre líneas y bloques) | `normal` |
|
|
155
157
|
| `--font-size <tamaño>` | Tamaño del texto en `pt` o `px`, de 6 a 24 pt; p. ej. `10pt` | `10.5pt` |
|
|
156
158
|
| `--theme <light\|dark\|css>` | `light`, `dark` o un `.css` que reemplaza el tema | `light` |
|
|
@@ -175,6 +177,13 @@ equipo); `--port` fija el puerto y `--no-open` no abre el navegador.
|
|
|
175
177
|
El encabezado y el pie se dibujan dentro de las márgenes superior e inferior: con menos de
|
|
176
178
|
10 mm pueden tocar el texto y mdrender lo advierte.
|
|
177
179
|
|
|
180
|
+
**Tipografía**: el texto usa [Manrope](https://fonts.google.com/specimen/Manrope);
|
|
181
|
+
`--font inter` (o `font: inter` en el front matter o en `mdrender.config.json`) vuelve a
|
|
182
|
+
[Inter](https://fonts.google.com/specimen/Inter), la tipografía de las versiones anteriores.
|
|
183
|
+
Ambas van incrustadas en el PDF, también en el encabezado, el pie y los diagramas. Manrope no
|
|
184
|
+
tiene cursivas (el navegador inclina la normal) ni algunos símbolos como `→`, `✓` o `≤`, que
|
|
185
|
+
se toman de Inter.
|
|
186
|
+
|
|
178
187
|
**Aprovechar la página**: `--density compact` reduce interlineado, espacios entre párrafos y
|
|
179
188
|
títulos, y `--font-size 10pt` achica el texto (los diagramas Mermaid siguen el mismo tamaño).
|
|
180
189
|
Juntos ahorran entre un 15 y un 25 % de páginas. Además, siempre:
|
|
@@ -318,6 +327,6 @@ cambios: [CHANGELOG.md](CHANGELOG.md).
|
|
|
318
327
|
|
|
319
328
|
## Licencia
|
|
320
329
|
|
|
321
|
-
[MIT](LICENSE). Inter y JetBrains Mono se distribuyen bajo la
|
|
330
|
+
[MIT](LICENSE). Manrope, Inter y JetBrains Mono se distribuyen bajo la
|
|
322
331
|
[SIL Open Font License 1.1](https://openfontlicense.org); los ejecutables incluyen
|
|
323
332
|
`THIRD-PARTY-NOTICES.txt` con las licencias de todos los componentes.
|
package/dist/assets/theme.css
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
/* mdrender light theme — Inter for text, JetBrains Mono for code. */
|
|
1
|
+
/* mdrender light theme — Manrope (or Inter) for text, JetBrains Mono for code. */
|
|
2
2
|
|
|
3
3
|
:root {
|
|
4
|
-
--font
|
|
4
|
+
/* mdrender sets both after the theme for the chosen --font (render/fonts.ts). */
|
|
5
|
+
--font-text: "Manrope", "Inter", system-ui, sans-serif;
|
|
5
6
|
/* Inter after the mono font covers symbols (arrows, math) missing from the code font. */
|
|
6
7
|
--font-code: "JetBrains Mono", "Inter", ui-monospace, monospace;
|
|
7
8
|
--color-text: #1f2328;
|
package/dist/assets.d.ts
CHANGED
|
@@ -31,6 +31,8 @@ export declare const INTER_FACES: readonly [{
|
|
|
31
31
|
readonly style: "italic";
|
|
32
32
|
readonly file: "Inter-BoldItalic.woff2";
|
|
33
33
|
}];
|
|
34
|
+
/** Manrope faces in use (Fontsource, split into unicode-range subsets); it has no italics. */
|
|
35
|
+
export declare const MANROPE_STYLESHEETS: string[];
|
|
34
36
|
export declare const MONO_STYLESHEETS: string[];
|
|
35
37
|
export declare const KATEX_CSS = "katex/dist/katex.min.css";
|
|
36
38
|
export declare const MERMAID_JS = "mermaid/dist/mermaid.min.js";
|
package/dist/assets.js
CHANGED
|
@@ -78,6 +78,12 @@ export const INTER_FACES = [
|
|
|
78
78
|
{ weight: 700, style: "normal", file: "Inter-Bold.woff2" },
|
|
79
79
|
{ weight: 700, style: "italic", file: "Inter-BoldItalic.woff2" },
|
|
80
80
|
];
|
|
81
|
+
/** Manrope faces in use (Fontsource, split into unicode-range subsets); it has no italics. */
|
|
82
|
+
export const MANROPE_STYLESHEETS = [
|
|
83
|
+
"@fontsource/manrope/400.css",
|
|
84
|
+
"@fontsource/manrope/600.css",
|
|
85
|
+
"@fontsource/manrope/700.css",
|
|
86
|
+
];
|
|
81
87
|
export const MONO_STYLESHEETS = [
|
|
82
88
|
"@fontsource/jetbrains-mono/400.css",
|
|
83
89
|
"@fontsource/jetbrains-mono/400-italic.css",
|
|
@@ -103,7 +109,7 @@ export function assetManifest() {
|
|
|
103
109
|
const keys = new Set([PACKAGE_JSON, ...THEMES, MERMAID_JS]);
|
|
104
110
|
for (const { file } of INTER_FACES)
|
|
105
111
|
keys.add(`${INTER_DIR}/${file}`);
|
|
106
|
-
for (const cssKey of [...MONO_STYLESHEETS, KATEX_CSS]) {
|
|
112
|
+
for (const cssKey of [...MANROPE_STYLESHEETS, ...MONO_STYLESHEETS, KATEX_CSS]) {
|
|
107
113
|
keys.add(cssKey);
|
|
108
114
|
for (const font of woff2References(cssKey, readTextAsset(cssKey)))
|
|
109
115
|
keys.add(font);
|
package/dist/cli.js
CHANGED
|
@@ -25,6 +25,7 @@ const DOCUMENT_FLAGS = [
|
|
|
25
25
|
"header",
|
|
26
26
|
"footer",
|
|
27
27
|
"pageNumbers",
|
|
28
|
+
"font",
|
|
28
29
|
"density",
|
|
29
30
|
"fontSize",
|
|
30
31
|
"css",
|
|
@@ -57,6 +58,7 @@ function addDocumentOptions(command) {
|
|
|
57
58
|
.option("--header <text>", 'page header, e.g. "{title} | | {date}"')
|
|
58
59
|
.option("--footer <text>", 'page footer (default: "{page} / {pages}")')
|
|
59
60
|
.option("--no-page-numbers", "no default page-number footer")
|
|
61
|
+
.option("--font <font>", "text typeface: manrope or inter (default: manrope)")
|
|
60
62
|
.option("--density <density>", "normal or compact (tighter spacing) (default: normal)")
|
|
61
63
|
.option("--font-size <size>", "text size, e.g. 10pt or 11pt (default: 10.5pt)")
|
|
62
64
|
.option("--theme <theme>", "light, dark, or a .css file replacing the theme (default: light)")
|
|
@@ -77,7 +79,7 @@ function configure(command, io) {
|
|
|
77
79
|
function buildRenderProgram(io) {
|
|
78
80
|
const program = new Command()
|
|
79
81
|
.name("mdrender")
|
|
80
|
-
.description("Render Markdown files to PDF (or self-contained HTML)
|
|
82
|
+
.description("Render Markdown files to PDF (or self-contained HTML) set in Manrope (or Inter).")
|
|
81
83
|
.version(version, "-V, --version")
|
|
82
84
|
.argument("<inputs...>", "Markdown files, folders or glob patterns (e.g. docs/**/*.md)")
|
|
83
85
|
.option("-o, --output <path>", "output file, or folder for several inputs")
|
package/dist/convert.js
CHANGED
|
@@ -12,6 +12,7 @@ import { BrowserSession } from "./render/browser.js";
|
|
|
12
12
|
import { DEFAULT_FOOTER, headerFooterTemplate } from "./render/header-footer.js";
|
|
13
13
|
import { fillMermaid, renderMermaid } from "./render/mermaid.js";
|
|
14
14
|
import { SMALL_SCALE } from "./render/mermaid-fit.js";
|
|
15
|
+
import { FONT_FAMILIES, fontStack } from "./render/fonts.js";
|
|
15
16
|
import { contentHeightPx, contentWidthPx, lengthToPx } from "./render/page.js";
|
|
16
17
|
import { buildHtmlDocument, builtinThemeCss, densityCss } from "./render/template.js";
|
|
17
18
|
import { writeFileAtomic } from "./write.js";
|
|
@@ -82,7 +83,16 @@ export function headerFooterSpaceWarnings(margin, hasHeader, hasFooter) {
|
|
|
82
83
|
/** Font size, if set, and the page's text height (used to fit images and diagrams). */
|
|
83
84
|
function layoutCss(options, margin) {
|
|
84
85
|
const height = contentHeightPx(options.pageSize, options.orientation === "landscape", margin);
|
|
85
|
-
const
|
|
86
|
+
const family = FONT_FAMILIES[options.font];
|
|
87
|
+
const rules = [
|
|
88
|
+
`:root { --page-content-height: ${height}px; }`,
|
|
89
|
+
`:root { --font-text: ${fontStack(options.font)}; ` +
|
|
90
|
+
`--font-code: "JetBrains Mono", "${family}", "Inter", ui-monospace, monospace; }`,
|
|
91
|
+
];
|
|
92
|
+
// Manrope's glyphs are taller than the headings' 1.25 line height (about 1.37 em). When a
|
|
93
|
+
// heading is pushed to the next page, Chrome then repeats its text in the PDF bookmark.
|
|
94
|
+
if (options.font === "manrope")
|
|
95
|
+
rules.push("h1, h2, h3, h4, h5, h6 { line-height: 1.4; }");
|
|
86
96
|
if (options.fontSize)
|
|
87
97
|
rules.push(`html { font-size: ${options.fontSize}; }`);
|
|
88
98
|
return rules.join("\n");
|
|
@@ -158,6 +168,7 @@ paged = false) {
|
|
|
158
168
|
: Infinity,
|
|
159
169
|
autofit: options.mermaidAutofit,
|
|
160
170
|
flip: options.mermaidFlip,
|
|
171
|
+
font: options.font,
|
|
161
172
|
});
|
|
162
173
|
const filled = fillMermaid(body, md.nonce, md.mermaid, results);
|
|
163
174
|
body = filled.html;
|
|
@@ -188,6 +199,7 @@ paged = false) {
|
|
|
188
199
|
date: options.date,
|
|
189
200
|
showTitle: options.title !== undefined,
|
|
190
201
|
themeCss,
|
|
202
|
+
font: options.font,
|
|
191
203
|
layoutCss: [...densityCss(options.density), layoutCss(options, margin)],
|
|
192
204
|
extraCss,
|
|
193
205
|
});
|
|
@@ -218,9 +230,11 @@ export async function convertFile(convert) {
|
|
|
218
230
|
landscape: options.orientation === "landscape",
|
|
219
231
|
margin,
|
|
220
232
|
headerTemplate: hasHeaderFooter
|
|
221
|
-
? headerFooterTemplate(options.header, values, margin)
|
|
233
|
+
? headerFooterTemplate(options.header, values, margin, options.font)
|
|
234
|
+
: undefined,
|
|
235
|
+
footerTemplate: hasHeaderFooter
|
|
236
|
+
? headerFooterTemplate(footer, values, margin, options.font)
|
|
222
237
|
: undefined,
|
|
223
|
-
footerTemplate: hasHeaderFooter ? headerFooterTemplate(footer, values, margin) : undefined,
|
|
224
238
|
allowRemote: convert.allowRemote,
|
|
225
239
|
});
|
|
226
240
|
for (const url of blocked) {
|
package/dist/doctor.js
CHANGED
|
@@ -135,8 +135,9 @@ function settingsCheck(options) {
|
|
|
135
135
|
name: "Settings",
|
|
136
136
|
status: "info",
|
|
137
137
|
detail: `${options.pageSize} ${options.orientation}, margins ${m.top} ${m.right} ${m.bottom} ` +
|
|
138
|
-
`${m.left} (top right bottom left), ${options.
|
|
139
|
-
`${options.fontSize ?? DEFAULT_FONT_SIZE}
|
|
138
|
+
`${m.left} (top right bottom left), ${options.font} ` +
|
|
139
|
+
`${options.fontSize ?? DEFAULT_FONT_SIZE}, ${options.density} density, ` +
|
|
140
|
+
`language ${options.lang}, theme ` +
|
|
140
141
|
path.basename(options.theme),
|
|
141
142
|
};
|
|
142
143
|
}
|
package/dist/options.d.ts
CHANGED
|
@@ -14,6 +14,8 @@ export type MermaidTheme = (typeof MERMAID_THEMES)[number];
|
|
|
14
14
|
export declare const MERMAID_FLIPS: readonly ["auto", "never"];
|
|
15
15
|
export type MermaidFlip = (typeof MERMAID_FLIPS)[number];
|
|
16
16
|
export declare const BUILTIN_THEMES: readonly ["light", "dark"];
|
|
17
|
+
export declare const TEXT_FONTS: readonly ["manrope", "inter"];
|
|
18
|
+
export type TextFont = (typeof TEXT_FONTS)[number];
|
|
17
19
|
export declare const DENSITIES: readonly ["normal", "compact"];
|
|
18
20
|
export type Density = (typeof DENSITIES)[number];
|
|
19
21
|
/** Rendering options, merged from defaults < config file < front matter < command line. */
|
|
@@ -41,6 +43,8 @@ export interface DocumentOptions {
|
|
|
41
43
|
mermaidAutofit: boolean;
|
|
42
44
|
/** Whether autofit may turn a flowchart to the other axis (LR ↔ TB) when it reads far better. */
|
|
43
45
|
mermaidFlip: MermaidFlip;
|
|
46
|
+
/** Typeface of the text: Manrope or Inter (code is always JetBrains Mono). */
|
|
47
|
+
font: TextFont;
|
|
44
48
|
/** Vertical spacing: "compact" tightens line height, paragraph and heading spacing. */
|
|
45
49
|
density: Density;
|
|
46
50
|
/** Base font size of the text, e.g. "10pt"; when unset, the theme's (10.5pt). */
|
package/dist/options.js
CHANGED
|
@@ -7,6 +7,7 @@ export const ORIENTATIONS = ["portrait", "landscape"];
|
|
|
7
7
|
export const MERMAID_THEMES = ["default", "neutral", "dark", "forest", "base"];
|
|
8
8
|
export const MERMAID_FLIPS = ["auto", "never"];
|
|
9
9
|
export const BUILTIN_THEMES = ["light", "dark"];
|
|
10
|
+
export const TEXT_FONTS = ["manrope", "inter"];
|
|
10
11
|
export const DENSITIES = ["normal", "compact"];
|
|
11
12
|
export const DEFAULT_OPTIONS = {
|
|
12
13
|
pageSize: "Letter",
|
|
@@ -17,6 +18,7 @@ export const DEFAULT_OPTIONS = {
|
|
|
17
18
|
pageNumbers: true,
|
|
18
19
|
mermaidAutofit: true,
|
|
19
20
|
mermaidFlip: "auto",
|
|
21
|
+
font: "manrope",
|
|
20
22
|
density: "normal",
|
|
21
23
|
theme: "light",
|
|
22
24
|
css: [],
|
|
@@ -115,6 +117,7 @@ const VALIDATORS = {
|
|
|
115
117
|
mermaidTheme: oneOf(MERMAID_THEMES),
|
|
116
118
|
mermaidAutofit: bool,
|
|
117
119
|
mermaidFlip: oneOf(MERMAID_FLIPS),
|
|
120
|
+
font: oneOf(TEXT_FONTS),
|
|
118
121
|
density: oneOf(DENSITIES),
|
|
119
122
|
fontSize,
|
|
120
123
|
theme: (value, describe, baseDir) => {
|
package/dist/render/fonts.d.ts
CHANGED
|
@@ -1,10 +1,24 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import type { TextFont } from "../options.js";
|
|
2
|
+
/**
|
|
3
|
+
* Static (non-variable) font files are used on purpose: Chromium embeds variable fonts in PDFs
|
|
4
|
+
* as Type 3 fonts, while static fonts are embedded as proper TrueType fonts.
|
|
5
|
+
*
|
|
6
|
+
* Text: Manrope (default) from Fontsource, or Inter 4 from the upstream release (full glyph
|
|
7
|
+
* set). Manrope covers Latin, Greek, Cyrillic and Vietnamese but has no italics (the browser
|
|
8
|
+
* slants it) nor arrows and math symbols such as → ✓ ≤: those fall back to Inter, which is
|
|
9
|
+
* embedded too in its regular and bold weights.
|
|
10
|
+
* Code: JetBrains Mono from Fontsource (split into unicode-range subsets).
|
|
11
|
+
*/
|
|
12
|
+
export declare const FONT_FAMILIES: Record<TextFont, string>;
|
|
13
|
+
/** CSS font-family list for the text: the chosen font, Inter for missing glyphs, the system's. */
|
|
14
|
+
export declare function fontStack(font: TextFont): string;
|
|
3
15
|
/**
|
|
4
16
|
* Inline a Fontsource stylesheet: the woff2 source becomes a data URI (the legacy woff fallback is
|
|
5
17
|
* dropped) so the document needs no network or file access. `font-display: block` makes the
|
|
6
18
|
* browser wait for the real font instead of printing with a fallback.
|
|
7
19
|
*/
|
|
8
20
|
export declare function inlineFontStylesheet(cssKey: string): string;
|
|
9
|
-
/** @font-face rules for every
|
|
10
|
-
export declare function fontFaceCss(): string;
|
|
21
|
+
/** @font-face rules for every font a document uses, with the font files embedded. */
|
|
22
|
+
export declare function fontFaceCss(font?: TextFont): string;
|
|
23
|
+
/** The text font's regular weight alone, for page headers/footers (rendered apart from the page). */
|
|
24
|
+
export declare function regularFontFace(font?: TextFont): string;
|
package/dist/render/fonts.js
CHANGED
|
@@ -1,11 +1,21 @@
|
|
|
1
|
-
import { INTER_DIR, INTER_FACES, MONO_STYLESHEETS, readAsset, readTextAsset, woff2References, } from "../assets.js";
|
|
1
|
+
import { INTER_DIR, INTER_FACES, MANROPE_STYLESHEETS, MONO_STYLESHEETS, readAsset, readTextAsset, woff2References, } from "../assets.js";
|
|
2
2
|
/**
|
|
3
3
|
* Static (non-variable) font files are used on purpose: Chromium embeds variable fonts in PDFs
|
|
4
4
|
* as Type 3 fonts, while static fonts are embedded as proper TrueType fonts.
|
|
5
5
|
*
|
|
6
|
-
* Text:
|
|
6
|
+
* Text: Manrope (default) from Fontsource, or Inter 4 from the upstream release (full glyph
|
|
7
|
+
* set). Manrope covers Latin, Greek, Cyrillic and Vietnamese but has no italics (the browser
|
|
8
|
+
* slants it) nor arrows and math symbols such as → ✓ ≤: those fall back to Inter, which is
|
|
9
|
+
* embedded too in its regular and bold weights.
|
|
7
10
|
* Code: JetBrains Mono from Fontsource (split into unicode-range subsets).
|
|
8
11
|
*/
|
|
12
|
+
export const FONT_FAMILIES = { manrope: "Manrope", inter: "Inter" };
|
|
13
|
+
/** CSS font-family list for the text: the chosen font, Inter for missing glyphs, the system's. */
|
|
14
|
+
export function fontStack(font) {
|
|
15
|
+
return font === "inter"
|
|
16
|
+
? `"Inter", system-ui, sans-serif`
|
|
17
|
+
: `"Manrope", "Inter", system-ui, sans-serif`;
|
|
18
|
+
}
|
|
9
19
|
function woff2DataUri(key) {
|
|
10
20
|
return `data:font/woff2;base64,${readAsset(key).toString("base64")}`;
|
|
11
21
|
}
|
|
@@ -18,12 +28,6 @@ function interFace({ weight, style, file }) {
|
|
|
18
28
|
src: url(${woff2DataUri(`${INTER_DIR}/${file}`)}) format("woff2");
|
|
19
29
|
}`;
|
|
20
30
|
}
|
|
21
|
-
let regularFace;
|
|
22
|
-
/** Inter Regular alone, for page headers/footers (Chrome renders them apart from the page). */
|
|
23
|
-
export function interRegularFontFace() {
|
|
24
|
-
regularFace ??= interFace(INTER_FACES[0]);
|
|
25
|
-
return regularFace;
|
|
26
|
-
}
|
|
27
31
|
/**
|
|
28
32
|
* Inline a Fontsource stylesheet: the woff2 source becomes a data URI (the legacy woff fallback is
|
|
29
33
|
* dropped) so the document needs no network or file access. `font-display: block` makes the
|
|
@@ -38,10 +42,39 @@ export function inlineFontStylesheet(cssKey) {
|
|
|
38
42
|
.replace(/,\s*url\([^)]+\.woff\)\s*format\(['"]woff['"]\)/g, "")
|
|
39
43
|
.replace(/url\((['"]?)([^)'"]+\.woff2)\1\)/g, () => `url(${woff2DataUri(fonts[index++])})`);
|
|
40
44
|
}
|
|
41
|
-
|
|
42
|
-
/** @font-face rules for
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
45
|
+
const interUpright = (weight) => INTER_FACES.find((face) => face.weight === weight && face.style === "normal");
|
|
46
|
+
/** @font-face rules for the text font and its fallback, with the font files embedded. */
|
|
47
|
+
function textFaces(font) {
|
|
48
|
+
if (font === "inter")
|
|
49
|
+
return INTER_FACES.map(interFace);
|
|
50
|
+
return [
|
|
51
|
+
...MANROPE_STYLESHEETS.map(inlineFontStylesheet),
|
|
52
|
+
// Fallback for glyphs Manrope lacks; 600 text takes the nearest heavier face, 700.
|
|
53
|
+
interFace(interUpright(400)),
|
|
54
|
+
interFace(interUpright(700)),
|
|
55
|
+
];
|
|
56
|
+
}
|
|
57
|
+
const cached = new Map();
|
|
58
|
+
/** @font-face rules for every font a document uses, with the font files embedded. */
|
|
59
|
+
export function fontFaceCss(font = "manrope") {
|
|
60
|
+
let css = cached.get(font);
|
|
61
|
+
if (css === undefined) {
|
|
62
|
+
css = [...textFaces(font), ...MONO_STYLESHEETS.map(inlineFontStylesheet)].join("\n");
|
|
63
|
+
cached.set(font, css);
|
|
64
|
+
}
|
|
65
|
+
return css;
|
|
66
|
+
}
|
|
67
|
+
const regularFaces = new Map();
|
|
68
|
+
/** The text font's regular weight alone, for page headers/footers (rendered apart from the page). */
|
|
69
|
+
export function regularFontFace(font = "manrope") {
|
|
70
|
+
let css = regularFaces.get(font);
|
|
71
|
+
if (css === undefined) {
|
|
72
|
+
css =
|
|
73
|
+
font === "inter"
|
|
74
|
+
? interFace(interUpright(400))
|
|
75
|
+
: [inlineFontStylesheet(MANROPE_STYLESHEETS[0]), interFace(interUpright(400))].join("\n");
|
|
76
|
+
regularFaces.set(font, css);
|
|
77
|
+
}
|
|
78
|
+
return css;
|
|
46
79
|
}
|
|
47
80
|
//# sourceMappingURL=fonts.js.map
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { TextFont } from "../options.js";
|
|
1
2
|
/**
|
|
2
3
|
* Header/footer text with placeholders, in up to three columns separated by `|`:
|
|
3
4
|
*
|
|
@@ -13,10 +14,10 @@ export interface HeaderFooterValues {
|
|
|
13
14
|
}
|
|
14
15
|
/**
|
|
15
16
|
* Chrome header/footer template. Chrome renders it apart from the page, so it carries its own
|
|
16
|
-
* styles and its own copy of
|
|
17
|
+
* styles and its own copy of the text font.
|
|
17
18
|
*/
|
|
18
19
|
export declare function headerFooterTemplate(spec: string | undefined, values: HeaderFooterValues, horizontalMargin: {
|
|
19
20
|
left: string;
|
|
20
21
|
right: string;
|
|
21
|
-
}): string;
|
|
22
|
+
}, font?: TextFont): string;
|
|
22
23
|
export declare const DEFAULT_FOOTER = "{page} / {pages}";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { escapeHtml } from "../html.js";
|
|
2
|
-
import {
|
|
2
|
+
import { fontStack, regularFontFace } from "./fonts.js";
|
|
3
3
|
const CHROME_CLASSES = { page: "pageNumber", pages: "totalPages" };
|
|
4
4
|
function renderText(text, values) {
|
|
5
5
|
return escapeHtml(text.trim()).replace(/\{(\w+)\}/g, (match, name) => {
|
|
@@ -21,15 +21,15 @@ function columns(spec) {
|
|
|
21
21
|
}
|
|
22
22
|
/**
|
|
23
23
|
* Chrome header/footer template. Chrome renders it apart from the page, so it carries its own
|
|
24
|
-
* styles and its own copy of
|
|
24
|
+
* styles and its own copy of the text font.
|
|
25
25
|
*/
|
|
26
|
-
export function headerFooterTemplate(spec, values, horizontalMargin) {
|
|
26
|
+
export function headerFooterTemplate(spec, values, horizontalMargin, font = "manrope") {
|
|
27
27
|
if (!spec)
|
|
28
28
|
return "<span></span>";
|
|
29
29
|
const [left, center, right] = columns(spec).map((c) => renderText(c, values));
|
|
30
|
-
return `<style>${
|
|
30
|
+
return `<style>${regularFontFace(font)}</style>
|
|
31
31
|
<div style="box-sizing: border-box; width: 100%; display: flex; padding: 0 ${horizontalMargin.right} 0 ${horizontalMargin.left};
|
|
32
|
-
font-family:
|
|
32
|
+
font-family: ${fontStack(font).replaceAll('"', "'")}; font-size: 8pt; color: #59636e;">
|
|
33
33
|
<span style="flex: 1; text-align: left;">${left}</span>
|
|
34
34
|
<span style="flex: 1; text-align: center;">${center}</span>
|
|
35
35
|
<span style="flex: 1; text-align: right;">${right}</span>
|
package/dist/render/mermaid.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Diagnostic } from "../diagnostics.js";
|
|
2
2
|
import type { MermaidBlock } from "../markdown/env.js";
|
|
3
|
-
import type { MermaidFlip, MermaidTheme } from "../options.js";
|
|
3
|
+
import type { MermaidFlip, MermaidTheme, TextFont } from "../options.js";
|
|
4
4
|
import type { BrowserSession } from "./browser.js";
|
|
5
5
|
export { MERMAID_THEMES, type MermaidTheme } from "../options.js";
|
|
6
6
|
/** How autofit laid out a flowchart (see render/mermaid-fit.ts). */
|
|
@@ -37,6 +37,8 @@ export interface MermaidRenderOptions {
|
|
|
37
37
|
/** Search label wrapping widths (and, with `flip`, the other axis) for each flowchart. */
|
|
38
38
|
autofit?: boolean;
|
|
39
39
|
flip?: MermaidFlip;
|
|
40
|
+
/** Text font of the document, used for diagram labels too. */
|
|
41
|
+
font?: TextFont;
|
|
40
42
|
}
|
|
41
43
|
/**
|
|
42
44
|
* Room kept for a heading printed together with the diagram below it (see keep-with-heading in
|
package/dist/render/mermaid.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { MERMAID_JS, readTextAsset } from "../assets.js";
|
|
2
2
|
import { mermaidPlaceholder } from "../markdown/mermaid.js";
|
|
3
|
-
import { fontFaceCss } from "./fonts.js";
|
|
3
|
+
import { FONT_FAMILIES, fontFaceCss, fontStack } from "./fonts.js";
|
|
4
4
|
import { escapeHtml } from "../html.js";
|
|
5
5
|
import { authorConfigured, candidate, chooseCandidate, flipWorthTrying, flippedDirection, flowchartHeader, withDirection, WRAPPING_WIDTHS, wrapsExhausted, } from "./mermaid-fit.js";
|
|
6
6
|
export { MERMAID_THEMES } from "../options.js";
|
|
@@ -14,15 +14,16 @@ const DEFAULT_FONT_PX = 14;
|
|
|
14
14
|
*/
|
|
15
15
|
export const HEADING_ALLOWANCE_EM = 5;
|
|
16
16
|
let mermaidJs;
|
|
17
|
-
function mermaidConfig(theme, fontPx, wrap) {
|
|
17
|
+
function mermaidConfig(theme, font, fontPx, wrap) {
|
|
18
|
+
const fontFamily = fontStack(font);
|
|
18
19
|
return {
|
|
19
20
|
startOnLoad: false,
|
|
20
21
|
securityLevel: "strict",
|
|
21
22
|
theme,
|
|
22
|
-
fontFamily
|
|
23
|
+
fontFamily,
|
|
23
24
|
// Sequence diagrams take their text sizes from here, the others from themeVariables.
|
|
24
25
|
fontSize: fontPx,
|
|
25
|
-
themeVariables: { fontFamily
|
|
26
|
+
themeVariables: { fontFamily, fontSize: `${fontPx}px` },
|
|
26
27
|
htmlLabels: true,
|
|
27
28
|
// Actors only at the top of sequence diagrams: repeating them below costs height.
|
|
28
29
|
sequence: { mirrorActors: false },
|
|
@@ -35,20 +36,21 @@ function mermaidConfig(theme, fontPx, wrap) {
|
|
|
35
36
|
* flowcharts are rendered in several layouts and the one whose text prints largest is kept.
|
|
36
37
|
*/
|
|
37
38
|
export async function renderMermaid(session, blocks, options = {}) {
|
|
38
|
-
const { theme = "neutral", widthPx = DEFAULT_WIDTH_PX, fontPx = DEFAULT_FONT_PX, pageHeightPx = Infinity, autofit = true, flip = "auto", } = options;
|
|
39
|
+
const { theme = "neutral", widthPx = DEFAULT_WIDTH_PX, fontPx = DEFAULT_FONT_PX, pageHeightPx = Infinity, autofit = true, flip = "auto", font = "manrope", } = options;
|
|
40
|
+
const family = FONT_FAMILIES[font];
|
|
39
41
|
mermaidJs ??= readTextAsset(MERMAID_JS);
|
|
40
42
|
const page = await session.newPage();
|
|
41
43
|
try {
|
|
42
|
-
await page.setContent(`<!doctype html><html><head><meta charset="utf-8"><style>${fontFaceCss()}
|
|
43
|
-
body { margin: 0; width: ${widthPx}px; font-family:
|
|
44
|
+
await page.setContent(`<!doctype html><html><head><meta charset="utf-8"><style>${fontFaceCss(font)}
|
|
45
|
+
body { margin: 0; width: ${widthPx}px; font-family: ${fontStack(font)}; }</style>
|
|
44
46
|
</head><body></body></html>`);
|
|
45
47
|
await page.evaluate(mermaidJs);
|
|
46
|
-
await page.evaluate(async () => {
|
|
48
|
+
await page.evaluate(async (family) => {
|
|
47
49
|
// Runs in the browser. The project is compiled without DOM types, hence PageGlobals.
|
|
48
50
|
const { document } = globalThis;
|
|
49
51
|
// Load the font weights Mermaid uses before it measures any text.
|
|
50
|
-
await Promise.all(["400", "600", "700"].map((w) => document.fonts.load(`${w} 16px
|
|
51
|
-
});
|
|
52
|
+
await Promise.all(["400", "600", "700"].map((w) => document.fonts.load(`${w} 16px "${family}"`)));
|
|
53
|
+
}, family);
|
|
52
54
|
const render = (source, id, wrap) => page.evaluate(async (source, id, config) => {
|
|
53
55
|
const { mermaid } = globalThis;
|
|
54
56
|
// Each render sets the whole configuration, so one layout never leaks into the next.
|
|
@@ -59,7 +61,7 @@ body { margin: 0; width: ${widthPx}px; font-family: "Inter", sans-serif; }</styl
|
|
|
59
61
|
catch (error) {
|
|
60
62
|
return { error: String(error?.message ?? error) };
|
|
61
63
|
}
|
|
62
|
-
}, source, id, mermaidConfig(theme, fontPx, wrap));
|
|
64
|
+
}, source, id, mermaidConfig(theme, font, fontPx, wrap));
|
|
63
65
|
const results = [];
|
|
64
66
|
for (const [i, block] of blocks.entries()) {
|
|
65
67
|
const id = `mermaid-${i + 1}`;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { escapeHtml } from "../html.js";
|
|
2
|
+
import type { TextFont } from "../options.js";
|
|
2
3
|
export { escapeHtml };
|
|
3
4
|
/** Stylesheets of a built-in theme ("light" or "dark"). */
|
|
4
5
|
export declare function builtinThemeCss(theme: "light" | "dark"): string[];
|
|
@@ -17,6 +18,8 @@ export interface DocumentParts {
|
|
|
17
18
|
showTitle?: boolean;
|
|
18
19
|
/** Theme stylesheet contents. Defaults to the built-in light theme. */
|
|
19
20
|
themeCss?: string[];
|
|
21
|
+
/** Text font to embed. Defaults to Manrope. */
|
|
22
|
+
font?: TextFont;
|
|
20
23
|
/** Layout stylesheets (density, font size, page height), applied after the theme. */
|
|
21
24
|
layoutCss?: string[];
|
|
22
25
|
/** Extra stylesheet contents, applied after the theme and layout. */
|
package/dist/render/template.js
CHANGED
|
@@ -38,7 +38,7 @@ export function buildHtmlDocument(parts) {
|
|
|
38
38
|
const { title, body, math = false, lang, author, layoutCss = [], extraCss = [] } = parts;
|
|
39
39
|
const themeCss = parts.themeCss ?? builtinThemeCss("light");
|
|
40
40
|
const styles = [
|
|
41
|
-
fontFaceCss(),
|
|
41
|
+
fontFaceCss(parts.font),
|
|
42
42
|
...(math ? [katexCss()] : []),
|
|
43
43
|
...themeCss,
|
|
44
44
|
...layoutCss,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@imontene/mdrender",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "Command-line app for Windows and Linux that renders Markdown — with LaTeX equations and Mermaid diagrams — to PDF and HTML
|
|
3
|
+
"version": "2.3.0",
|
|
4
|
+
"description": "Command-line app for Windows and Linux that renders Markdown — with LaTeX equations and Mermaid diagrams — to PDF and HTML set in Manrope or Inter",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"markdown",
|
|
7
7
|
"pdf",
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
"latex",
|
|
12
12
|
"cli",
|
|
13
13
|
"inter",
|
|
14
|
+
"manrope",
|
|
14
15
|
"converter"
|
|
15
16
|
],
|
|
16
17
|
"homepage": "https://github.com/imontene/MD_reder#readme",
|
|
@@ -65,6 +66,7 @@
|
|
|
65
66
|
},
|
|
66
67
|
"dependencies": {
|
|
67
68
|
"@fontsource/jetbrains-mono": "^5.3.0",
|
|
69
|
+
"@fontsource/manrope": "^5.3.0",
|
|
68
70
|
"@puppeteer/browsers": "^3.2.3",
|
|
69
71
|
"chokidar": "^5.0.0",
|
|
70
72
|
"commander": "^15.0.0",
|