convpdf 0.2.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.
Files changed (45) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +101 -0
  3. package/dist/bin/convpdf.d.ts +3 -0
  4. package/dist/bin/convpdf.d.ts.map +1 -0
  5. package/dist/bin/convpdf.js +297 -0
  6. package/dist/bin/convpdf.js.map +1 -0
  7. package/dist/src/html/template.d.ts +10 -0
  8. package/dist/src/html/template.d.ts.map +1 -0
  9. package/dist/src/html/template.js +57 -0
  10. package/dist/src/html/template.js.map +1 -0
  11. package/dist/src/markdown/frontmatter.d.ts +3 -0
  12. package/dist/src/markdown/frontmatter.d.ts.map +1 -0
  13. package/dist/src/markdown/frontmatter.js +48 -0
  14. package/dist/src/markdown/frontmatter.js.map +1 -0
  15. package/dist/src/markdown/marked.d.ts +4 -0
  16. package/dist/src/markdown/marked.d.ts.map +1 -0
  17. package/dist/src/markdown/marked.js +82 -0
  18. package/dist/src/markdown/marked.js.map +1 -0
  19. package/dist/src/markdown/math.d.ts +7 -0
  20. package/dist/src/markdown/math.d.ts.map +1 -0
  21. package/dist/src/markdown/math.js +42 -0
  22. package/dist/src/markdown/math.js.map +1 -0
  23. package/dist/src/markdown/toc.d.ts +3 -0
  24. package/dist/src/markdown/toc.d.ts.map +1 -0
  25. package/dist/src/markdown/toc.js +29 -0
  26. package/dist/src/markdown/toc.js.map +1 -0
  27. package/dist/src/renderer.d.ts +17 -0
  28. package/dist/src/renderer.d.ts.map +1 -0
  29. package/dist/src/renderer.js +181 -0
  30. package/dist/src/renderer.js.map +1 -0
  31. package/dist/src/styles/default.css +147 -0
  32. package/dist/src/styles/github.css +118 -0
  33. package/dist/src/types.d.ts +40 -0
  34. package/dist/src/types.d.ts.map +1 -0
  35. package/dist/src/types.js +14 -0
  36. package/dist/src/types.js.map +1 -0
  37. package/dist/src/utils/html.d.ts +3 -0
  38. package/dist/src/utils/html.d.ts.map +1 -0
  39. package/dist/src/utils/html.js +24 -0
  40. package/dist/src/utils/html.js.map +1 -0
  41. package/dist/src/utils/validation.d.ts +6 -0
  42. package/dist/src/utils/validation.d.ts.map +1 -0
  43. package/dist/src/utils/validation.js +49 -0
  44. package/dist/src/utils/validation.js.map +1 -0
  45. package/package.json +82 -0
