printdown 1.0.0 → 1.1.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/README.md +73 -0
- package/dist/{chunk-HX2CR2SJ.js → chunk-FFA5GXRD.js} +247 -2
- package/dist/chunk-FFA5GXRD.js.map +1 -0
- package/dist/{chunk-INBZBWSZ.js → chunk-YBTWG5UP.js} +100 -1
- package/dist/chunk-YBTWG5UP.js.map +1 -0
- package/dist/cli.js +3 -3
- package/dist/cli.js.map +1 -1
- package/dist/index.js +2 -2
- package/dist/themes/default/index.d.ts +5 -2
- package/dist/themes/default/index.js +3 -1
- package/dist/themes/default/style.css +95 -0
- package/package.json +1 -1
- package/dist/chunk-HX2CR2SJ.js.map +0 -1
- package/dist/chunk-INBZBWSZ.js.map +0 -1
package/README.md
CHANGED
|
@@ -12,6 +12,8 @@ Printdown turns Markdown into clean, publication-grade PDFs, PNGs, JPEGs, and We
|
|
|
12
12
|
- **Marker Pen Highlights**: Physical marker pen effect with `==text==` (blue) and `=={amber}text==` (amber).
|
|
13
13
|
- **Badges & Labels**: Badges with `<Badge>` / `[badge:...]` supporting `solid`, `soft`, and `outline` variants.
|
|
14
14
|
- **Keyboard Keys**: Styled keyboard keys with `<Kbd icon="command" />` / `[kbd:...]` and embedded icon masks.
|
|
15
|
+
- **Center Alignment**: Center text, headings, or blocks with `::: center`, `-> text <-`, `<center>`, or `[center:...]`.
|
|
16
|
+
- **Heading Border Control**: Remove underline/border from individual headings using `{.no-border}` or `{-}`.
|
|
15
17
|
- **Code Highlighting**: Syntax highlighting powered by **Shiki** with dark theme support.
|
|
16
18
|
- **Custom CSS & Variables**: Full control over styling by overriding OKLCH CSS variables or injecting custom CSS.
|
|
17
19
|
- **Japanese & Multilingual Typography**: Balanced typography for Japanese and English mixed documents with robust fallbacks.
|
|
@@ -163,6 +165,77 @@ Use <Kbd icon="arrow-up" /> or <Kbd icon="arrow-down" /> to navigate.
|
|
|
163
165
|
[kbd:Enter]
|
|
164
166
|
```
|
|
165
167
|
|
|
168
|
+
## Center Alignment
|
|
169
|
+
|
|
170
|
+
Printdown provides flexible block and inline center alignment options.
|
|
171
|
+
|
|
172
|
+
### Container Block Syntax (`::: center`)
|
|
173
|
+
|
|
174
|
+
```markdown
|
|
175
|
+
::: center
|
|
176
|
+
|
|
177
|
+
# Centered Heading
|
|
178
|
+
|
|
179
|
+
This entire block of paragraphs, lists, and images is centered.
|
|
180
|
+
:::
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Arrow Block Syntax (`-> ... <-`)
|
|
184
|
+
|
|
185
|
+
```markdown
|
|
186
|
+
-> Centered Single Line <-
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
```markdown
|
|
190
|
+
->
|
|
191
|
+
|
|
192
|
+
# Centered Block
|
|
193
|
+
|
|
194
|
+
Multiple lines of text centered easily.
|
|
195
|
+
<-
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### Component / HTML Tag Syntax
|
|
199
|
+
|
|
200
|
+
```markdown
|
|
201
|
+
<center>
|
|
202
|
+
# Centered Title
|
|
203
|
+
Centered paragraph text.
|
|
204
|
+
</center>
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### Markdown Inline Syntax
|
|
208
|
+
|
|
209
|
+
```markdown
|
|
210
|
+
Normal text with [center:centered inline text] in the sentence.
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
## Heading Underline / Border Control
|
|
214
|
+
|
|
215
|
+
In Printdown's default theme, `h1` has a bottom border/underline. You can remove the border from individual headings (H1, H2, H3, etc.) using suffix attributes or tags:
|
|
216
|
+
|
|
217
|
+
### Suffix Syntax
|
|
218
|
+
|
|
219
|
+
```markdown
|
|
220
|
+
# Title Without Underline {.no-border}
|
|
221
|
+
|
|
222
|
+
# Another Heading {.no-underline}
|
|
223
|
+
|
|
224
|
+
# Clean Heading {-}
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### Supported Suffix Variations
|
|
228
|
+
|
|
229
|
+
- `{.no-border}` / `{.no-underline}` / `{.plain}`
|
|
230
|
+
- `{no-border}` / `{no-underline}` / `{-}`
|
|
231
|
+
- `[no-border]` / `[no-underline]`
|
|
232
|
+
|
|
233
|
+
### HTML Heading Syntax
|
|
234
|
+
|
|
235
|
+
```html
|
|
236
|
+
<h1 no-border>Title Without Underline</h1>
|
|
237
|
+
```
|
|
238
|
+
|
|
166
239
|
## CSS Customization
|
|
167
240
|
|
|
168
241
|
Printdown loads styles in a predictable cascade:
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
defaultThemeCss,
|
|
3
3
|
getFontDir
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-YBTWG5UP.js";
|
|
5
5
|
|
|
6
6
|
// src/index.ts
|
|
7
7
|
import fs3 from "fs";
|
|
@@ -424,6 +424,249 @@ function kbdPlugin(md) {
|
|
|
424
424
|
});
|
|
425
425
|
}
|
|
426
426
|
|
|
427
|
+
// src/markdown/plugins/center.ts
|
|
428
|
+
var CENTER_INLINE_BRACKET_REGEX = /^\[center:([\s\S]*?)\]/i;
|
|
429
|
+
var CENTER_INLINE_ARROW_REGEX = /^->\s*([\s\S]*?)\s*<-/;
|
|
430
|
+
var CENTER_OPEN_TAG_REGEX = /^<center(\s+[^>]*)?>$/i;
|
|
431
|
+
var CENTER_CLOSE_TAG_REGEX = /^<\/center>$/i;
|
|
432
|
+
function centerPlugin(md) {
|
|
433
|
+
function inlineCenterRule(state, silent) {
|
|
434
|
+
const start = state.pos;
|
|
435
|
+
const max = state.posMax;
|
|
436
|
+
const src = state.src;
|
|
437
|
+
if (src.charCodeAt(start) === 91) {
|
|
438
|
+
const match = src.slice(start).match(CENTER_INLINE_BRACKET_REGEX);
|
|
439
|
+
if (match) {
|
|
440
|
+
const matchedLength = match[0].length;
|
|
441
|
+
if (start + matchedLength > max) return false;
|
|
442
|
+
if (silent) {
|
|
443
|
+
state.pos += matchedLength;
|
|
444
|
+
return true;
|
|
445
|
+
}
|
|
446
|
+
const centerText = match[1];
|
|
447
|
+
const tokenOpen = state.push("center_open", "span", 1);
|
|
448
|
+
tokenOpen.attrs = [["class", "printdown-center"]];
|
|
449
|
+
const nestedTokens = [];
|
|
450
|
+
state.md.inline.parse(centerText, state.md, state.env, nestedTokens);
|
|
451
|
+
for (const nested of nestedTokens) {
|
|
452
|
+
state.tokens.push(nested);
|
|
453
|
+
}
|
|
454
|
+
state.push("center_close", "span", -1);
|
|
455
|
+
state.pos += matchedLength;
|
|
456
|
+
return true;
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
if (src.charCodeAt(start) === 45 && src.charCodeAt(start + 1) === 62) {
|
|
460
|
+
if (start > 0 && src.charCodeAt(start - 1) === 45) {
|
|
461
|
+
return false;
|
|
462
|
+
}
|
|
463
|
+
const rest = src.slice(start);
|
|
464
|
+
const match = rest.match(CENTER_INLINE_ARROW_REGEX);
|
|
465
|
+
if (match && match[1].length > 0) {
|
|
466
|
+
const matchedLength = match[0].length;
|
|
467
|
+
if (start + matchedLength > max) return false;
|
|
468
|
+
if (silent) {
|
|
469
|
+
state.pos += matchedLength;
|
|
470
|
+
return true;
|
|
471
|
+
}
|
|
472
|
+
const centerText = match[1];
|
|
473
|
+
const tokenOpen = state.push("center_open", "span", 1);
|
|
474
|
+
tokenOpen.attrs = [["class", "printdown-center"]];
|
|
475
|
+
const nestedTokens = [];
|
|
476
|
+
state.md.inline.parse(centerText, state.md, state.env, nestedTokens);
|
|
477
|
+
for (const nested of nestedTokens) {
|
|
478
|
+
state.tokens.push(nested);
|
|
479
|
+
}
|
|
480
|
+
state.push("center_close", "span", -1);
|
|
481
|
+
state.pos += matchedLength;
|
|
482
|
+
return true;
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
return false;
|
|
486
|
+
}
|
|
487
|
+
md.inline.ruler.before("link", "center_inline", inlineCenterRule);
|
|
488
|
+
function blockCenterRule(state, startLine, endLine, silent) {
|
|
489
|
+
const startPos = state.bMarks[startLine] + state.tShift[startLine];
|
|
490
|
+
const maxPos = state.eMarks[startLine];
|
|
491
|
+
const lineText = state.src.slice(startPos, maxPos).trim();
|
|
492
|
+
if (/^:::\s*center\s*$/i.test(lineText)) {
|
|
493
|
+
if (silent) return true;
|
|
494
|
+
let nextLine = startLine + 1;
|
|
495
|
+
let foundEnd = false;
|
|
496
|
+
while (nextLine < endLine) {
|
|
497
|
+
const pos = state.bMarks[nextLine] + state.tShift[nextLine];
|
|
498
|
+
const max = state.eMarks[nextLine];
|
|
499
|
+
const curLine = state.src.slice(pos, max).trim();
|
|
500
|
+
if (/^:::\s*$/.test(curLine) || /^:::\s*center\s*$/i.test(curLine)) {
|
|
501
|
+
foundEnd = true;
|
|
502
|
+
break;
|
|
503
|
+
}
|
|
504
|
+
nextLine++;
|
|
505
|
+
}
|
|
506
|
+
const tokenOpen = state.push("center_container_open", "div", 1);
|
|
507
|
+
tokenOpen.attrs = [["class", "printdown-center"]];
|
|
508
|
+
tokenOpen.block = true;
|
|
509
|
+
const oldParentType = state.parentType;
|
|
510
|
+
state.parentType = "root";
|
|
511
|
+
state.md.block.tokenize(state, startLine + 1, nextLine);
|
|
512
|
+
state.parentType = oldParentType;
|
|
513
|
+
const tokenClose = state.push("center_container_close", "div", -1);
|
|
514
|
+
tokenClose.block = true;
|
|
515
|
+
state.line = foundEnd ? nextLine + 1 : nextLine;
|
|
516
|
+
return true;
|
|
517
|
+
}
|
|
518
|
+
if (lineText.startsWith("->")) {
|
|
519
|
+
if (lineText.endsWith("<-") && lineText.length >= 4) {
|
|
520
|
+
if (silent) return true;
|
|
521
|
+
const content = lineText.slice(2, -2).trim();
|
|
522
|
+
const tokenOpen = state.push("center_container_open", "div", 1);
|
|
523
|
+
tokenOpen.attrs = [["class", "printdown-center"]];
|
|
524
|
+
tokenOpen.block = true;
|
|
525
|
+
const pOpen = state.push("paragraph_open", "p", 1);
|
|
526
|
+
pOpen.attrs = [["class", "printdown-center"]];
|
|
527
|
+
const inlineToken = state.push("inline", "", 0);
|
|
528
|
+
inlineToken.content = content;
|
|
529
|
+
inlineToken.children = [];
|
|
530
|
+
state.push("paragraph_close", "p", -1);
|
|
531
|
+
const tokenClose = state.push("center_container_close", "div", -1);
|
|
532
|
+
tokenClose.block = true;
|
|
533
|
+
state.line = startLine + 1;
|
|
534
|
+
return true;
|
|
535
|
+
}
|
|
536
|
+
if (lineText === "->") {
|
|
537
|
+
if (silent) return true;
|
|
538
|
+
let nextLine = startLine + 1;
|
|
539
|
+
let foundEnd = false;
|
|
540
|
+
while (nextLine < endLine) {
|
|
541
|
+
const pos = state.bMarks[nextLine] + state.tShift[nextLine];
|
|
542
|
+
const max = state.eMarks[nextLine];
|
|
543
|
+
const curLine = state.src.slice(pos, max).trim();
|
|
544
|
+
if (curLine === "<-") {
|
|
545
|
+
foundEnd = true;
|
|
546
|
+
break;
|
|
547
|
+
}
|
|
548
|
+
nextLine++;
|
|
549
|
+
}
|
|
550
|
+
const tokenOpen = state.push("center_container_open", "div", 1);
|
|
551
|
+
tokenOpen.attrs = [["class", "printdown-center"]];
|
|
552
|
+
tokenOpen.block = true;
|
|
553
|
+
const oldParentType = state.parentType;
|
|
554
|
+
state.parentType = "root";
|
|
555
|
+
state.md.block.tokenize(state, startLine + 1, nextLine);
|
|
556
|
+
state.parentType = oldParentType;
|
|
557
|
+
const tokenClose = state.push("center_container_close", "div", -1);
|
|
558
|
+
tokenClose.block = true;
|
|
559
|
+
state.line = foundEnd ? nextLine + 1 : nextLine;
|
|
560
|
+
return true;
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
return false;
|
|
564
|
+
}
|
|
565
|
+
md.block.ruler.before("fence", "center_block", blockCenterRule);
|
|
566
|
+
md.core.ruler.after("inline", "center_tags", (state) => {
|
|
567
|
+
const tokens = state.tokens;
|
|
568
|
+
function processToken(token) {
|
|
569
|
+
if (token.type === "html_inline") {
|
|
570
|
+
const trimmed = token.content.trim();
|
|
571
|
+
if (CENTER_OPEN_TAG_REGEX.test(trimmed)) {
|
|
572
|
+
token.content = '<span class="printdown-center">';
|
|
573
|
+
} else if (CENTER_CLOSE_TAG_REGEX.test(trimmed)) {
|
|
574
|
+
token.content = "</span>";
|
|
575
|
+
}
|
|
576
|
+
} else if (token.type === "html_block") {
|
|
577
|
+
token.content = token.content.replace(
|
|
578
|
+
/<center(\s+[^>]*)?>([\s\S]*?)<\/center>/gi,
|
|
579
|
+
(_, _attrs, body) => {
|
|
580
|
+
const rendered = md.render(body.trim()).trim();
|
|
581
|
+
return `<div class="printdown-center">${rendered}</div>`;
|
|
582
|
+
}
|
|
583
|
+
);
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
587
|
+
processToken(tokens[i]);
|
|
588
|
+
if (tokens[i].children) {
|
|
589
|
+
for (let j = 0; j < tokens[i].children.length; j++) {
|
|
590
|
+
processToken(tokens[i].children[j]);
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
// src/markdown/plugins/heading.ts
|
|
598
|
+
var NO_BORDER_ATTR_REGEX = /\s*(?:\{(?:\.|\s)*(?:no-border|no-underline|no-bottom-border|plain|-)\s*\}|\[(?:no-border|no-underline)\]|<!--\s*(?:no-border|no-underline)\s*-->)\s*$/i;
|
|
599
|
+
function addClass2(token, className) {
|
|
600
|
+
const classIndex = token.attrIndex("class");
|
|
601
|
+
if (classIndex < 0) {
|
|
602
|
+
token.attrPush(["class", className]);
|
|
603
|
+
} else if (token.attrs) {
|
|
604
|
+
const existing = token.attrs[classIndex][1];
|
|
605
|
+
if (typeof existing === "string") {
|
|
606
|
+
const classes = existing.split(" ");
|
|
607
|
+
const newClasses = className.split(" ");
|
|
608
|
+
for (const cls of newClasses) {
|
|
609
|
+
if (!classes.includes(cls)) {
|
|
610
|
+
classes.push(cls);
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
token.attrs[classIndex][1] = classes.join(" ").trim();
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
function headingPlugin(md) {
|
|
618
|
+
md.core.ruler.after("inline", "heading_no_border", (state) => {
|
|
619
|
+
const tokens = state.tokens;
|
|
620
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
621
|
+
if (tokens[i].type === "heading_open") {
|
|
622
|
+
const inlineToken = tokens[i + 1];
|
|
623
|
+
if (inlineToken && inlineToken.type === "inline") {
|
|
624
|
+
if (NO_BORDER_ATTR_REGEX.test(inlineToken.content)) {
|
|
625
|
+
addClass2(tokens[i], "printdown-no-border printdown-no-underline");
|
|
626
|
+
inlineToken.content = inlineToken.content.replace(NO_BORDER_ATTR_REGEX, "");
|
|
627
|
+
if (inlineToken.children && inlineToken.children.length > 0) {
|
|
628
|
+
for (let j = inlineToken.children.length - 1; j >= 0; j--) {
|
|
629
|
+
const child = inlineToken.children[j];
|
|
630
|
+
if (child.type === "text") {
|
|
631
|
+
if (NO_BORDER_ATTR_REGEX.test(child.content)) {
|
|
632
|
+
child.content = child.content.replace(NO_BORDER_ATTR_REGEX, "");
|
|
633
|
+
if (child.content === "") {
|
|
634
|
+
inlineToken.children.splice(j, 1);
|
|
635
|
+
}
|
|
636
|
+
break;
|
|
637
|
+
}
|
|
638
|
+
} else if (child.type === "html_inline") {
|
|
639
|
+
if (child.content.includes("no-border") || child.content.includes("no-underline")) {
|
|
640
|
+
inlineToken.children.splice(j, 1);
|
|
641
|
+
break;
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
} else if (tokens[i].type === "html_block") {
|
|
649
|
+
tokens[i].content = tokens[i].content.replace(
|
|
650
|
+
/<(h[1-6])(\s+[^>]*)?(?:\s+(?:no-border|no-underline))\s*([^>]*)>/gi,
|
|
651
|
+
(match, tag, before, after) => {
|
|
652
|
+
const combinedAttrs = `${before || ""} ${after || ""}`.trim();
|
|
653
|
+
if (combinedAttrs.includes('class="') || combinedAttrs.includes("class='")) {
|
|
654
|
+
return `<${tag} ${combinedAttrs.replace(
|
|
655
|
+
/class=(["'])(.*?)\1/,
|
|
656
|
+
"class=$1$2 printdown-no-border printdown-no-underline$1"
|
|
657
|
+
)}>`;
|
|
658
|
+
}
|
|
659
|
+
return `<${tag} class="printdown-no-border printdown-no-underline" ${combinedAttrs}>`.replace(
|
|
660
|
+
/\s+>/,
|
|
661
|
+
">"
|
|
662
|
+
);
|
|
663
|
+
}
|
|
664
|
+
);
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
});
|
|
668
|
+
}
|
|
669
|
+
|
|
427
670
|
// src/markdown/index.ts
|
|
428
671
|
var DEFAULT_LANGS = [
|
|
429
672
|
"javascript",
|
|
@@ -498,6 +741,8 @@ async function createMarkdownRenderer(options = {}) {
|
|
|
498
741
|
md.use(taskListPlugin);
|
|
499
742
|
md.use(badgePlugin);
|
|
500
743
|
md.use(kbdPlugin);
|
|
744
|
+
md.use(centerPlugin);
|
|
745
|
+
md.use(headingPlugin);
|
|
501
746
|
const highlighter = await getHighlighter(options.shiki);
|
|
502
747
|
const theme = options.shiki?.theme || DEFAULT_THEME;
|
|
503
748
|
const defaultFence = md.renderer.rules.fence;
|
|
@@ -1003,4 +1248,4 @@ export {
|
|
|
1003
1248
|
render,
|
|
1004
1249
|
renderFile
|
|
1005
1250
|
};
|
|
1006
|
-
//# sourceMappingURL=chunk-
|
|
1251
|
+
//# sourceMappingURL=chunk-FFA5GXRD.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/markdown/index.ts","../src/markdown/plugins/highlight.ts","../src/markdown/plugins/task-list.ts","../src/markdown/plugins/badge.ts","../src/markdown/plugins/kbd.ts","../src/markdown/plugins/center.ts","../src/markdown/plugins/heading.ts","../src/renderer/index.ts","../src/renderer/html.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { createMarkdownRenderer } from \"./markdown/index.js\";\nimport { BrowserRenderer, renderHtmlToBuffer, renderHtmlToPageBuffers } from \"./renderer/index.js\";\nimport { buildHtmlDocument } from \"./renderer/html.js\";\nimport { defaultTheme, defaultThemeCss, defaultThemeCssParts } from \"./themes/default/index.js\";\nimport type { OutputFormat, RenderFileOptions, RenderOptions } from \"./types.js\";\n\nexport * from \"./types.js\";\nexport {\n BrowserRenderer,\n createMarkdownRenderer,\n defaultTheme,\n defaultThemeCss,\n defaultThemeCssParts,\n renderHtmlToPageBuffers,\n};\n\nfunction inferFormat(output?: string, explicitFormat?: OutputFormat): OutputFormat {\n if (explicitFormat) {\n return explicitFormat;\n }\n if (output) {\n const ext = path.extname(output).toLowerCase();\n if (ext === \".png\") return \"png\";\n if (ext === \".jpg\" || ext === \".jpeg\") return \"jpeg\";\n if (ext === \".webp\") return \"webp\";\n if (ext === \".pdf\") return \"pdf\";\n }\n return \"pdf\";\n}\n\nfunction resolvePageOutputFilePath(\n outputPath: string,\n pageIndex: number,\n totalPages: number,\n): string {\n const pageNumber = pageIndex + 1;\n if (outputPath.includes(\"%d\")) {\n return outputPath.replace(/%d/g, String(pageNumber));\n }\n if (totalPages === 1 && !outputPath.includes(\"-1\")) {\n // If only 1 page, we can still use -1 or keep name, but let's append -1 for consistency with multi-page requests\n const parsed = path.parse(outputPath);\n return path.join(parsed.dir, `${parsed.name}-${pageNumber}${parsed.ext}`);\n }\n const parsed = path.parse(outputPath);\n return path.join(parsed.dir, `${parsed.name}-${pageNumber}${parsed.ext}`);\n}\n\n/**\n * Compiles Markdown into an HTML document with theme and custom styles.\n */\nexport async function renderToHtml(markdown: string, options: RenderOptions = {}): Promise<string> {\n const md = await createMarkdownRenderer(options.markdown);\n const bodyHtml = md.render(markdown);\n return buildHtmlDocument(bodyHtml, options);\n}\n\n/**\n * Renders Markdown to multiple page image buffers (matching A4 / PDF page dimensions).\n * If `options.output` is provided, each page image will be saved to disk.\n */\nexport async function renderPages(\n markdown: string,\n options: RenderOptions = {},\n): Promise<Buffer[]> {\n const format = inferFormat(options.output, options.format);\n const html = await renderToHtml(markdown, options);\n const buffers = await renderHtmlToPageBuffers(html, format, options);\n\n if (options.output) {\n const resolvedPath = path.resolve(options.output);\n const outputDir = path.dirname(resolvedPath);\n if (!fs.existsSync(outputDir)) {\n fs.mkdirSync(outputDir, { recursive: true });\n }\n\n if (format === \"pdf\" && buffers.length === 1) {\n fs.writeFileSync(resolvedPath, buffers[0]);\n } else {\n buffers.forEach((buf, idx) => {\n const filePath = resolvePageOutputFilePath(resolvedPath, idx, buffers.length);\n fs.writeFileSync(filePath, buf);\n });\n }\n }\n\n return buffers;\n}\n\n/**\n * Renders a Markdown file to multiple page image buffers.\n * If `options.output` is provided, each page image will be saved to disk.\n */\nexport async function renderFilePages(\n filePath: string,\n options: RenderFileOptions = {},\n): Promise<Buffer[]> {\n const absolutePath = path.resolve(filePath);\n const encoding = options.encoding ?? \"utf-8\";\n const markdown = fs.readFileSync(absolutePath, encoding);\n\n const fileDir = path.dirname(absolutePath);\n const baseUrl = options.baseUrl ?? fileDir;\n\n return renderPages(markdown, {\n baseUrl,\n ...options,\n });\n}\n\n/**\n * Renders Markdown to PDF, PNG, JPEG, or WebP buffer (or multiple buffers if `options.pages` is true).\n * If `options.output` is provided, the result is saved to disk.\n */\nexport async function render(\n markdown: string,\n options?: RenderOptions & { pages?: false },\n): Promise<Buffer>;\nexport async function render(\n markdown: string,\n options: RenderOptions & { pages: true },\n): Promise<Buffer[]>;\nexport async function render(markdown: string, options?: RenderOptions): Promise<Buffer | Buffer[]>;\nexport async function render(\n markdown: string,\n options: RenderOptions = {},\n): Promise<Buffer | Buffer[]> {\n if (options.pages) {\n return renderPages(markdown, options);\n }\n\n const format = inferFormat(options.output, options.format);\n const html = await renderToHtml(markdown, options);\n const buffer = await renderHtmlToBuffer(html, format, options);\n\n if (options.output) {\n const outputPath = path.resolve(options.output);\n const outputDir = path.dirname(outputPath);\n if (!fs.existsSync(outputDir)) {\n fs.mkdirSync(outputDir, { recursive: true });\n }\n fs.writeFileSync(outputPath, buffer);\n }\n\n return buffer;\n}\n\n/**\n * Renders a Markdown file to PDF, PNG, JPEG, or WebP buffer (or multiple buffers if `options.pages` is true).\n * If `options.output` is provided, the result is saved to disk.\n */\nexport async function renderFile(\n filePath: string,\n options?: RenderFileOptions & { pages?: false },\n): Promise<Buffer>;\nexport async function renderFile(\n filePath: string,\n options: RenderFileOptions & { pages: true },\n): Promise<Buffer[]>;\nexport async function renderFile(\n filePath: string,\n options?: RenderFileOptions,\n): Promise<Buffer | Buffer[]>;\nexport async function renderFile(\n filePath: string,\n options: RenderFileOptions = {},\n): Promise<Buffer | Buffer[]> {\n const absolutePath = path.resolve(filePath);\n const encoding = options.encoding ?? \"utf-8\";\n const markdown = fs.readFileSync(absolutePath, encoding);\n\n const fileDir = path.dirname(absolutePath);\n const baseUrl = options.baseUrl ?? fileDir;\n\n if (options.pages) {\n return renderPages(markdown, {\n baseUrl,\n ...options,\n });\n }\n\n return render(markdown, {\n baseUrl,\n ...options,\n });\n}\n","import MarkdownIt from \"markdown-it\";\nimport {\n createHighlighter,\n type BundledLanguage,\n type BundledTheme,\n type Highlighter,\n} from \"shiki\";\nimport { highlightPlugin } from \"./plugins/highlight.js\";\nimport { taskListPlugin } from \"./plugins/task-list.js\";\nimport { badgePlugin } from \"./plugins/badge.js\";\nimport { kbdPlugin } from \"./plugins/kbd.js\";\nimport { centerPlugin } from \"./plugins/center.js\";\nimport { headingPlugin } from \"./plugins/heading.js\";\n\nexport interface MarkdownOptions {\n html?: boolean;\n linkify?: boolean;\n typographer?: boolean;\n shiki?: {\n theme?: BundledTheme | string;\n langs?: (BundledLanguage | string)[];\n };\n}\n\nconst DEFAULT_LANGS: BundledLanguage[] = [\n \"javascript\",\n \"typescript\",\n \"jsx\",\n \"tsx\",\n \"html\",\n \"css\",\n \"json\",\n \"markdown\",\n \"mdx\",\n \"bash\",\n \"shell\",\n \"yaml\",\n \"toml\",\n \"sql\",\n \"python\",\n \"rust\",\n \"go\",\n \"c\",\n \"cpp\",\n \"diff\",\n \"dockerfile\",\n \"graphql\",\n \"ini\",\n \"java\",\n \"kotlin\",\n \"ruby\",\n \"php\",\n \"swift\",\n \"xml\",\n];\n\nconst DEFAULT_THEME: BundledTheme = \"github-dark\";\n\nlet cachedHighlighter: Highlighter | null = null;\nconst cachedLangs = new Set<string>();\nconst cachedThemes = new Set<string>();\n\nexport async function getHighlighter(options?: MarkdownOptions[\"shiki\"]): Promise<Highlighter> {\n const theme = (options?.theme as BundledTheme) || DEFAULT_THEME;\n const userLangs = (options?.langs as BundledLanguage[]) || [];\n const requiredLangs = Array.from(new Set([...DEFAULT_LANGS, ...userLangs]));\n\n if (!cachedHighlighter) {\n cachedHighlighter = await createHighlighter({\n themes: [theme],\n langs: requiredLangs,\n });\n for (const lang of requiredLangs) {\n cachedLangs.add(lang);\n }\n cachedThemes.add(theme);\n return cachedHighlighter;\n }\n\n // Load missing theme if necessary\n if (!cachedThemes.has(theme)) {\n await cachedHighlighter.loadTheme(theme);\n cachedThemes.add(theme);\n }\n\n // Load missing languages if necessary\n const missingLangs = requiredLangs.filter((l) => !cachedLangs.has(l));\n if (missingLangs.length > 0) {\n await cachedHighlighter.loadLanguage(...missingLangs);\n for (const lang of missingLangs) {\n cachedLangs.add(lang);\n }\n }\n\n return cachedHighlighter;\n}\n\nexport async function createMarkdownRenderer(\n options: MarkdownOptions = {},\n): Promise<InstanceType<typeof MarkdownIt>> {\n const md = new MarkdownIt({\n html: options.html ?? true,\n linkify: options.linkify ?? true,\n typographer: options.typographer ?? true,\n });\n\n // Apply plugins\n md.use(highlightPlugin);\n md.use(taskListPlugin);\n md.use(badgePlugin);\n md.use(kbdPlugin);\n md.use(centerPlugin);\n md.use(headingPlugin);\n\n // Initialize Shiki\n const highlighter = await getHighlighter(options.shiki);\n const theme = options.shiki?.theme || DEFAULT_THEME;\n\n // Custom fence renderer\n const defaultFence = md.renderer.rules.fence;\n md.renderer.rules.fence = (tokens, idx, fenceOptions, env, self) => {\n const token = tokens[idx];\n const info = token.info ? token.info.trim() : \"\";\n const lang = info ? info.split(/\\s+/)[0] : \"text\";\n const code = token.content;\n\n try {\n const loadedLangs = highlighter.getLoadedLanguages();\n const targetLang = loadedLangs.includes(lang) ? lang : \"text\";\n\n return highlighter.codeToHtml(code, {\n lang: targetLang,\n theme,\n transformers: [\n {\n pre(node) {\n const existingClass = (node.properties.class as string) || \"\";\n node.properties.class = `printdown-code ${existingClass}`.trim();\n },\n },\n ],\n });\n } catch {\n // Fallback to default fence rule or simple pre/code\n if (defaultFence) {\n return defaultFence(tokens, idx, fenceOptions, env, self);\n }\n return `<pre class=\"printdown-code\"><code>${md.utils.escapeHtml(code)}</code></pre>\\n`;\n }\n };\n\n return md;\n}\n","import type MarkdownIt from \"markdown-it\";\nimport type { StateInline } from \"markdown-it\";\n\nexport function highlightPlugin(md: InstanceType<typeof MarkdownIt>): void {\n function tokenize(state: StateInline, silent: boolean): boolean {\n const start = state.pos;\n const marker = state.src.charCodeAt(start);\n\n // 0x3d is '='\n if (marker !== 0x3d) {\n return false;\n }\n\n const scanned = state.scanDelims(state.pos, true);\n const len = scanned.length;\n const ch = String.fromCharCode(marker);\n\n if (len < 2) {\n return false;\n }\n\n let isColor = false;\n let color = \"blue\";\n\n // Check if opener has {color}\n if (scanned.can_open) {\n const match = state.src.slice(start + len).match(/^\\{([a-zA-Z0-9_-]+)\\}/);\n if (match) {\n isColor = true;\n color = match[1];\n state.pos += match[0].length;\n }\n }\n\n if (silent) {\n state.pos += len;\n return true;\n }\n\n for (let i = 0; i < Math.floor(len / 2); i++) {\n const token = state.push(\"text\", \"\", 0);\n token.content = ch + ch;\n\n state.delimiters.push({\n marker: 0x3d,\n length: 0,\n jump: i,\n token: state.tokens.length - 1,\n level: state.level,\n end: -1,\n open: scanned.can_open,\n close: scanned.can_close,\n color: isColor ? color : \"blue\",\n } as unknown as (typeof state.delimiters)[number]);\n }\n\n state.pos += len;\n return true;\n }\n\n function postProcess(state: StateInline): void {\n const delimiters = state.delimiters;\n if (!delimiters) return;\n\n for (let i = 0; i < delimiters.length; i++) {\n const startDelim = delimiters[i] as unknown as {\n marker: number;\n end: number;\n token: number;\n color?: string;\n };\n if (startDelim.marker !== 0x3d || startDelim.end === -1) continue;\n\n const endDelim = delimiters[startDelim.end] as unknown as {\n token: number;\n };\n\n const tokenStart = state.tokens[startDelim.token];\n tokenStart.type = \"mark_open\";\n tokenStart.tag = \"mark\";\n tokenStart.nesting = 1;\n tokenStart.markup = \"==\";\n tokenStart.content = \"\";\n tokenStart.attrs = [\n [\"class\", `printdown-highlight printdown-highlight--${startDelim.color || \"blue\"}`],\n ];\n\n const tokenEnd = state.tokens[endDelim.token];\n tokenEnd.type = \"mark_close\";\n tokenEnd.tag = \"mark\";\n tokenEnd.nesting = -1;\n tokenEnd.markup = \"==\";\n tokenEnd.content = \"\";\n }\n }\n\n md.inline.ruler.before(\"emphasis\", \"highlight\", tokenize);\n md.inline.ruler2.before(\"emphasis\", \"highlight\", (state: StateInline) => {\n const delimiters = state.delimiters;\n if (!delimiters) return;\n\n for (let i = 0; i < delimiters.length; i++) {\n const startDelim = delimiters[i];\n if (startDelim.marker !== 0x3d) continue;\n\n for (let j = i + 1; j < delimiters.length; j++) {\n const endDelim = delimiters[j];\n if (endDelim.marker !== 0x3d) continue;\n\n if (startDelim.open && endDelim.close && endDelim.end === -1 && startDelim.end === -1) {\n startDelim.end = j;\n endDelim.end = i;\n break;\n }\n }\n }\n\n postProcess(state);\n });\n}\n","import type MarkdownIt from \"markdown-it\";\nimport type { StateCore, Token } from \"markdown-it\";\n\nconst TASK_LIST_REGEX = /^\\[([ xX])\\][ \\t]+/;\n\nexport function taskListPlugin(md: InstanceType<typeof MarkdownIt>): void {\n md.core.ruler.after(\"inline\", \"task_list\", (state: StateCore) => {\n const tokens = state.tokens;\n\n for (let i = 0; i < tokens.length; i++) {\n if (tokens[i].type !== \"bullet_list_open\") {\n continue;\n }\n\n // Check if this list contains any task list items\n let listEnd = i + 1;\n let depth = 1;\n let hasTaskItems = false;\n\n while (listEnd < tokens.length && depth > 0) {\n if (tokens[listEnd].type === \"bullet_list_open\") {\n depth++;\n } else if (tokens[listEnd].type === \"bullet_list_close\") {\n depth--;\n }\n listEnd++;\n }\n\n // Scan items within this list level\n for (let j = i + 1; j < listEnd; j++) {\n if (tokens[j].type === \"list_item_open\") {\n // Look for paragraph -> inline\n const inlineTokenIndex = findFirstInlineToken(tokens, j);\n if (inlineTokenIndex !== -1) {\n const inlineToken = tokens[inlineTokenIndex];\n if (isTaskListItem(inlineToken)) {\n hasTaskItems = true;\n break;\n }\n }\n }\n }\n\n if (!hasTaskItems) {\n continue;\n }\n\n // Add class to bullet_list_open\n addClass(tokens[i], \"printdown-task-list\");\n\n // Now transform each task list item\n for (let j = i + 1; j < listEnd; j++) {\n if (tokens[j].type === \"list_item_open\") {\n const inlineTokenIndex = findFirstInlineToken(tokens, j);\n if (inlineTokenIndex !== -1) {\n const inlineToken = tokens[inlineTokenIndex];\n const match = getTaskListMatch(inlineToken);\n if (match) {\n addClass(tokens[j], \"printdown-task-list-item\");\n const isChecked = match[1].toLowerCase() === \"x\";\n removeTaskListPrefix(inlineToken);\n insertCheckboxToken(inlineToken, isChecked, state.Token);\n }\n }\n }\n }\n }\n });\n}\n\nfunction findFirstInlineToken(tokens: Token[], listItemIndex: number): number {\n for (let i = listItemIndex + 1; i < tokens.length; i++) {\n if (tokens[i].type === \"list_item_close\" || tokens[i].type === \"list_item_open\") {\n return -1;\n }\n if (tokens[i].type === \"inline\") {\n return i;\n }\n }\n return -1;\n}\n\nfunction isTaskListItem(inlineToken: Token): boolean {\n if (!inlineToken.children || inlineToken.children.length === 0) {\n return false;\n }\n const firstChild = inlineToken.children[0];\n return firstChild.type === \"text\" && TASK_LIST_REGEX.test(firstChild.content);\n}\n\nfunction getTaskListMatch(inlineToken: Token): RegExpMatchArray | null {\n if (!inlineToken.children || inlineToken.children.length === 0) {\n return null;\n }\n const firstChild = inlineToken.children[0];\n if (firstChild.type === \"text\") {\n return firstChild.content.match(TASK_LIST_REGEX);\n }\n return null;\n}\n\nfunction removeTaskListPrefix(inlineToken: Token): void {\n if (!inlineToken.children || inlineToken.children.length === 0) {\n return;\n }\n const firstChild = inlineToken.children[0];\n if (firstChild.type === \"text\") {\n firstChild.content = firstChild.content.replace(TASK_LIST_REGEX, \"\");\n }\n}\n\nfunction insertCheckboxToken(\n inlineToken: Token,\n checked: boolean,\n TokenConstructor: typeof Token,\n): void {\n const checkboxToken = new TokenConstructor(\"html_inline\", \"\", 0);\n checkboxToken.content = `<input class=\"printdown-task-list-checkbox\" type=\"checkbox\" disabled${\n checked ? ' checked=\"\"' : \"\"\n } /> `;\n if (!inlineToken.children) {\n inlineToken.children = [];\n }\n inlineToken.children.unshift(checkboxToken);\n}\n\nfunction addClass(token: Token, className: string): void {\n const classIndex = token.attrIndex(\"class\");\n if (classIndex < 0) {\n token.attrPush([\"class\", className]);\n } else if (token.attrs) {\n const existing = token.attrs[classIndex][1];\n if (typeof existing === \"string\" && !existing.split(\" \").includes(className)) {\n token.attrs[classIndex][1] = `${existing} ${className}`.trim();\n }\n }\n}\n","import type MarkdownIt from \"markdown-it\";\nimport type { StateCore, StateInline, Token } from \"markdown-it\";\n\nexport type BadgeVariant = \"solid\" | \"soft\" | \"outline\";\n\nconst BADGE_MARKDOWN_REGEX = /^\\[badge(?:\\(([^)]+)\\)|\\{([^}]+)\\})?:([\\s\\S]*?)\\]/i;\nconst BADGE_OPEN_TAG_REGEX = /^<badge(\\s+[^>]*)?>$/i;\nconst BADGE_CLOSE_TAG_REGEX = /^<\\/badge>$/i;\nconst BADGE_SELF_CLOSING_REGEX = /^<badge(\\s+[^>]*)?\\/>$/i;\n\nfunction parseBadgeOptions(optionsStr?: string): { variant: BadgeVariant; color: string } {\n let variant: BadgeVariant = \"solid\";\n let color = \"blue\";\n\n if (!optionsStr) {\n return { variant, color };\n }\n\n const parts = optionsStr\n .split(/[\\s,]+/)\n .map((p) => p.trim().toLowerCase())\n .filter(Boolean);\n\n for (const part of parts) {\n if (part === \"solid\" || part === \"soft\" || part === \"outline\") {\n variant = part;\n } else if (/^[a-z0-9_-]+$/.test(part)) {\n color = part;\n }\n }\n\n return { variant, color };\n}\n\nfunction parseBadgeAttrs(attrsStr?: string): { variant: BadgeVariant; color: string } {\n let variant: BadgeVariant = \"solid\";\n let color = \"blue\";\n\n if (!attrsStr) {\n return { variant, color };\n }\n\n const variantMatch = attrsStr.match(/variant=[\"']?([a-zA-Z0-9_-]+)[\"']?/i);\n if (variantMatch) {\n const v = variantMatch[1].toLowerCase();\n if (v === \"solid\" || v === \"soft\" || v === \"outline\") {\n variant = v;\n }\n }\n\n const colorMatch = attrsStr.match(/color=[\"']?([a-zA-Z0-9_-]+)[\"']?/i);\n if (colorMatch) {\n color = colorMatch[1].toLowerCase();\n }\n\n return { variant, color };\n}\n\nexport function badgePlugin(md: InstanceType<typeof MarkdownIt>): void {\n // 1. Inline ruler for markdown syntax: [badge:Text], [badge(soft):Text], [badge(outline,amber):Text], [badge{amber}:Text]\n function inlineBadgeRule(state: StateInline, silent: boolean): boolean {\n const max = state.posMax;\n const start = state.pos;\n\n if (state.src.charCodeAt(start) !== 0x5b /* '[' */) {\n return false;\n }\n\n const match = state.src.slice(start).match(BADGE_MARKDOWN_REGEX);\n if (!match) {\n return false;\n }\n\n const matchedLength = match[0].length;\n if (start + matchedLength > max) {\n return false;\n }\n\n if (silent) {\n state.pos += matchedLength;\n return true;\n }\n\n const rawOptions = match[1] || match[2] || \"\";\n const badgeText = match[3];\n const { variant, color } = parseBadgeOptions(rawOptions);\n\n const tokenOpen = state.push(\"badge_open\", \"span\", 1);\n tokenOpen.attrs = [\n [\"class\", `printdown-badge printdown-badge--${variant} printdown-badge--${color}`],\n ];\n\n // Parse nested inline markdown if any\n const nestedTokens: Token[] = [];\n state.md.inline.parse(badgeText, state.md, state.env, nestedTokens);\n for (const nested of nestedTokens) {\n state.tokens.push(nested);\n }\n\n state.push(\"badge_close\", \"span\", -1);\n\n state.pos += matchedLength;\n return true;\n }\n\n md.inline.ruler.before(\"link\", \"badge\", inlineBadgeRule);\n\n // 2. Core ruler to process <Badge ...> and <badge ...> HTML tags\n md.core.ruler.after(\"inline\", \"badge_tags\", (state: StateCore) => {\n const tokens = state.tokens;\n\n function processToken(token: Token) {\n if (token.type === \"html_inline\") {\n const trimmed = token.content.trim();\n\n if (BADGE_SELF_CLOSING_REGEX.test(trimmed)) {\n const match = trimmed.match(BADGE_SELF_CLOSING_REGEX);\n const { variant, color } = parseBadgeAttrs(match?.[1]);\n token.content = `<span class=\"printdown-badge printdown-badge--${variant} printdown-badge--${color}\"></span>`;\n } else if (BADGE_OPEN_TAG_REGEX.test(trimmed)) {\n const match = trimmed.match(BADGE_OPEN_TAG_REGEX);\n const { variant, color } = parseBadgeAttrs(match?.[1]);\n token.content = `<span class=\"printdown-badge printdown-badge--${variant} printdown-badge--${color}\">`;\n } else if (BADGE_CLOSE_TAG_REGEX.test(trimmed)) {\n token.content = `</span>`;\n }\n } else if (token.type === \"html_block\") {\n token.content = token.content.replace(\n /<badge(\\s+[^>]*)?>([\\s\\S]*?)<\\/badge>/gi,\n (_, attrs, body) => {\n const { variant, color } = parseBadgeAttrs(attrs);\n return `<span class=\"printdown-badge printdown-badge--${variant} printdown-badge--${color}\">${body}</span>`;\n },\n );\n }\n }\n\n for (let i = 0; i < tokens.length; i++) {\n processToken(tokens[i]);\n if (tokens[i].children) {\n for (let j = 0; j < tokens[i].children!.length; j++) {\n processToken(tokens[i].children![j]);\n }\n }\n }\n });\n}\n","import type MarkdownIt from \"markdown-it\";\nimport type { StateCore, StateInline, Token } from \"markdown-it\";\n\nconst KBD_MARKDOWN_REGEX = /^\\[kbd(?:\\(([^)]+)\\))?:([\\s\\S]*?)\\]/i;\nconst KBD_ICON_ONLY_REGEX = /^\\[kbd\\(([^)]+)\\)\\]/i;\nconst KBD_OPEN_TAG_REGEX = /^<kbd(\\s+[^>]*)?>$/i;\nconst KBD_CLOSE_TAG_REGEX = /^<\\/kbd>$/i;\nconst KBD_SELF_CLOSING_REGEX = /^<kbd(\\s+[^>]*)?\\/>$/i;\n\nfunction parseKbdAttrs(attrsStr?: string): { icon?: string } {\n if (!attrsStr) return {};\n\n const iconMatch = attrsStr.match(/icon=[\"']?([a-zA-Z0-9_-]+)[\"']?/i);\n if (iconMatch) {\n return { icon: iconMatch[1].toLowerCase() };\n }\n return {};\n}\n\nexport function kbdPlugin(md: InstanceType<typeof MarkdownIt>): void {\n // 1. Inline ruler for markdown syntax: [kbd:K], [kbd(command)], [kbd(command):K], [kbd:Ctrl]\n function inlineKbdRule(state: StateInline, silent: boolean): boolean {\n const max = state.posMax;\n const start = state.pos;\n\n if (state.src.charCodeAt(start) !== 0x5b /* '[' */) {\n return false;\n }\n\n const rest = state.src.slice(start);\n\n // Try [kbd(icon):Text] or [kbd:Text]\n const matchWithText = rest.match(KBD_MARKDOWN_REGEX);\n // Try [kbd(icon)]\n const matchIconOnly = !matchWithText ? rest.match(KBD_ICON_ONLY_REGEX) : null;\n\n const match = matchWithText || matchIconOnly;\n if (!match) {\n return false;\n }\n\n const matchedLength = match[0].length;\n if (start + matchedLength > max) {\n return false;\n }\n\n if (silent) {\n state.pos += matchedLength;\n return true;\n }\n\n const icon = matchWithText ? matchWithText[1] : matchIconOnly ? matchIconOnly[1] : undefined;\n const kbdText = matchWithText ? matchWithText[2] : \"\";\n\n const tokenOpen = state.push(\"kbd_open\", \"kbd\", 1);\n tokenOpen.attrs = [[\"class\", \"printdown-kbd\"]];\n\n if (icon) {\n const iconToken = state.push(\"html_inline\", \"\", 0);\n iconToken.content = `<span class=\"printdown-kbd-icon printdown-kbd-icon--${icon.toLowerCase()}\"></span>`;\n }\n\n if (kbdText) {\n const nestedTokens: Token[] = [];\n state.md.inline.parse(kbdText, state.md, state.env, nestedTokens);\n for (const nested of nestedTokens) {\n state.tokens.push(nested);\n }\n }\n\n state.push(\"kbd_close\", \"kbd\", -1);\n\n state.pos += matchedLength;\n return true;\n }\n\n md.inline.ruler.before(\"link\", \"kbd\", inlineKbdRule);\n\n // 2. Core ruler to process <Kbd ...> and <kbd ...> HTML tags\n md.core.ruler.after(\"inline\", \"kbd_tags\", (state: StateCore) => {\n const tokens = state.tokens;\n\n function processToken(token: Token) {\n if (token.type === \"html_inline\") {\n const trimmed = token.content.trim();\n\n if (KBD_SELF_CLOSING_REGEX.test(trimmed)) {\n const match = trimmed.match(KBD_SELF_CLOSING_REGEX);\n const { icon } = parseKbdAttrs(match?.[1]);\n const iconHtml = icon\n ? `<span class=\"printdown-kbd-icon printdown-kbd-icon--${icon}\"></span>`\n : \"\";\n token.content = `<kbd class=\"printdown-kbd\">${iconHtml}</kbd>`;\n } else if (KBD_OPEN_TAG_REGEX.test(trimmed)) {\n const match = trimmed.match(KBD_OPEN_TAG_REGEX);\n const { icon } = parseKbdAttrs(match?.[1]);\n const iconHtml = icon\n ? `<span class=\"printdown-kbd-icon printdown-kbd-icon--${icon}\"></span>`\n : \"\";\n token.content = `<kbd class=\"printdown-kbd\">${iconHtml}`;\n } else if (KBD_CLOSE_TAG_REGEX.test(trimmed)) {\n token.content = `</kbd>`;\n }\n } else if (token.type === \"html_block\") {\n token.content = token.content.replace(\n /<kbd(\\s+[^>]*)?>([\\s\\S]*?)<\\/kbd>/gi,\n (_, attrs, body) => {\n const { icon } = parseKbdAttrs(attrs);\n const iconHtml = icon\n ? `<span class=\"printdown-kbd-icon printdown-kbd-icon--${icon}\"></span>`\n : \"\";\n return `<kbd class=\"printdown-kbd\">${iconHtml}${body}</kbd>`;\n },\n );\n }\n }\n\n for (let i = 0; i < tokens.length; i++) {\n processToken(tokens[i]);\n if (tokens[i].children) {\n for (let j = 0; j < tokens[i].children!.length; j++) {\n processToken(tokens[i].children![j]);\n }\n }\n }\n });\n}\n","import type MarkdownIt from \"markdown-it\";\nimport type { StateBlock, StateCore, StateInline, Token } from \"markdown-it\";\n\nconst CENTER_INLINE_BRACKET_REGEX = /^\\[center:([\\s\\S]*?)\\]/i;\nconst CENTER_INLINE_ARROW_REGEX = /^->\\s*([\\s\\S]*?)\\s*<-/;\n\nconst CENTER_OPEN_TAG_REGEX = /^<center(\\s+[^>]*)?>$/i;\nconst CENTER_CLOSE_TAG_REGEX = /^<\\/center>$/i;\n\n/**\n * Center alignment plugin for MarkdownIt.\n *\n * Supports:\n * 1. Inline bracket syntax: `[center:Centered Text]`\n * 2. Inline arrow syntax: `->Centered Text<-`\n * 3. Container block syntax: `::: center` ... `:::`\n * 4. Arrow block syntax: `->` ... `<-` or `-> Centered Line <-`\n * 5. HTML tags: `<center>...</center>` and `<Center>...</Center>`\n */\nexport function centerPlugin(md: InstanceType<typeof MarkdownIt>): void {\n // 1. Inline ruler for [center:...] and ->...<-\n function inlineCenterRule(state: StateInline, silent: boolean): boolean {\n const start = state.pos;\n const max = state.posMax;\n const src = state.src;\n\n // Check [center:...]\n if (src.charCodeAt(start) === 0x5b /* '[' */) {\n const match = src.slice(start).match(CENTER_INLINE_BRACKET_REGEX);\n if (match) {\n const matchedLength = match[0].length;\n if (start + matchedLength > max) return false;\n\n if (silent) {\n state.pos += matchedLength;\n return true;\n }\n\n const centerText = match[1];\n const tokenOpen = state.push(\"center_open\", \"span\", 1);\n tokenOpen.attrs = [[\"class\", \"printdown-center\"]];\n\n const nestedTokens: Token[] = [];\n state.md.inline.parse(centerText, state.md, state.env, nestedTokens);\n for (const nested of nestedTokens) {\n state.tokens.push(nested);\n }\n\n state.push(\"center_close\", \"span\", -1);\n state.pos += matchedLength;\n return true;\n }\n }\n\n // Check ->...<- inline\n if (src.charCodeAt(start) === 0x2d /* '-' */ && src.charCodeAt(start + 1) === 0x3e /* '>' */) {\n // Must not be preceded by another '-' (e.g. '-->')\n if (start > 0 && src.charCodeAt(start - 1) === 0x2d) {\n return false;\n }\n\n const rest = src.slice(start);\n const match = rest.match(CENTER_INLINE_ARROW_REGEX);\n if (match && match[1].length > 0) {\n const matchedLength = match[0].length;\n if (start + matchedLength > max) return false;\n\n if (silent) {\n state.pos += matchedLength;\n return true;\n }\n\n const centerText = match[1];\n const tokenOpen = state.push(\"center_open\", \"span\", 1);\n tokenOpen.attrs = [[\"class\", \"printdown-center\"]];\n\n const nestedTokens: Token[] = [];\n state.md.inline.parse(centerText, state.md, state.env, nestedTokens);\n for (const nested of nestedTokens) {\n state.tokens.push(nested);\n }\n\n state.push(\"center_close\", \"span\", -1);\n state.pos += matchedLength;\n return true;\n }\n }\n\n return false;\n }\n\n md.inline.ruler.before(\"link\", \"center_inline\", inlineCenterRule);\n\n // 2. Block ruler for ::: center and -> ... <-\n function blockCenterRule(\n state: StateBlock,\n startLine: number,\n endLine: number,\n silent: boolean,\n ): boolean {\n const startPos = state.bMarks[startLine] + state.tShift[startLine];\n const maxPos = state.eMarks[startLine];\n const lineText = state.src.slice(startPos, maxPos).trim();\n\n // Check for ::: center\n if (/^:::\\s*center\\s*$/i.test(lineText)) {\n if (silent) return true;\n\n let nextLine = startLine + 1;\n let foundEnd = false;\n\n while (nextLine < endLine) {\n const pos = state.bMarks[nextLine] + state.tShift[nextLine];\n const max = state.eMarks[nextLine];\n const curLine = state.src.slice(pos, max).trim();\n\n if (/^:::\\s*$/.test(curLine) || /^:::\\s*center\\s*$/i.test(curLine)) {\n foundEnd = true;\n break;\n }\n nextLine++;\n }\n\n const tokenOpen = state.push(\"center_container_open\", \"div\", 1);\n tokenOpen.attrs = [[\"class\", \"printdown-center\"]];\n tokenOpen.block = true;\n\n const oldParentType = state.parentType;\n state.parentType = \"root\";\n\n // Parse nested markdown blocks within container\n state.md.block.tokenize(state, startLine + 1, nextLine);\n\n state.parentType = oldParentType;\n\n const tokenClose = state.push(\"center_container_close\", \"div\", -1);\n tokenClose.block = true;\n\n state.line = foundEnd ? nextLine + 1 : nextLine;\n return true;\n }\n\n // Check for -> ... <- single line or multi-line block\n if (lineText.startsWith(\"->\")) {\n // Single line: -> Centered Text <-\n if (lineText.endsWith(\"<-\") && lineText.length >= 4) {\n if (silent) return true;\n\n const content = lineText.slice(2, -2).trim();\n const tokenOpen = state.push(\"center_container_open\", \"div\", 1);\n tokenOpen.attrs = [[\"class\", \"printdown-center\"]];\n tokenOpen.block = true;\n\n const pOpen = state.push(\"paragraph_open\", \"p\", 1);\n pOpen.attrs = [[\"class\", \"printdown-center\"]];\n const inlineToken = state.push(\"inline\", \"\", 0);\n inlineToken.content = content;\n inlineToken.children = [];\n state.push(\"paragraph_close\", \"p\", -1);\n\n const tokenClose = state.push(\"center_container_close\", \"div\", -1);\n tokenClose.block = true;\n\n state.line = startLine + 1;\n return true;\n }\n\n // Multi-line block starting with standalone '->'\n if (lineText === \"->\") {\n if (silent) return true;\n\n let nextLine = startLine + 1;\n let foundEnd = false;\n\n while (nextLine < endLine) {\n const pos = state.bMarks[nextLine] + state.tShift[nextLine];\n const max = state.eMarks[nextLine];\n const curLine = state.src.slice(pos, max).trim();\n\n if (curLine === \"<-\") {\n foundEnd = true;\n break;\n }\n nextLine++;\n }\n\n const tokenOpen = state.push(\"center_container_open\", \"div\", 1);\n tokenOpen.attrs = [[\"class\", \"printdown-center\"]];\n tokenOpen.block = true;\n\n const oldParentType = state.parentType;\n state.parentType = \"root\";\n\n state.md.block.tokenize(state, startLine + 1, nextLine);\n\n state.parentType = oldParentType;\n\n const tokenClose = state.push(\"center_container_close\", \"div\", -1);\n tokenClose.block = true;\n\n state.line = foundEnd ? nextLine + 1 : nextLine;\n return true;\n }\n }\n\n return false;\n }\n\n md.block.ruler.before(\"fence\", \"center_block\", blockCenterRule);\n\n // 3. Core ruler to process <center> and <Center> tags and parse inner markdown\n md.core.ruler.after(\"inline\", \"center_tags\", (state: StateCore) => {\n const tokens = state.tokens;\n\n function processToken(token: Token) {\n if (token.type === \"html_inline\") {\n const trimmed = token.content.trim();\n\n if (CENTER_OPEN_TAG_REGEX.test(trimmed)) {\n token.content = '<span class=\"printdown-center\">';\n } else if (CENTER_CLOSE_TAG_REGEX.test(trimmed)) {\n token.content = \"</span>\";\n }\n } else if (token.type === \"html_block\") {\n // Match <center>...</center> across multiple lines\n token.content = token.content.replace(\n /<center(\\s+[^>]*)?>([\\s\\S]*?)<\\/center>/gi,\n (_, _attrs, body) => {\n const rendered = md.render(body.trim()).trim();\n return `<div class=\"printdown-center\">${rendered}</div>`;\n },\n );\n }\n }\n\n for (let i = 0; i < tokens.length; i++) {\n processToken(tokens[i]);\n if (tokens[i].children) {\n for (let j = 0; j < tokens[i].children!.length; j++) {\n processToken(tokens[i].children![j]);\n }\n }\n }\n });\n}\n","import type MarkdownIt from \"markdown-it\";\nimport type { StateCore, Token } from \"markdown-it\";\n\nconst NO_BORDER_ATTR_REGEX =\n /\\s*(?:\\{(?:\\.|\\s)*(?:no-border|no-underline|no-bottom-border|plain|-)\\s*\\}|\\[(?:no-border|no-underline)\\]|<!--\\s*(?:no-border|no-underline)\\s*-->)\\s*$/i;\n\nfunction addClass(token: Token, className: string): void {\n const classIndex = token.attrIndex(\"class\");\n if (classIndex < 0) {\n token.attrPush([\"class\", className]);\n } else if (token.attrs) {\n const existing = token.attrs[classIndex][1];\n if (typeof existing === \"string\") {\n const classes = existing.split(\" \");\n const newClasses = className.split(\" \");\n for (const cls of newClasses) {\n if (!classes.includes(cls)) {\n classes.push(cls);\n }\n }\n token.attrs[classIndex][1] = classes.join(\" \").trim();\n }\n }\n}\n\n/**\n * Heading plugin to support individually removing bottom border/underline from headings (H1, H2, H3, etc.).\n *\n * Supported syntaxes at the end of a heading:\n * - `# Heading {.no-border}` or `# Heading {.no-underline}`\n * - `# Heading {no-border}` or `# Heading {no-underline}`\n * - `# Heading {-}` or `# Heading {.plain}`\n * - `# Heading [no-border]` or `# Heading [no-underline]`\n * - `# Heading <!-- no-border -->`\n */\nexport function headingPlugin(md: InstanceType<typeof MarkdownIt>): void {\n md.core.ruler.after(\"inline\", \"heading_no_border\", (state: StateCore) => {\n const tokens = state.tokens;\n\n for (let i = 0; i < tokens.length; i++) {\n if (tokens[i].type === \"heading_open\") {\n const inlineToken = tokens[i + 1];\n if (inlineToken && inlineToken.type === \"inline\") {\n if (NO_BORDER_ATTR_REGEX.test(inlineToken.content)) {\n // Add no-border classes to heading_open\n addClass(tokens[i], \"printdown-no-border printdown-no-underline\");\n\n // Strip from raw content\n inlineToken.content = inlineToken.content.replace(NO_BORDER_ATTR_REGEX, \"\");\n\n // Strip from children tokens\n if (inlineToken.children && inlineToken.children.length > 0) {\n for (let j = inlineToken.children.length - 1; j >= 0; j--) {\n const child = inlineToken.children[j];\n if (child.type === \"text\") {\n if (NO_BORDER_ATTR_REGEX.test(child.content)) {\n child.content = child.content.replace(NO_BORDER_ATTR_REGEX, \"\");\n if (child.content === \"\") {\n inlineToken.children.splice(j, 1);\n }\n break;\n }\n } else if (child.type === \"html_inline\") {\n if (\n child.content.includes(\"no-border\") ||\n child.content.includes(\"no-underline\")\n ) {\n inlineToken.children.splice(j, 1);\n break;\n }\n }\n }\n }\n }\n }\n } else if (tokens[i].type === \"html_block\") {\n // Handle HTML headings like <h1 no-border> or <h1 no-underline>\n tokens[i].content = tokens[i].content.replace(\n /<(h[1-6])(\\s+[^>]*)?(?:\\s+(?:no-border|no-underline))\\s*([^>]*)>/gi,\n (match, tag, before, after) => {\n const combinedAttrs = `${before || \"\"} ${after || \"\"}`.trim();\n if (combinedAttrs.includes('class=\"') || combinedAttrs.includes(\"class='\")) {\n return `<${tag} ${combinedAttrs.replace(\n /class=([\"'])(.*?)\\1/,\n \"class=$1$2 printdown-no-border printdown-no-underline$1\",\n )}>`;\n }\n return `<${tag} class=\"printdown-no-border printdown-no-underline\" ${combinedAttrs}>`.replace(\n /\\s+>/,\n \">\",\n );\n },\n );\n }\n }\n });\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { chromium, type Browser, type BrowserContext, type Page } from \"playwright\";\nimport { getFontDir } from \"../themes/default/fonts.js\";\nimport type { RenderOptions, OutputFormat } from \"../types.js\";\n\nasync function setupFontRouting(context: BrowserContext): Promise<void> {\n const interDir = getFontDir(\"@fontsource-variable/inter/index.css\");\n const notoDir = getFontDir(\"@fontsource-variable/noto-sans-jp/index.css\");\n const jbDir = getFontDir(\"@fontsource-variable/jetbrains-mono/index.css\");\n const genDir = getFontDir(\"gen-interface-jp/500.css\");\n\n await context.route(\"https://printdown.local/fonts/**\", async (route) => {\n const reqUrl = route.request().url();\n let filePath = \"\";\n\n if (interDir && reqUrl.startsWith(\"https://printdown.local/fonts/inter/\")) {\n filePath = path.join(\n interDir,\n \"files\",\n reqUrl.replace(\"https://printdown.local/fonts/inter/\", \"\"),\n );\n } else if (notoDir && reqUrl.startsWith(\"https://printdown.local/fonts/noto-sans-jp/\")) {\n filePath = path.join(\n notoDir,\n \"files\",\n reqUrl.replace(\"https://printdown.local/fonts/noto-sans-jp/\", \"\"),\n );\n } else if (jbDir && reqUrl.startsWith(\"https://printdown.local/fonts/jetbrains-mono/\")) {\n filePath = path.join(\n jbDir,\n \"files\",\n reqUrl.replace(\"https://printdown.local/fonts/jetbrains-mono/\", \"\"),\n );\n } else if (genDir && reqUrl.startsWith(\"https://printdown.local/fonts/gen-interface-jp/w/\")) {\n filePath = path.join(\n genDir,\n \"w\",\n reqUrl.replace(\"https://printdown.local/fonts/gen-interface-jp/w/\", \"\"),\n );\n }\n\n if (filePath && fs.existsSync(filePath)) {\n try {\n const body = fs.readFileSync(filePath);\n await route.fulfill({\n status: 200,\n contentType: \"font/woff2\",\n body,\n });\n return;\n } catch {\n // Fallback to abort\n }\n }\n await route.abort();\n });\n}\n\nexport class BrowserRenderer {\n private browser: Browser | null = null;\n\n async init(): Promise<void> {\n if (!this.browser) {\n this.browser = await chromium.launch({\n headless: true,\n args: [\n \"--disable-gpu\",\n \"--no-sandbox\",\n \"--disable-setuid-sandbox\",\n \"--font-render-hinting=medium\",\n ],\n });\n }\n }\n\n async close(): Promise<void> {\n if (this.browser) {\n await this.browser.close();\n this.browser = null;\n }\n }\n\n private async preparePage(\n html: string,\n width: number,\n height: number,\n deviceScaleFactor: number,\n ): Promise<{ browser: Browser; context: BrowserContext; page: Page; isLocalBrowser: boolean }> {\n let isLocalBrowser = false;\n let browser = this.browser;\n\n if (!browser) {\n browser = await chromium.launch({\n headless: true,\n args: [\n \"--disable-gpu\",\n \"--no-sandbox\",\n \"--disable-setuid-sandbox\",\n \"--font-render-hinting=medium\",\n ],\n });\n isLocalBrowser = true;\n }\n\n const context = await browser.newContext({\n viewport: { width, height },\n deviceScaleFactor,\n });\n\n // Intercept and serve font files directly from npm packages (Fontsource, Gen Interface JP)\n await setupFontRouting(context);\n\n const page = await context.newPage();\n\n // Load HTML content\n await page.setContent(html, {\n waitUntil: \"networkidle\",\n });\n\n // Wait for fonts to be loaded\n await page.evaluate(async () => {\n if (document.fonts) {\n await document.fonts.ready;\n }\n });\n\n // Wait for all images to complete loading\n await page.evaluate(async () => {\n const images = Array.from(document.images);\n const pendingImages = images.filter((img) => !img.complete);\n if (pendingImages.length > 0) {\n await Promise.all(\n pendingImages.map(\n (img) =>\n new Promise((resolve) => {\n img.onload = img.onerror = resolve;\n }),\n ),\n );\n }\n });\n\n return { browser, context, page, isLocalBrowser };\n }\n\n async renderToBuffer(\n html: string,\n format: OutputFormat,\n options: RenderOptions = {},\n ): Promise<Buffer> {\n const width = options.width ?? (format === \"pdf\" ? 820 : 1200);\n const height = options.height ?? 800;\n const deviceScaleFactor = options.scale ?? options.deviceScaleFactor ?? 2;\n\n const { browser, context, page, isLocalBrowser } = await this.preparePage(\n html,\n width,\n height,\n deviceScaleFactor,\n );\n\n try {\n if (format === \"pdf\") {\n const pdfBuffer = await page.pdf({\n printBackground: true,\n preferCSSPageSize: true,\n ...options.pdf,\n });\n return Buffer.from(pdfBuffer);\n } else if (format === \"png\") {\n const screenshotBuffer = await page.screenshot({\n type: \"png\",\n fullPage: options.image?.fullPage ?? true,\n ...options.image,\n });\n return Buffer.from(screenshotBuffer);\n } else if (format === \"jpeg\") {\n const screenshotBuffer = await page.screenshot({\n type: \"jpeg\",\n quality: options.quality ?? 90,\n fullPage: options.image?.fullPage ?? true,\n ...options.image,\n });\n return Buffer.from(screenshotBuffer);\n } else if (format === \"webp\") {\n const screenshotBuffer = await page.screenshot({\n type: \"webp\",\n quality: options.quality ?? 90,\n fullPage: options.image?.fullPage ?? true,\n ...options.image,\n });\n return Buffer.from(screenshotBuffer);\n } else {\n throw new Error(`Unsupported output format: ${format}`);\n }\n } finally {\n await page.close().catch(() => {});\n await context.close().catch(() => {});\n if (isLocalBrowser && browser) {\n await browser.close().catch(() => {});\n }\n }\n }\n\n async renderToPageBuffers(\n html: string,\n format: OutputFormat,\n options: RenderOptions = {},\n ): Promise<Buffer[]> {\n if (format === \"pdf\") {\n const singleBuffer = await this.renderToBuffer(html, format, options);\n return [singleBuffer];\n }\n\n const width = options.width ?? 820;\n const pageHeight = options.pageHeight ?? Math.round((width * 297) / 210);\n const marginTop = options.pageMarginTop ?? Math.round((20 / 297) * pageHeight);\n const marginBottom = options.pageMarginBottom ?? Math.round((20 / 297) * pageHeight);\n const contentHeightPerPage = Math.max(100, pageHeight - marginTop - marginBottom);\n const deviceScaleFactor = options.scale ?? options.deviceScaleFactor ?? 2;\n\n // In pages mode, remove vertical padding from .printdown container so the page margin provides clean top/bottom whitespace\n const pagesHtml = html.includes(\"</head>\")\n ? html.replace(\n \"</head>\",\n \"<style>.printdown { padding-top: 0 !important; padding-bottom: 0 !important; }</style></head>\",\n )\n : html +\n \"<style>.printdown { padding-top: 0 !important; padding-bottom: 0 !important; }</style>\";\n\n const { browser, context, page, isLocalBrowser } = await this.preparePage(\n pagesHtml,\n width,\n pageHeight,\n deviceScaleFactor,\n );\n\n let composePage: Page | null = null;\n\n try {\n const totalContentHeight = await page.evaluate(() =>\n Math.max(document.documentElement.scrollHeight, document.body.scrollHeight),\n );\n\n const pageCount = Math.max(1, Math.ceil(totalContentHeight / contentHeightPerPage));\n\n // Expand viewport height for the full document capture\n await page.setViewportSize({\n width,\n height: Math.max(totalContentHeight + 100, pageHeight),\n });\n\n const fullContentScreenshot = await page.screenshot({\n type: \"png\",\n fullPage: true,\n });\n\n // Create a page for compositing each page slice on an A4 canvas with top/bottom margins\n composePage = await context.newPage();\n await composePage.setContent(`\n <!DOCTYPE html>\n <html>\n <head>\n <style>\n html, body { margin: 0; padding: 0; background: #ffffff; overflow: hidden; }\n canvas { display: block; }\n </style>\n </head>\n <body>\n <canvas id=\"canvas\" width=\"${width * deviceScaleFactor}\" height=\"${pageHeight * deviceScaleFactor}\" style=\"width:${width}px; height:${pageHeight}px;\"></canvas>\n </body>\n </html>\n `);\n\n const base64Img = fullContentScreenshot.toString(\"base64\");\n const pageBuffers: Buffer[] = [];\n const imageType = format === \"jpeg\" ? \"jpeg\" : format === \"webp\" ? \"webp\" : \"png\";\n\n for (let i = 0; i < pageCount; i++) {\n const srcY = i * contentHeightPerPage;\n const srcH = Math.min(contentHeightPerPage, totalContentHeight - srcY);\n\n await composePage.evaluate(\n async ({ base64, width, marginTop, srcY, srcH, scale }) => {\n const canvas = document.getElementById(\"canvas\") as HTMLCanvasElement;\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) return;\n\n ctx.fillStyle = \"#ffffff\";\n ctx.fillRect(0, 0, canvas.width, canvas.height);\n\n const img = new Image();\n await new Promise((resolve) => {\n img.onload = resolve;\n img.src = \"data:image/png;base64,\" + base64;\n });\n\n ctx.drawImage(\n img,\n 0,\n srcY * scale,\n width * scale,\n srcH * scale,\n 0,\n marginTop * scale,\n width * scale,\n srcH * scale,\n );\n },\n {\n base64: base64Img,\n width,\n marginTop,\n srcY,\n srcH,\n scale: deviceScaleFactor,\n },\n );\n\n const screenshotBuffer = await composePage.screenshot({\n type: imageType,\n quality: imageType !== \"png\" ? (options.quality ?? 90) : undefined,\n ...options.image,\n });\n\n pageBuffers.push(Buffer.from(screenshotBuffer));\n }\n\n return pageBuffers;\n } finally {\n if (composePage) {\n await composePage.close().catch(() => {});\n }\n await page.close().catch(() => {});\n await context.close().catch(() => {});\n if (isLocalBrowser && browser) {\n await browser.close().catch(() => {});\n }\n }\n }\n}\n\nexport async function renderHtmlToBuffer(\n html: string,\n format: OutputFormat,\n options: RenderOptions = {},\n): Promise<Buffer> {\n const renderer = new BrowserRenderer();\n return renderer.renderToBuffer(html, format, options);\n}\n\nexport async function renderHtmlToPageBuffers(\n html: string,\n format: OutputFormat,\n options: RenderOptions = {},\n): Promise<Buffer[]> {\n const renderer = new BrowserRenderer();\n return renderer.renderToPageBuffers(html, format, options);\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { defaultThemeCss, type Theme } from \"../themes/default/index.js\";\nimport type { RenderOptions } from \"../types.js\";\n\nfunction resolveCss(cssInput: string | string[], baseUrl?: string): string {\n const inputs = Array.isArray(cssInput) ? cssInput : [cssInput];\n const resolvedCssParts: string[] = [];\n\n for (const input of inputs) {\n if (!input || typeof input !== \"string\") continue;\n\n // Check if input is a file path\n let isFile = false;\n let filePath = input;\n\n if (baseUrl && !path.isAbsolute(input)) {\n const candidate = path.resolve(baseUrl, input);\n if (fs.existsSync(candidate) && fs.statSync(candidate).isFile()) {\n isFile = true;\n filePath = candidate;\n }\n }\n\n if (!isFile && (fs.existsSync(input) || input.endsWith(\".css\"))) {\n if (fs.existsSync(input) && fs.statSync(input).isFile()) {\n isFile = true;\n filePath = path.resolve(input);\n }\n }\n\n if (isFile) {\n try {\n const fileContent = fs.readFileSync(filePath, \"utf-8\");\n resolvedCssParts.push(fileContent);\n } catch (err) {\n console.warn(`[printdown] Failed to read CSS file \"${filePath}\":`, err);\n }\n } else {\n // Inline CSS string\n resolvedCssParts.push(input);\n }\n }\n\n return resolvedCssParts.join(\"\\n\\n\");\n}\n\nfunction escapeHtml(str: string): string {\n return str\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\")\n .replace(/'/g, \"'\");\n}\n\nexport function buildHtmlDocument(bodyHtml: string, options: RenderOptions = {}): string {\n const title = options.title || \"Printdown Document\";\n let themeCss = \"\";\n\n if (options.theme !== false) {\n if (typeof options.theme === \"object\" && options.theme !== null && \"css\" in options.theme) {\n themeCss = (options.theme as Theme).css;\n } else {\n // Default theme\n themeCss = defaultThemeCss;\n }\n }\n\n const userCss = options.css ? resolveCss(options.css, options.baseUrl) : \"\";\n\n const baseTag = options.baseUrl\n ? `<base href=\"${options.baseUrl.endsWith(\"/\") ? options.baseUrl : options.baseUrl + \"/\"}\">`\n : \"\";\n\n return `<!DOCTYPE html>\n<html lang=\"ja\">\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <title>${escapeHtml(title)}</title>\n ${baseTag}\n ${themeCss ? `<style id=\"printdown-theme\">${themeCss}</style>` : \"\"}\n ${userCss ? `<style id=\"printdown-user-css\">${userCss}</style>` : \"\"}\n</head>\n<body>\n <article class=\"printdown\">\n${bodyHtml}\n </article>\n</body>\n</html>`;\n}\n"],"mappings":"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","names":["fs","path","addClass","width","marginTop","srcY","srcH","fs","path","path","fs","path","parsed","fs"]}
|