wasm-html-to-image 1.0.21 → 1.0.23
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/dist/cli-options.d.ts +44 -0
- package/dist/cli-options.js +94 -1
- package/dist/cli.js +41 -3
- package/dist/core-orchestrate.d.ts +2 -0
- package/dist/core-orchestrate.js +6 -4
- package/dist/core-resources.d.ts +8 -0
- package/dist/core-resources.js +16 -3
- package/dist/core.d.ts +10 -0
- package/dist/core.js +2 -0
- package/dist/encode-args.d.ts +7 -0
- package/dist/encode-args.js +18 -1
- package/dist/html-to-image-wasm.js +3 -3
- package/dist/html-to-image.wasm +0 -0
- package/dist/katex-css.d.ts +6 -0
- package/dist/katex-css.js +7 -0
- package/dist/markdown-math-syntax.d.ts +20 -0
- package/dist/markdown-math-syntax.js +60 -0
- package/dist/markdown-math.d.ts +27 -0
- package/dist/markdown-math.js +25 -0
- package/dist/markdown-template.d.ts +113 -0
- package/dist/markdown-template.js +174 -0
- package/dist/markdown.d.ts +88 -0
- package/dist/markdown.js +302 -0
- package/dist/web-workers.js +34 -5
- package/package.json +13 -2
package/dist/markdown.js
ADDED
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
import { marked, Marked } from "marked";
|
|
2
|
+
import { mathExtension } from "./markdown-math-syntax.js";
|
|
3
|
+
import { DEFAULT_PAGE, DEFAULT_TYPE, fillRunningText, parseFrontMatter, resolveTemplate, titleHtml, } from "./markdown-template.js";
|
|
4
|
+
import hljs from "highlight.js/lib/core";
|
|
5
|
+
import bash from "highlight.js/lib/languages/bash";
|
|
6
|
+
import c from "highlight.js/lib/languages/c";
|
|
7
|
+
import cpp from "highlight.js/lib/languages/cpp";
|
|
8
|
+
import csharp from "highlight.js/lib/languages/csharp";
|
|
9
|
+
import css from "highlight.js/lib/languages/css";
|
|
10
|
+
import diff from "highlight.js/lib/languages/diff";
|
|
11
|
+
import dockerfile from "highlight.js/lib/languages/dockerfile";
|
|
12
|
+
import go from "highlight.js/lib/languages/go";
|
|
13
|
+
import ini from "highlight.js/lib/languages/ini";
|
|
14
|
+
import java from "highlight.js/lib/languages/java";
|
|
15
|
+
import javascript from "highlight.js/lib/languages/javascript";
|
|
16
|
+
import json from "highlight.js/lib/languages/json";
|
|
17
|
+
import kotlin from "highlight.js/lib/languages/kotlin";
|
|
18
|
+
import markdownLang from "highlight.js/lib/languages/markdown";
|
|
19
|
+
import php from "highlight.js/lib/languages/php";
|
|
20
|
+
import python from "highlight.js/lib/languages/python";
|
|
21
|
+
import ruby from "highlight.js/lib/languages/ruby";
|
|
22
|
+
import rust from "highlight.js/lib/languages/rust";
|
|
23
|
+
import scss from "highlight.js/lib/languages/scss";
|
|
24
|
+
import shell from "highlight.js/lib/languages/shell";
|
|
25
|
+
import sql from "highlight.js/lib/languages/sql";
|
|
26
|
+
import swift from "highlight.js/lib/languages/swift";
|
|
27
|
+
import typescript from "highlight.js/lib/languages/typescript";
|
|
28
|
+
import xml from "highlight.js/lib/languages/xml";
|
|
29
|
+
import yaml from "highlight.js/lib/languages/yaml";
|
|
30
|
+
// A private instance: registering languages must not leak into an app's own highlight.js.
|
|
31
|
+
const highlighter = hljs.newInstance();
|
|
32
|
+
for (const [name, def] of Object.entries({
|
|
33
|
+
bash, c, cpp, csharp, css, diff, dockerfile, go, ini, java, javascript, json, kotlin,
|
|
34
|
+
markdown: markdownLang, php, python, ruby, rust, scss, shell, sql, swift, typescript, xml, yaml,
|
|
35
|
+
})) {
|
|
36
|
+
highlighter.registerLanguage(name, def);
|
|
37
|
+
}
|
|
38
|
+
highlighter.registerAliases(["js", "jsx", "mjs", "cjs"], { languageName: "javascript" });
|
|
39
|
+
highlighter.registerAliases(["ts", "tsx"], { languageName: "typescript" });
|
|
40
|
+
highlighter.registerAliases(["sh", "zsh"], { languageName: "bash" });
|
|
41
|
+
highlighter.registerAliases(["html", "svg", "vue"], { languageName: "xml" });
|
|
42
|
+
highlighter.registerAliases(["yml"], { languageName: "yaml" });
|
|
43
|
+
highlighter.registerAliases(["py"], { languageName: "python" });
|
|
44
|
+
highlighter.registerAliases(["rs"], { languageName: "rust" });
|
|
45
|
+
highlighter.registerAliases(["rb"], { languageName: "ruby" });
|
|
46
|
+
highlighter.registerAliases(["md"], { languageName: "markdown" });
|
|
47
|
+
highlighter.registerAliases(["c++", "h", "hpp"], { languageName: "cpp" });
|
|
48
|
+
highlighter.registerAliases(["cs"], { languageName: "csharp" });
|
|
49
|
+
highlighter.registerAliases(["kt"], { languageName: "kotlin" });
|
|
50
|
+
highlighter.registerAliases(["toml"], { languageName: "ini" });
|
|
51
|
+
/** Options for {@link markdownToHtml}. */
|
|
52
|
+
// The template API lives beside the converter: `import { listTemplates, type MarkdownTemplate } from ".../markdown"`.
|
|
53
|
+
export { listTemplates, TEMPLATES } from "./markdown-template.js";
|
|
54
|
+
/** A URL-fragment-safe id for a heading: letters (any script), digits, `-` and `_`, spaces as `-`. */
|
|
55
|
+
function slugify(text) {
|
|
56
|
+
const slug = text
|
|
57
|
+
.trim()
|
|
58
|
+
.toLowerCase()
|
|
59
|
+
.replace(/<[^>]*>/g, "")
|
|
60
|
+
.replace(/[^\p{L}\p{N}\s_-]/gu, "")
|
|
61
|
+
.replace(/\s+/g, "-")
|
|
62
|
+
.replace(/^-+|-+$/g, "");
|
|
63
|
+
return slug || "section";
|
|
64
|
+
}
|
|
65
|
+
const escapeHtml = (text) => text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
66
|
+
const PALETTES = {
|
|
67
|
+
light: {
|
|
68
|
+
fg: "#1f2328",
|
|
69
|
+
muted: "#59636e",
|
|
70
|
+
bg: "#ffffff",
|
|
71
|
+
border: "#d1d9e0",
|
|
72
|
+
codeBg: "#f6f8fa",
|
|
73
|
+
link: "#0969da",
|
|
74
|
+
quote: "#59636e",
|
|
75
|
+
keyword: "#cf222e",
|
|
76
|
+
string: "#0a3069",
|
|
77
|
+
comment: "#6e7781",
|
|
78
|
+
number: "#0550ae",
|
|
79
|
+
title: "#8250df",
|
|
80
|
+
tag: "#116329",
|
|
81
|
+
add: "#dafbe1",
|
|
82
|
+
del: "#ffebe9",
|
|
83
|
+
},
|
|
84
|
+
dark: {
|
|
85
|
+
fg: "#e6edf3",
|
|
86
|
+
muted: "#9198a1",
|
|
87
|
+
bg: "#0d1117",
|
|
88
|
+
border: "#3d444d",
|
|
89
|
+
codeBg: "#151b23",
|
|
90
|
+
link: "#4493f8",
|
|
91
|
+
quote: "#9198a1",
|
|
92
|
+
keyword: "#ff7b72",
|
|
93
|
+
string: "#a5d6ff",
|
|
94
|
+
comment: "#8b949e",
|
|
95
|
+
number: "#79c0ff",
|
|
96
|
+
title: "#d2a8ff",
|
|
97
|
+
tag: "#7ee787",
|
|
98
|
+
add: "#12261e",
|
|
99
|
+
del: "#3c1618",
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
/** The built-in GitHub-like stylesheet. Plain selectors only: it has to render in the engine as well. */
|
|
103
|
+
export function markdownStylesheet(theme = "light", padding = 32, maxWidth, paged = false, type = DEFAULT_TYPE, accent, page) {
|
|
104
|
+
const c = { ...PALETTES[theme], ...(accent ? { link: accent } : {}) };
|
|
105
|
+
const head = type.headingFont ?? type.font;
|
|
106
|
+
const headColor = type.accentHeadings && accent ? accent : c.fg;
|
|
107
|
+
const pageRule = page
|
|
108
|
+
? `@page { ${page.size ? `size: ${page.size}; ` : ""}margin: ${page.margin}; }\n`
|
|
109
|
+
: paged
|
|
110
|
+
? `@page { margin: ${padding}px; }\n`
|
|
111
|
+
: "";
|
|
112
|
+
return `${pageRule}:root { --accent: ${accent ?? c.link}; }
|
|
113
|
+
html, body { margin: 0; padding: 0; background: ${c.bg}; }
|
|
114
|
+
body { color: ${c.fg}; font-family: ${type.font}; font-size: ${type.size}px; line-height: ${type.lineHeight};${type.justify ? " text-align: justify;" : ""} }
|
|
115
|
+
.markdown-body { padding: ${paged ? 0 : padding}px;${maxWidth ? ` max-width: ${maxWidth}px; margin: 0 auto;` : ""} }
|
|
116
|
+
.markdown-body > *:first-child { margin-top: 0; }
|
|
117
|
+
.markdown-body > *:last-child { margin-bottom: 0; }
|
|
118
|
+
.markdown-body h1, .markdown-body h2, .markdown-body h3, .markdown-body h4, .markdown-body h5, .markdown-body h6 { margin-top: 24px; margin-bottom: 16px; font-family: ${head}; font-weight: ${type.headingWeight}; line-height: 1.25; text-align: left; color: ${headColor}; }
|
|
119
|
+
.markdown-body h1 { font-size: 2em;${type.h1Rule ? ` padding-bottom: 0.3em; border-bottom: 1px solid ${c.border};` : ""}${type.chapterBreak ? " break-before: page;" : ""} }
|
|
120
|
+
.markdown-body h2 { font-size: 1.5em;${type.h2Rule ? ` padding-bottom: 0.3em; border-bottom: 1px solid ${c.border};` : ""} }
|
|
121
|
+
.markdown-body h3 { font-size: 1.25em; }
|
|
122
|
+
.markdown-body h4 { font-size: 1em; }
|
|
123
|
+
.markdown-body h5 { font-size: 0.875em; }
|
|
124
|
+
.markdown-body h6 { font-size: 0.85em; color: ${c.muted}; }
|
|
125
|
+
.markdown-body p, .markdown-body ul, .markdown-body ol, .markdown-body blockquote, .markdown-body pre, .markdown-body table { margin-top: 0; margin-bottom: 16px; }
|
|
126
|
+
.markdown-body ul, .markdown-body ol { padding-left: 2em; }
|
|
127
|
+
.markdown-body li + li { margin-top: 0.25em; }
|
|
128
|
+
.markdown-body li > ul, .markdown-body li > ol { margin-top: 0.25em; margin-bottom: 0; }
|
|
129
|
+
.markdown-body a { color: ${c.link}; text-decoration: none; }
|
|
130
|
+
.markdown-body strong { font-weight: 600; }
|
|
131
|
+
.markdown-body blockquote { padding: 0 1em; color: ${c.quote}; border-left: 4px solid ${c.border}; }
|
|
132
|
+
.markdown-body blockquote > *:last-child { margin-bottom: 0; }
|
|
133
|
+
.markdown-body code { padding: 0.2em 0.4em; font-family: ${type.mono}; font-size: 85%; background: ${c.codeBg}; border-radius: ${type.radius}px; }
|
|
134
|
+
.markdown-body pre { padding: 16px; overflow: hidden; font-size: 85%; line-height: 1.45; background: ${c.codeBg}; border-radius: ${type.radius}px; text-align: left; }
|
|
135
|
+
.markdown-body pre code { padding: 0; font-size: 100%; background: transparent; white-space: pre; }
|
|
136
|
+
.markdown-body hr { height: 0; margin: 24px 0; border: 0; border-top: 4px solid ${c.border}; }
|
|
137
|
+
.markdown-body img { max-width: 100%; }
|
|
138
|
+
.markdown-body table { border-collapse: collapse; }
|
|
139
|
+
.markdown-body th, .markdown-body td { padding: 6px 13px; border: 1px solid ${c.border}; }
|
|
140
|
+
.markdown-body th { font-weight: 600; }
|
|
141
|
+
.markdown-body tr:nth-child(2n) { background: ${c.codeBg}; }
|
|
142
|
+
.markdown-body input[type="checkbox"] { margin: 0 0.4em 0 -1.4em; }
|
|
143
|
+
.hljs-keyword, .hljs-doctag, .hljs-template-tag, .hljs-type, .hljs-meta { color: ${c.keyword}; }
|
|
144
|
+
.hljs-string, .hljs-regexp, .hljs-symbol { color: ${c.string}; }
|
|
145
|
+
.hljs-comment, .hljs-quote { color: ${c.comment}; }
|
|
146
|
+
.hljs-number, .hljs-literal, .hljs-attr, .hljs-attribute, .hljs-variable, .hljs-built_in, .hljs-selector-id, .hljs-selector-class, .hljs-params { color: ${c.number}; }
|
|
147
|
+
.hljs-title { color: ${c.title}; }
|
|
148
|
+
.hljs-name, .hljs-tag, .hljs-selector-tag, .hljs-bullet { color: ${c.tag}; }
|
|
149
|
+
.hljs-section { color: ${c.number}; font-weight: 600; }
|
|
150
|
+
.hljs-emphasis { font-style: italic; }
|
|
151
|
+
.hljs-strong { font-weight: 600; }
|
|
152
|
+
.hljs-addition { color: ${c.tag}; background: ${c.add}; }
|
|
153
|
+
.hljs-deletion { color: ${c.keyword}; background: ${c.del}; }
|
|
154
|
+
.markdown-body h1, .markdown-body h2, .markdown-body h3, .markdown-body h4, .markdown-body h5, .markdown-body h6 { break-after: avoid; }
|
|
155
|
+
.markdown-body .toc { margin: 0 0 24px; break-after: page; }
|
|
156
|
+
.markdown-body .toc-title { margin: 0 0 12px; font-size: 1.5em; font-weight: 600; line-height: 1.25; }
|
|
157
|
+
.markdown-body .toc-item { display: flex; align-items: baseline; padding: 2px 0; color: ${c.fg}; text-decoration: none; }
|
|
158
|
+
.markdown-body .toc-l1 { font-weight: 600; margin-top: 6px; }
|
|
159
|
+
.markdown-body .toc-l2 { padding-left: 1.25em; }
|
|
160
|
+
.markdown-body .toc-l3 { padding-left: 2.5em; }
|
|
161
|
+
.markdown-body .toc-l4, .markdown-body .toc-l5, .markdown-body .toc-l6 { padding-left: 3.75em; }
|
|
162
|
+
.markdown-body .toc-leader { flex: 1; min-width: 12px; margin: 0 6px; border-bottom: 1px dotted ${c.muted}; }
|
|
163
|
+
.markdown-body .toc-page { min-width: 2.5em; text-align: right; color: ${c.muted}; }
|
|
164
|
+
.markdown-body pre, .markdown-body table, .markdown-body blockquote, .markdown-body img, .markdown-body tr { break-inside: avoid; }
|
|
165
|
+
`;
|
|
166
|
+
}
|
|
167
|
+
/** The cover: a fixed structure the template's CSS dresses. Its title is not an h1, so it stays out of the outline. */
|
|
168
|
+
function coverHtml(settings) {
|
|
169
|
+
const parts = [
|
|
170
|
+
settings.logo ? `<img class="cover-logo" src="${escapeHtml(settings.logo)}" alt="">` : "",
|
|
171
|
+
`<p class="cover-title">${titleHtml(settings.title ?? "")}</p>`,
|
|
172
|
+
settings.subtitle ? `<p class="cover-subtitle">${escapeHtml(settings.subtitle)}</p>` : "",
|
|
173
|
+
settings.author || settings.date
|
|
174
|
+
? `<p class="cover-meta">${settings.author ? `<span class="cover-author">${escapeHtml(settings.author)}</span>` : ""}${settings.date ? `<span class="cover-date">${escapeHtml(settings.date)}</span>` : ""}</p>`
|
|
175
|
+
: "",
|
|
176
|
+
];
|
|
177
|
+
return `<header class="cover">${parts.join("")}</header>
|
|
178
|
+
`;
|
|
179
|
+
}
|
|
180
|
+
/** Structure of the cover and the first page; templates tune it with their own `css`. */
|
|
181
|
+
const COVER_CSS = `.cover { margin: 40mm 0 0; break-after: page; }
|
|
182
|
+
.cover-logo { display: block; max-height: 22mm; max-width: 60mm; margin-bottom: 14mm; }
|
|
183
|
+
.cover-title { margin: 0; font-size: 2.6em; font-weight: 700; line-height: 1.25; }
|
|
184
|
+
.cover-subtitle { margin: 0.6em 0 0; font-size: 1.25em; color: #6b7280; }
|
|
185
|
+
.cover-meta { margin: 3em 0 0; color: #6b7280; }
|
|
186
|
+
.cover-meta span + span { margin-left: 1.5em; }
|
|
187
|
+
`;
|
|
188
|
+
/**
|
|
189
|
+
* The table-of-contents block: a nav with one row per heading. A row is a block link (the whole row
|
|
190
|
+
* is clickable in the PDF) holding the heading, a dotted leader and the page number. The number is
|
|
191
|
+
* an element with data-toc-page, which the PDF renderer fills in once the pages are planned; it has
|
|
192
|
+
* a fixed width so a longer number does not rewrap the row and move the pages.
|
|
193
|
+
*/
|
|
194
|
+
function tocHtml(entries, options) {
|
|
195
|
+
const depth = Math.min(6, Math.max(1, options.depth ?? 3));
|
|
196
|
+
const title = options.title ?? "Contents";
|
|
197
|
+
const rows = entries
|
|
198
|
+
.filter((e) => e.level <= depth)
|
|
199
|
+
.map((e) => `<a class="toc-item toc-l${e.level}" href="#${e.id}"><span class="toc-text">${e.html}</span><span class="toc-leader"></span><span class="toc-page" data-toc-page="${e.id}">0</span></a>`)
|
|
200
|
+
.join("");
|
|
201
|
+
if (!rows)
|
|
202
|
+
return "";
|
|
203
|
+
return `<nav class="toc">${title ? `<p class="toc-title">${escapeHtml(title)}</p>` : ""}${rows}</nav>
|
|
204
|
+
`;
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Convert Markdown (GitHub flavoured, via `marked`) to a complete HTML document with a built-in
|
|
208
|
+
* GitHub-like stylesheet, ready to pass to `render({ value })`.
|
|
209
|
+
*/
|
|
210
|
+
export function markdownToHtml(markdown, options = {}) {
|
|
211
|
+
return markdownToDocument(markdown, options).html;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Convert Markdown (GitHub flavoured, via `marked`) to a page and the render options that go with it
|
|
215
|
+
* (a template's running header and footer, the PDF's title). {@link markdownToHtml} is the HTML alone.
|
|
216
|
+
*/
|
|
217
|
+
export function markdownToDocument(markdown, options = {}) {
|
|
218
|
+
let source = markdown;
|
|
219
|
+
let front = {};
|
|
220
|
+
if (options.frontMatter !== false) {
|
|
221
|
+
const m = /^?---[ \t]*\r?\n([^]*?)\r?\n---[ \t]*(?:\r?\n|$)/.exec(source);
|
|
222
|
+
if (m) {
|
|
223
|
+
source = source.slice(m[0].length);
|
|
224
|
+
front = parseFrontMatter(m[1]);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
// Template defaults < front matter < options.
|
|
228
|
+
const named = (typeof options.template === "string" ? options.template : undefined) ?? front.template;
|
|
229
|
+
const template = resolveTemplate(typeof options.template === "object" ? options.template : named);
|
|
230
|
+
const settings = {
|
|
231
|
+
...template.defaults,
|
|
232
|
+
...front,
|
|
233
|
+
...options.settings,
|
|
234
|
+
...(options.title !== undefined ? { title: options.title } : {}),
|
|
235
|
+
...(options.toc !== undefined ? { toc: options.toc !== false } : {}),
|
|
236
|
+
};
|
|
237
|
+
// `settings` reports what applied: the template that was used, whichever of the option and the front matter named it.
|
|
238
|
+
if (typeof options.template === "object")
|
|
239
|
+
delete settings.template;
|
|
240
|
+
else if (named !== undefined)
|
|
241
|
+
settings.template = named;
|
|
242
|
+
const renderer = new marked.Renderer();
|
|
243
|
+
// Every heading gets an id (a link target, and what the table of contents points at).
|
|
244
|
+
const entries = [];
|
|
245
|
+
const usedIds = new Map();
|
|
246
|
+
renderer.heading = function ({ tokens, depth, text }) {
|
|
247
|
+
const base = slugify(text);
|
|
248
|
+
const seen = usedIds.get(base) ?? 0;
|
|
249
|
+
usedIds.set(base, seen + 1);
|
|
250
|
+
const id = seen === 0 ? base : `${base}-${seen + 1}`;
|
|
251
|
+
const inner = this.parser.parseInline(tokens);
|
|
252
|
+
entries.push({ level: depth, id, html: inner });
|
|
253
|
+
return `<h${depth} id="${id}">${inner}</h${depth}>\n`;
|
|
254
|
+
};
|
|
255
|
+
if (options.highlight !== false) {
|
|
256
|
+
renderer.code = ({ text, lang }) => {
|
|
257
|
+
const name = (lang ?? "").trim().split(/\s+/)[0];
|
|
258
|
+
const body = name && highlighter.getLanguage(name)
|
|
259
|
+
? highlighter.highlight(text, { language: name, ignoreIllegals: true }).value
|
|
260
|
+
: escapeHtml(text);
|
|
261
|
+
return `<pre><code${name ? ` class="language-${escapeHtml(name)}"` : ""}>${body}\n</code></pre>\n`;
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
const parser = options.math ? new Marked(mathExtension(options.math)) : marked;
|
|
265
|
+
const text = parser.parse(source, { async: false, gfm: true, renderer });
|
|
266
|
+
const tocOption = options.toc ?? (front.toc !== undefined || template.defaults?.toc !== undefined ? settings.toc : !!options.paged);
|
|
267
|
+
const toc = tocOption === true && typeof options.toc === "object" ? options.toc : tocOption;
|
|
268
|
+
const tocBlock = toc && entries.length ? tocHtml(entries, toc === true ? {} : toc) : "";
|
|
269
|
+
const cover = settings.cover && settings.title ? coverHtml(settings) : "";
|
|
270
|
+
const body = cover + tocBlock + text;
|
|
271
|
+
const type = { ...DEFAULT_TYPE, ...template.type, ...(settings.font ? { font: settings.font } : {}) };
|
|
272
|
+
const page = { ...DEFAULT_PAGE, ...template.page };
|
|
273
|
+
const hasTemplate = options.template !== undefined || front.template !== undefined;
|
|
274
|
+
// A plain document keeps the exact page it always had; a template adds its own @page (size and margins).
|
|
275
|
+
const pageSetting = hasTemplate && options.paged !== false
|
|
276
|
+
? { size: settings.page_size ?? page.size, margin: page.margin }
|
|
277
|
+
: settings.page_size && options.paged
|
|
278
|
+
? { size: settings.page_size, margin: `${options.padding ?? 32}px` }
|
|
279
|
+
: undefined;
|
|
280
|
+
const sheet = options.stylesheet ??
|
|
281
|
+
markdownStylesheet(options.theme, options.padding, options.maxWidth, options.paged, type, settings.accent, pageSetting);
|
|
282
|
+
const titleText = settings.title;
|
|
283
|
+
const title = titleText ? `<title>${escapeHtml(titleText)}</title>` : "";
|
|
284
|
+
const lang = settings.lang ? ` lang="${escapeHtml(settings.lang)}"` : "";
|
|
285
|
+
const dir = settings.dir ? ` dir="${escapeHtml(settings.dir)}"` : "";
|
|
286
|
+
const html = `<!doctype html><html${lang}${dir}><head><meta charset="utf-8">${title}<style>${sheet}${COVER_CSS}${template.css ?? ""}${options.math?.css ?? ""}${options.css ?? ""}</style></head><body><article class="markdown-body">${body}</article></body></html>`;
|
|
287
|
+
const running = (t) => (t ? fillRunningText(t, settings) : undefined);
|
|
288
|
+
const stamp = settings.stamp ? `<span style="float:right">${escapeHtml(settings.stamp)}</span>` : "";
|
|
289
|
+
const header = hasTemplate ? running(page.header) : settings.stamp ? `<div style="font:11px sans-serif;color:#6b7280;padding-top:8mm;text-align:right">${stamp.replace(/<[^>]+>/g, "")}</div>` : undefined;
|
|
290
|
+
return {
|
|
291
|
+
html,
|
|
292
|
+
settings,
|
|
293
|
+
renderOptions: {
|
|
294
|
+
// The text area takes the page's background from the CSS; the margins need the page itself painted.
|
|
295
|
+
...(options.paged && options.theme === "dark" && options.stylesheet === undefined ? { pdfBackground: PALETTES.dark.bg } : {}),
|
|
296
|
+
...(header ? { pdfHeader: header } : {}),
|
|
297
|
+
...(hasTemplate && page.footer ? { pdfFooter: running(page.footer) } : {}),
|
|
298
|
+
...(settings.title ? { pdfTitle: settings.title.replace(/<br\s*\/?>/gi, " ") } : {}),
|
|
299
|
+
...(settings.author ? { pdfAuthor: settings.author } : {}),
|
|
300
|
+
},
|
|
301
|
+
};
|
|
302
|
+
}
|