@@ -0,0 +1,82 @@
1
+ import { Marked } from 'marked';
2
+ import { markedHighlight } from 'marked-highlight';
3
+ import { gfmHeadingId } from 'marked-gfm-heading-id';
4
+ import footnote from 'marked-footnote';
5
+ import hljs from 'highlight.js';
6
+ import { escapeHtml, sanitizeHref } from '../utils/html.js';
7
+ const stripHtml = (value) => value.replace(/<[^>]+>/g, '').trim();
8
+ const stripMarkdownLinks = (value) => value.replace(/\[([^\]]+)\]\([^\)]+\)/g, '$1');
9
+ export const createMarkedInstance = (slugger) => {
10
+ const marked = new Marked()
11
+ .use(markedHighlight({
12
+ langPrefix: 'hljs language-',
13
+ highlight: (code, languageHint) => {
14
+ const language = hljs.getLanguage(languageHint) ? languageHint : 'plaintext';
15
+ return hljs.highlight(code, { language }).value;
16
+ }
17
+ }))
18
+ .use(footnote())
19
+ .use(gfmHeadingId())
20
+ .use({
21
+ walkTokens(token) {
22
+ const current = token;
23
+ if (current.type === 'heading' && !current.id && current.text) {
24
+ current.id = slugger.slug(stripMarkdownLinks(stripHtml(current.text)));
25
+ }
26
+ },
27
+ extensions: [
28
+ {
29
+ name: 'pageBreak',
30
+ level: 'block',
31
+ start(source) {
32
+ return source.match(/<!--\s*PAGE_BREAK\s*-->/)?.index;
33
+ },
34
+ tokenizer(source) {
35
+ const match = /^<!--\s*PAGE_BREAK\s*-->/.exec(source);
36
+ if (!match)
37
+ return undefined;
38
+ return { type: 'pageBreak', raw: match[0] };
39
+ },
40
+ renderer() {
41
+ return '<div class="page-break"></div>';
42
+ }
43
+ },
44
+ {
45
+ name: 'tocPlaceholder',
46
+ level: 'block',
47
+ start(source) {
48
+ return source.match(/^\[TOC\]/im)?.index;
49
+ },
50
+ tokenizer(source) {
51
+ const match = /^\[TOC\]/i.exec(source);
52
+ if (!match)
53
+ return undefined;
54
+ return { type: 'tocPlaceholder', raw: match[0] };
55
+ },
56
+ renderer() {
57
+ return '[[TOC_PLACEHOLDER]]';
58
+ }
59
+ }
60
+ ],
61
+ renderer: {
62
+ link(token) {
63
+ const href = token.href?.trim();
64
+ const title = token.title?.trim();
65
+ const text = token.text ?? '';
66
+ if (!href)
67
+ return text;
68
+ const external = /^(?:[a-z][a-z\d+\-.]*:)?\/\//i.test(href);
69
+ const rewrittenHref = !external && href.toLowerCase().endsWith('.md') ? href.replace(/\.md$/i, '.pdf') : href;
70
+ const safeHref = sanitizeHref(rewrittenHref);
71
+ let output = `<a href="${escapeHtml(safeHref)}"`;
72
+ if (title)
73
+ output += ` title="${escapeHtml(title)}"`;
74
+ output += `>${text}</a>`;
75
+ return output;
76
+ }
77
+ }
78
+ });
79
+ marked.setOptions({ gfm: true, breaks: true });
80
+ return marked;
81
+ };
82
+ //# sourceMappingURL=marked.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"marked.js","sourceRoot":"","sources":["../../../src/markdown/marked.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ export interface MathProtectionResult {
2
+ text: string;
3
+ restore: (html: string) => string;
4
+ }
5
+ export declare const protectMath: (content: string) => MathProtectionResult;
6
+ export declare const hasMathSyntax: (content: string) => boolean;
7
+ //# sourceMappingURL=math.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"math.d.ts","sourceRoot":"","sources":["../../../src/markdown/math.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;CACnC;AAcD,eAAO,MAAM,WAAW,GAAI,SAAS,MAAM,KAAG,oBAsD7C,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,SAAS,MAAM,KAAG,OAS/C,CAAC"}
@@ -0,0 +1,42 @@
1
+ import { randomBytes } from 'crypto';
2
+ const replaceWithPlaceholders = (input, pattern, keyPrefix, store) => input.replace(pattern, (match) => {
3
+ const id = `${keyPrefix}_${randomBytes(6).toString('hex')}`;
4
+ store.set(id, match);
5
+ return id;
6
+ });
7
+ export const protectMath = (content) => {
8
+ const codeGuards = new Map();
9
+ const mathGuards = new Map();
10
+ // Fence guards first so we never interpret math inside code blocks.
11
+ let text = replaceWithPlaceholders(content, /^( {0,3})(`{3,}|~{3,})[^\n]*\n[\s\S]*?\n\1\2[ \t]*$/gm, 'CODE_FENCE', codeGuards);
12
+ // Inline code spans can still contain things that look like LaTeX.
13
+ text = replaceWithPlaceholders(text, /(`+)([^`\n]|`(?!\1))+?\1/g, 'CODE_SPAN', codeGuards);
14
+ // Display math blocks.
15
+ text = replaceWithPlaceholders(text, /^\$\$[ \t]*\n[\s\S]*?\n\$\$[ \t]*$/gm, 'MATH_BLOCK', mathGuards);
16
+ text = replaceWithPlaceholders(text, /^\\\[[ \t]*\n[\s\S]*?\n\\\][ \t]*$/gm, 'MATH_BLOCK', mathGuards);
17
+ text = replaceWithPlaceholders(text, /\$\$[^\n]+?\$\$/g, 'MATH_INLINE', mathGuards);
18
+ text = replaceWithPlaceholders(text, /\\\[[^\n]*?\\\]/g, 'MATH_INLINE', mathGuards);
19
+ // Inline math.
20
+ text = replaceWithPlaceholders(text, /\\\([^\n]*?\\\)/g, 'MATH_INLINE', mathGuards);
21
+ text = replaceWithPlaceholders(text, /(?<!\\)\$(?!\s)([^\n$]|\\\$)+?(?<!\s)(?<!\\)\$/g, 'MATH_INLINE', mathGuards);
22
+ // Restore code guards before lexing markdown.
23
+ for (const [id, code] of codeGuards) {
24
+ text = text.split(id).join(code);
25
+ }
26
+ const restore = (html) => {
27
+ let output = html;
28
+ for (const [id, original] of mathGuards) {
29
+ output = output.split(id).join(original);
30
+ }
31
+ return output;
32
+ };
33
+ return { text, restore };
34
+ };
35
+ export const hasMathSyntax = (content) => {
36
+ const sanitized = content
37
+ .replace(/^( {0,3})(`{3,}|~{3,})[^\n]*\n[\s\S]*?\n\1\2[ \t]*$/gm, '')
38
+ .replace(/`[^`\n]*`/g, '')
39
+ .replace(/\[([^\]]*)\]\([^\)]+\)/g, '$1');
40
+ return /(?<!\\)\$[^$\n]+\$|(?<!\\)\$\$[\s\S]+?\$\$|\\\([^\n]+?\\\)|\\\[[\s\S]+?\\\]/.test(sanitized);
41
+ };
42
+ //# sourceMappingURL=math.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"math.js","sourceRoot":"","sources":["../../../src/markdown/math.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AAOrC,MAAM,uBAAuB,GAAG,CAC9B,KAAa,EACb,OAAe,EACf,SAAiB,EACjB,KAA0B,EAClB,EAAE,CACV,KAAK,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;IAC/B,MAAM,EAAE,GAAG,GAAG,SAAS,IAAI,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;IAC5D,KAAK,CAAC,GAAG,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;IACrB,OAAO,EAAE,CAAC;AACZ,CAAC,CAAC,CAAC;AAEL,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,OAAe,EAAwB,EAAE;IACnE,MAAM,UAAU,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC7C,MAAM,UAAU,GAAG,IAAI,GAAG,EAAkB,CAAC;IAE7C,oEAAoE;IACpE,IAAI,IAAI,GAAG,uBAAuB,CAChC,OAAO,EACP,uDAAuD,EACvD,YAAY,EACZ,UAAU,CACX,CAAC;IAEF,mEAAmE;IACnE,IAAI,GAAG,uBAAuB,CAAC,IAAI,EAAE,2BAA2B,EAAE,WAAW,EAAE,UAAU,CAAC,CAAC;IAE3F,uBAAuB;IACvB,IAAI,GAAG,uBAAuB,CAC5B,IAAI,EACJ,sCAAsC,EACtC,YAAY,EACZ,UAAU,CACX,CAAC;IACF,IAAI,GAAG,uBAAuB,CAC5B,IAAI,EACJ,sCAAsC,EACtC,YAAY,EACZ,UAAU,CACX,CAAC;IACF,IAAI,GAAG,uBAAuB,CAAC,IAAI,EAAE,kBAAkB,EAAE,aAAa,EAAE,UAAU,CAAC,CAAC;IACpF,IAAI,GAAG,uBAAuB,CAAC,IAAI,EAAE,kBAAkB,EAAE,aAAa,EAAE,UAAU,CAAC,CAAC;IAEpF,eAAe;IACf,IAAI,GAAG,uBAAuB,CAAC,IAAI,EAAE,kBAAkB,EAAE,aAAa,EAAE,UAAU,CAAC,CAAC;IACpF,IAAI,GAAG,uBAAuB,CAC5B,IAAI,EACJ,iDAAiD,EACjD,aAAa,EACb,UAAU,CACX,CAAC;IAEF,8CAA8C;IAC9C,KAAK,MAAM,CAAC,EAAE,EAAE,IAAI,CAAC,IAAI,UAAU,EAAE,CAAC;QACpC,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACnC,CAAC;IAED,MAAM,OAAO,GAAG,CAAC,IAAY,EAAU,EAAE;QACvC,IAAI,MAAM,GAAG,IAAI,CAAC;QAClB,KAAK,MAAM,CAAC,EAAE,EAAE,QAAQ,CAAC,IAAI,UAAU,EAAE,CAAC;YACxC,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC3C,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC,CAAC;IAEF,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;AAC3B,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,OAAe,EAAW,EAAE;IACxD,MAAM,SAAS,GAAG,OAAO;SACtB,OAAO,CAAC,uDAAuD,EAAE,EAAE,CAAC;SACpE,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC;SACzB,OAAO,CAAC,yBAAyB,EAAE,IAAI,CAAC,CAAC;IAE5C,OAAO,6EAA6E,CAAC,IAAI,CACvF,SAAS,CACV,CAAC;AACJ,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { CustomToken } from '../types.js';
2
+ export declare const generateToc: (tokens: CustomToken[], depthInput?: number) => string;
3
+ //# sourceMappingURL=toc.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toc.d.ts","sourceRoot":"","sources":["../../../src/markdown/toc.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAc,MAAM,aAAa,CAAC;AAM3D,eAAO,MAAM,WAAW,GAAI,QAAQ,WAAW,EAAE,EAAE,aAAa,MAAM,KAAG,MA8BxE,CAAC"}
@@ -0,0 +1,29 @@
1
+ import { Marked } from 'marked';
2
+ import { normalizeTocDepth } from '../utils/validation.js';
3
+ const stripNestedAnchors = (value) => value.replace(/<a\s+[^>]*>([\s\S]*?)<\/a>/gi, '$1');
4
+ export const generateToc = (tokens, depthInput) => {
5
+ const depth = normalizeTocDepth(depthInput);
6
+ const headings = [];
7
+ const inlineParser = new Marked();
8
+ const walk = (items) => {
9
+ for (const token of items) {
10
+ if (token.type === 'heading' && token.depth !== undefined && token.depth <= depth) {
11
+ headings.push({
12
+ level: token.depth,
13
+ text: inlineParser.parseInline(token.text ?? ''),
14
+ id: token.id ?? ''
15
+ });
16
+ }
17
+ if (token.tokens?.length)
18
+ walk(token.tokens);
19
+ }
20
+ };
21
+ walk(tokens);
22
+ if (!headings.length)
23
+ return '';
24
+ const listItems = headings
25
+ .map((heading) => `<li class="toc-level-${heading.level}"><a href="#${heading.id}">${stripNestedAnchors(heading.text)}</a></li>`)
26
+ .join('\n');
27
+ return `<div class="toc"><h2>Table of Contents</h2><ul>${listItems}</ul></div>`;
28
+ };
29
+ //# sourceMappingURL=toc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toc.js","sourceRoot":"","sources":["../../../src/markdown/toc.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAE3D,MAAM,kBAAkB,GAAG,CAAC,KAAa,EAAU,EAAE,CACnD,KAAK,CAAC,OAAO,CAAC,8BAA8B,EAAE,IAAI,CAAC,CAAC;AAEtD,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,MAAqB,EAAE,UAAmB,EAAU,EAAE;IAChF,MAAM,KAAK,GAAG,iBAAiB,CAAC,UAAU,CAAC,CAAC;IAC5C,MAAM,QAAQ,GAAiB,EAAE,CAAC;IAClC,MAAM,YAAY,GAAG,IAAI,MAAM,EAAE,CAAC;IAElC,MAAM,IAAI,GAAG,CAAC,KAAoB,EAAQ,EAAE;QAC1C,KAAK,MAAM,KAAK,IAAI,KAAK,EAAE,CAAC;YAC1B,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,KAAK,IAAI,KAAK,EAAE,CAAC;gBAClF,QAAQ,CAAC,IAAI,CAAC;oBACZ,KAAK,EAAE,KAAK,CAAC,KAAK;oBAClB,IAAI,EAAE,YAAY,CAAC,WAAW,CAAC,KAAK,CAAC,IAAI,IAAI,EAAE,CAAW;oBAC1D,EAAE,EAAE,KAAK,CAAC,EAAE,IAAI,EAAE;iBACnB,CAAC,CAAC;YACL,CAAC;YAED,IAAI,KAAK,CAAC,MAAM,EAAE,MAAM;gBAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAC/C,CAAC;IACH,CAAC,CAAC;IAEF,IAAI,CAAC,MAAM,CAAC,CAAC;IACb,IAAI,CAAC,QAAQ,CAAC,MAAM;QAAE,OAAO,EAAE,CAAC;IAEhC,MAAM,SAAS,GAAG,QAAQ;SACvB,GAAG,CACF,CAAC,OAAO,EAAE,EAAE,CACV,wBAAwB,OAAO,CAAC,KAAK,eAAe,OAAO,CAAC,EAAE,KAAK,kBAAkB,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW,CACjH;SACA,IAAI,CAAC,IAAI,CAAC,CAAC;IAEd,OAAO,kDAAkD,SAAS,aAAa,CAAC;AAClF,CAAC,CAAC"}
@@ -0,0 +1,17 @@
1
+ import type { RendererOptions, Frontmatter, CustomToken } from './types.js';
2
+ export declare class Renderer {
3
+ private options;
4
+ private browser;
5
+ private page;
6
+ constructor(options?: RendererOptions);
7
+ init(): Promise<void>;
8
+ close(): Promise<void>;
9
+ parseFrontmatter(markdown: string): {
10
+ data: Frontmatter;
11
+ content: string;
12
+ };
13
+ generateToc(tokens: CustomToken[], depth?: number): string;
14
+ renderHtml(md: string, overrides?: RendererOptions): Promise<string>;
15
+ generatePdf(md: string, outputPath: string, overrides?: RendererOptions): Promise<void>;
16
+ }
17
+ //# sourceMappingURL=renderer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../../src/renderer.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,eAAe,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAY5E,qBAAa,QAAQ;IACnB,OAAO,CAAC,OAAO,CAAkB;IACjC,OAAO,CAAC,OAAO,CAAwB;IACvC,OAAO,CAAC,IAAI,CAAqB;gBAErB,OAAO,GAAE,eAAoB;IAInC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAUrB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAM5B,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG;QAAE,IAAI,EAAE,WAAW,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE;IAQ1E,WAAW,CAAC,MAAM,EAAE,WAAW,EAAE,EAAE,KAAK,SAAI,GAAG,MAAM;IAI/C,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,GAAE,eAAoB,GAAG,OAAO,CAAC,MAAM,CAAC;IAmExE,WAAW,CACf,EAAE,EAAE,MAAM,EACV,UAAU,EAAE,MAAM,EAClB,SAAS,GAAE,eAAoB,GAC9B,OAAO,CAAC,IAAI,CAAC;CA4FjB"}
@@ -0,0 +1,181 @@
1
+ import puppeteer from 'puppeteer';
2
+ import { readFile, writeFile, unlink, rm, mkdtemp } from 'fs/promises';
3
+ import { join, dirname, resolve } from 'path';
4
+ import { tmpdir } from 'os';
5
+ import { fileURLToPath, pathToFileURL } from 'url';
6
+ import GithubSlugger from 'github-slugger';
7
+ import { parseFrontmatter } from './markdown/frontmatter.js';
8
+ import { protectMath, hasMathSyntax } from './markdown/math.js';
9
+ import { createMarkedInstance } from './markdown/marked.js';
10
+ import { generateToc } from './markdown/toc.js';
11
+ import { renderTemplate } from './html/template.js';
12
+ import { normalizePaperFormat, normalizeTocDepth, parseMargin } from './utils/validation.js';
13
+ const __dirname = dirname(fileURLToPath(import.meta.url));
14
+ const read = (p) => readFile(join(__dirname, p), 'utf-8');
15
+ const stylesPromise = Promise.all([read('styles/default.css'), read('styles/github.css')]);
16
+ export class Renderer {
17
+ options;
18
+ browser = null;
19
+ page = null;
20
+ constructor(options = {}) {
21
+ this.options = { margin: '20mm', format: 'A4', ...options };
22
+ }
23
+ async init() {
24
+ if (!this.browser) {
25
+ this.browser = await puppeteer.launch({
26
+ headless: true,
27
+ args: ['--no-sandbox', '--disable-setuid-sandbox']
28
+ });
29
+ this.page = await this.browser.newPage();
30
+ }
31
+ }
32
+ async close() {
33
+ if (this.page)
34
+ await this.page.close();
35
+ if (this.browser)
36
+ await this.browser.close();
37
+ this.page = this.browser = null;
38
+ }
39
+ parseFrontmatter(markdown) {
40
+ const parsed = parseFrontmatter(markdown);
41
+ for (const warning of parsed.warnings) {
42
+ console.warn(warning);
43
+ }
44
+ return { data: parsed.data, content: parsed.content };
45
+ }
46
+ generateToc(tokens, depth = 6) {
47
+ return generateToc(tokens, depth);
48
+ }
49
+ async renderHtml(md, overrides = {}) {
50
+ const opts = { ...this.options, ...overrides };
51
+ const parsedFrontmatter = parseFrontmatter(md);
52
+ const { data, content } = parsedFrontmatter;
53
+ for (const warning of parsedFrontmatter.warnings) {
54
+ console.warn(warning);
55
+ }
56
+ const slugger = new GithubSlugger();
57
+ const marked = createMarkedInstance(slugger);
58
+ // Protect math from Marked's break conversion and entity escaping.
59
+ const { text: safeContent, restore: restoreMath } = protectMath(content);
60
+ const tokens = marked.lexer(safeContent);
61
+ if (marked.defaults.walkTokens) {
62
+ void marked.walkTokens(tokens, marked.defaults.walkTokens);
63
+ }
64
+ const footnoteIndex = tokens.findIndex((t) => t.type === 'footnotes');
65
+ if (footnoteIndex !== -1) {
66
+ const [footnoteToken] = tokens.splice(footnoteIndex, 1);
67
+ if (footnoteToken)
68
+ tokens.push(footnoteToken);
69
+ }
70
+ const tocDepth = normalizeTocDepth(typeof data.tocDepth === 'number' ? data.tocDepth : opts.tocDepth);
71
+ const hasTocPlaceholder = tokens.some((t) => t.type === 'tocPlaceholder');
72
+ const frontmatterToc = typeof data.toc === 'boolean' ? data.toc : undefined;
73
+ const tocEnabled = opts.toc ?? frontmatterToc ?? false;
74
+ const tocHtml = tocEnabled || hasTocPlaceholder ? this.generateToc(tokens, tocDepth) : '';
75
+ let html = restoreMath(marked.parser(tokens));
76
+ if (tocHtml) {
77
+ if (html.includes('[[TOC_PLACEHOLDER]]')) {
78
+ html = html.replace('[[TOC_PLACEHOLDER]]', tocHtml);
79
+ }
80
+ else if (tocEnabled && !hasTocPlaceholder) {
81
+ html = tocHtml + html;
82
+ }
83
+ }
84
+ let customCssContent = '';
85
+ if (opts.customCss) {
86
+ try {
87
+ customCssContent = await readFile(resolve(opts.customCss), 'utf-8');
88
+ }
89
+ catch (error) {
90
+ const message = error instanceof Error ? error.message : String(error);
91
+ throw new Error(`Failed to read custom CSS at "${opts.customCss}": ${message}`);
92
+ }
93
+ }
94
+ const [defaultCss, highlightCss] = await stylesPromise;
95
+ const css = `${defaultCss}\n${highlightCss}\n${customCssContent}`;
96
+ const title = typeof data.title === 'string' ? data.title : 'Markdown Document';
97
+ return renderTemplate({
98
+ templatePath: opts.template,
99
+ title: overrides.title ?? title,
100
+ css,
101
+ content: html,
102
+ basePath: opts.basePath,
103
+ includeMathJax: opts.math !== false && hasMathSyntax(content)
104
+ });
105
+ }
106
+ async generatePdf(md, outputPath, overrides = {}) {
107
+ const opts = { ...this.options, ...overrides };
108
+ const html = await this.renderHtml(md, opts);
109
+ await this.init();
110
+ if (!this.page)
111
+ throw new Error('Browser page not initialized');
112
+ const tempDir = await mkdtemp(join(tmpdir(), 'convpdf-'));
113
+ const tempHtmlPath = join(tempDir, 'document.html');
114
+ await writeFile(tempHtmlPath, html, 'utf-8');
115
+ try {
116
+ await this.page.emulateMediaType('print');
117
+ await this.page.goto(pathToFileURL(tempHtmlPath).href, {
118
+ waitUntil: 'networkidle0',
119
+ timeout: 60000
120
+ });
121
+ await this.page.evaluate(async () => {
122
+ const images = Array.from(document.querySelectorAll('img'));
123
+ await Promise.all(images.map((img) => {
124
+ if (img.complete)
125
+ return Promise.resolve();
126
+ return new Promise((resolve) => {
127
+ img.addEventListener('load', () => {
128
+ resolve();
129
+ }, { once: true });
130
+ img.addEventListener('error', () => {
131
+ resolve();
132
+ }, { once: true });
133
+ setTimeout(resolve, 5000); // Max 5s per image
134
+ });
135
+ }));
136
+ const win = window;
137
+ if (!document.getElementById('MathJax-script'))
138
+ return;
139
+ await new Promise((resolve, reject) => {
140
+ const startedAt = Date.now();
141
+ const tick = () => {
142
+ if (win.MathJax?.typesetPromise) {
143
+ resolve();
144
+ return;
145
+ }
146
+ if (Date.now() - startedAt > 10000) {
147
+ reject(new Error('MathJax did not initialize within 10s'));
148
+ return;
149
+ }
150
+ setTimeout(tick, 100);
151
+ };
152
+ tick();
153
+ });
154
+ await win.MathJax?.typesetPromise?.();
155
+ });
156
+ const margin = parseMargin(opts.margin);
157
+ const format = normalizePaperFormat(typeof opts.format === 'string' ? opts.format : undefined);
158
+ await this.page.pdf({
159
+ path: outputPath,
160
+ format,
161
+ printBackground: true,
162
+ margin,
163
+ displayHeaderFooter: !!(opts.headerTemplate || opts.footerTemplate),
164
+ headerTemplate: opts.headerTemplate || '<span></span>',
165
+ footerTemplate: opts.footerTemplate ||
166
+ '<div style="font-size: 10px; width: 100%; text-align: center; color: #666;"><span class="pageNumber"></span> / <span class="totalPages"></span></div>'
167
+ });
168
+ }
169
+ catch (e) {
170
+ if (e instanceof Error && e.message.includes('Session closed')) {
171
+ this.page = null;
172
+ }
173
+ throw e;
174
+ }
175
+ finally {
176
+ await unlink(tempHtmlPath).catch(() => { });
177
+ await rm(tempDir, { recursive: true, force: true }).catch(() => { });
178
+ }
179
+ }
180
+ }
181
+ //# sourceMappingURL=renderer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"renderer.js","sourceRoot":"","sources":["../../src/renderer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,147 @@
1
+ :root {
2
+ --primary-color: #333;
3
+ --bg-color: #fff;
4
+ --code-bg: #f6f8fa;
5
+ --border-color: #d0d7de;
6
+ }
7
+
8
+ body {
9
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
10
+ font-size: 16px;
11
+ line-height: 1.6;
12
+ color: var(--primary-color);
13
+ background-color: var(--bg-color);
14
+ max-width: 800px;
15
+ margin: 0 auto;
16
+ padding: 2rem;
17
+ }
18
+
19
+ h1, h2, h3, h4, h5, h6 {
20
+ margin-top: 24px;
21
+ margin-bottom: 16px;
22
+ font-weight: 600;
23
+ line-height: 1.25;
24
+ }
25
+
26
+ h1 { font-size: 2em; padding-bottom: 0.3em; border-bottom: 1px solid var(--border-color); }
27
+ h2 { font-size: 1.5em; padding-bottom: 0.3em; border-bottom: 1px solid var(--border-color); }
28
+
29
+ a { color: #0969da; text-decoration: none; }
30
+ a:hover { text-decoration: underline; }
31
+
32
+ p, blockquote, ul, ol, dl, table, pre { margin-top: 0; margin-bottom: 16px; }
33
+
34
+ blockquote {
35
+ padding: 0 1em;
36
+ color: #636c76;
37
+ border-left: 0.25em solid var(--border-color);
38
+ }
39
+
40
+ table {
41
+ border-spacing: 0;
42
+ border-collapse: collapse;
43
+ width: 100%;
44
+ }
45
+
46
+ table th, table td {
47
+ padding: 6px 13px;
48
+ border: 1px solid var(--border-color);
49
+ }
50
+
51
+ table tr:nth-child(2n) { background-color: #f6f8fa; }
52
+
53
+ pre {
54
+ padding: 16px;
55
+ overflow: auto;
56
+ font-size: 85%;
57
+ line-height: 1.45;
58
+ background-color: var(--code-bg);
59
+ border-radius: 6px;
60
+ }
61
+
62
+ code {
63
+ padding: 0.2em 0.4em;
64
+ margin: 0;
65
+ font-size: 85%;
66
+ background-color: rgba(175, 184, 193, 0.2);
67
+ border-radius: 6px;
68
+ }
69
+
70
+ pre code {
71
+ padding: 0;
72
+ background-color: transparent;
73
+ }
74
+
75
+ /* Footnotes */
76
+ .footnotes {
77
+ font-size: 85%;
78
+ color: #636c76;
79
+ border-top: 1px solid var(--border-color);
80
+ margin-top: 40px;
81
+ }
82
+
83
+ .footnotes-title {
84
+ font-size: 1.2rem;
85
+ margin-bottom: 0.5rem;
86
+ font-weight: bold;
87
+ }
88
+
89
+ /* TOC */
90
+ .toc {
91
+ background-color: #f8f9fa;
92
+ padding: 1rem;
93
+ border-radius: 6px;
94
+ margin-bottom: 2rem;
95
+ border: 1px solid var(--border-color);
96
+ page-break-inside: avoid;
97
+ }
98
+
99
+ .toc h2 {
100
+ margin-top: 0;
101
+ font-size: 1.2rem;
102
+ border-bottom: none;
103
+ }
104
+
105
+ .toc ul {
106
+ list-style: none;
107
+ padding-left: 0;
108
+ margin-bottom: 0;
109
+ }
110
+
111
+ .toc li { margin-bottom: 0.25rem; }
112
+ .toc-level-1 { font-weight: bold; }
113
+ .toc-level-2 { padding-left: 1rem; }
114
+ .toc-level-3 { padding-left: 2rem; }
115
+ .toc-level-4 { padding-left: 3rem; }
116
+ .toc-level-5 { padding-left: 4rem; }
117
+ .toc-level-6 { padding-left: 5rem; }
118
+
119
+ @media print {
120
+ body {
121
+ padding: 0;
122
+ max-width: none;
123
+ width: 100%;
124
+ font-size: 12pt;
125
+ }
126
+
127
+ /* Prevent long content from causing the page to zoom out */
128
+ pre, code, table {
129
+ max-width: 100%;
130
+ overflow-wrap: break-word;
131
+ word-wrap: break-word;
132
+ }
133
+
134
+ pre {
135
+ white-space: pre-wrap;
136
+ word-break: break-all;
137
+ }
138
+
139
+ img {
140
+ max-width: 100%;
141
+ height: auto;
142
+ }
143
+
144
+ .page-break { page-break-before: always; }
145
+ h1, h2, h3, h4, h5, h6 { page-break-after: avoid; }
146
+ table, pre, blockquote, .toc { page-break-inside: avoid; }
147
+ }