@jiyeqian/md2pdf 1.4.0 → 1.5.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 CHANGED
@@ -3,7 +3,13 @@
3
3
  把 Markdown 排成**优雅的中文 A4 PDF**:报头大标题、元信息条、精心排过的表格/代码/引用/列表、页脚页码。
4
4
  不是 pandoc 的默认样式 —— 是可以直接拿去打印、发给别人看的版式。
5
5
 
6
- ![themes](docs/themes.png)
6
+ **elegant 主题**(默认,墨蓝 + 古铜):
7
+
8
+ ![elegant 主题效果](docs/theme-elegant.png)
9
+
10
+ **minimal 主题**(黑白公文风):
11
+
12
+ ![minimal 主题效果](docs/theme-minimal.png)
7
13
 
8
14
  仓库:https://cnb.cool/jiyeqian/md2pdf
9
15
 
@@ -121,6 +127,7 @@ node ci/inspect-pdf.mjs out.pdf
121
127
  - 首个 H1 提升为报头大标题,正文不再重复;其后的首段自动成为导语。
122
128
  - YAML frontmatter 的 `name` / `description` 生成元信息条;description 里「适用于…」「不用于…」会自动拆成「适用 / 不适用」两栏。
123
129
  - H2 自动分节并加色块标记;表格深色表头+隔行浅底;有序列表用圆形序号。
130
+ - 数学公式:正文里的 `$...$`(行内)与 `$$...$$`(独立成行)由内置 MathJax 渲染(SVG 输出,零字体依赖)。
124
131
  - 相对路径图片自动解析成绝对地址,能正常进入 PDF。
125
132
 
126
133
  ## 改样式
@@ -220,4 +227,4 @@ skill/SKILL.md Agent 技能说明书(postinstall 会装到技能目
220
227
 
221
228
  MIT
222
229
 
223
- 第三方组件:`vendor/marked.esm.js` 来自 [marked](https://github.com/markedjs/marked)(MIT License),随仓库分发以便零依赖安装。
230
+ 第三方组件:`vendor/marked.esm.js` 来自 [marked](https://github.com/markedjs/marked)(MIT License);`vendor/mathjax/tex-svg.js` 来自 [MathJax](https://github.com/mathjax/MathJax)(Apache-2.0 License)。均随仓库分发以便零依赖安装。
package/assets/shell.html CHANGED
@@ -27,5 +27,7 @@
27
27
  <span>{{COLOPHON_RIGHT}}</span>
28
28
  </div>
29
29
 
30
+ {{MATHJAX}}
31
+
30
32
  </body>
31
33
  </html>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jiyeqian/md2pdf",
3
- "version": "1.4.0",
3
+ "version": "1.5.0",
4
4
  "description": "把 Markdown 排成优雅的中文 A4 PDF(无头 Chrome 渲染,带报头、表格/代码排版与页脚页码)",
5
5
  "type": "module",
6
6
  "bin": {
package/src/md2pdf.mjs CHANGED
@@ -40,7 +40,7 @@ import { fileURLToPath, pathToFileURL } from 'node:url';
40
40
  const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
41
41
  const ASSETS = path.join(ROOT, 'assets');
42
42
 
43
- const VERSION = '1.4.0';
43
+ const VERSION = '1.5.0';
44
44
 
45
45
  // Node ≥ 22 有全局 WebSocket;更老的版本退回到内置的极简实现
46
46
  let _WS;
@@ -293,6 +293,19 @@ class Chrome {
293
293
  await this.send('Page.navigate', { url: pathToFileURL(htmlPath).href });
294
294
  await loaded;
295
295
  await new Promise(r => setTimeout(r, 250));
296
+ // MathJax 排版:正文含公式时,轮询等待 typeset 完成再打印(否则公式是空白)
297
+ if (opts.waitMath) {
298
+ const t0 = Date.now();
299
+ while (Date.now() - t0 < 10000) {
300
+ let done = false;
301
+ try {
302
+ const r = await this.send('Runtime.evaluate', { expression: 'window.__md2pdfMathReady === true', returnByValue: true });
303
+ done = !!(r && r.result && r.result.value);
304
+ } catch { /* 忽略运行时异常,继续等待 */ }
305
+ if (done) break;
306
+ await new Promise(r => setTimeout(r, 100));
307
+ }
308
+ }
296
309
  const base = {
297
310
  printBackground: true,
298
311
  preferCSSPageSize: true,
@@ -328,6 +341,9 @@ async function renderOne(mdPath, opts, chrome, marked, tmpRoot) {
328
341
  const { fm, body } = splitFrontmatter(src);
329
342
  const isSkill = path.basename(mdPath) === 'SKILL.md' || !!fm.name;
330
343
 
344
+ // 数学公式:检测 $...$ 或 $...$,命中则注入 MathJax(SVG 输出,零字体依赖)
345
+ const hasMath = /\$\$|\$[^$\n]+\$/.test(body);
346
+
331
347
  let html = marked.parse(body, { gfm: true, breaks: false, async: false });
332
348
 
333
349
  // 标题:正文首个 H1 → 报头
@@ -406,6 +422,24 @@ async function renderOne(mdPath, opts, chrome, marked, tmpRoot) {
406
422
  const fill = (tpl, map) => Object.entries(map).reduce(
407
423
  (s, [k, v]) => s.split(k).join(v), tpl);
408
424
 
425
+ const mathUrl = pathToFileURL(path.join(ROOT, 'vendor', 'mathjax', 'tex-svg.js')).href;
426
+ const mathScript = hasMath ? [
427
+ '<script>',
428
+ 'window.__md2pdfMathReady = false;',
429
+ 'window.MathJax = {',
430
+ ' tex: { inlineMath: [["$", "$"]] },',
431
+ ' svg: { fontCache: "none" },',
432
+ ' startup: {',
433
+ ' ready: function () {',
434
+ ' MathJax.startup.defaultReady();',
435
+ ' MathJax.startup.promise.then(function () { window.__md2pdfMathReady = true; });',
436
+ ' }',
437
+ ' }',
438
+ '};',
439
+ '</script>',
440
+ '<script src="' + mathUrl + '" id="MathJax-script"></script>',
441
+ ].join('\n') : '';
442
+
409
443
  const shell = await readFile(path.join(ASSETS, 'shell.html'), 'utf8');
410
444
  const out = fill(shell, {
411
445
  '{{TITLE}}': esc(title),
@@ -417,6 +451,7 @@ async function renderOne(mdPath, opts, chrome, marked, tmpRoot) {
417
451
  '{{BODY}}': html,
418
452
  '{{COLOPHON_LEFT}}': esc(colophonLeft),
419
453
  '{{COLOPHON_RIGHT}}': esc(colophonRight),
454
+ '{{MATHJAX}}': mathScript,
420
455
  });
421
456
 
422
457
  if (opts.htmlOnly) return { title, html: out };
@@ -431,7 +466,7 @@ async function renderOne(mdPath, opts, chrome, marked, tmpRoot) {
431
466
  <span style="flex:1;text-align:right;">${opts.footerRight || ''}</span>
432
467
  </div>`;
433
468
 
434
- const buf = await chrome.print(htmlPath, { footer: opts.footer, footerTemplate, landscape: opts.landscape, outline: opts.outline });
469
+ const buf = await chrome.print(htmlPath, { footer: opts.footer, footerTemplate, landscape: opts.landscape, outline: opts.outline, waitMath: hasMath });
435
470
 
436
471
  if (opts.keepHtml) {
437
472
  await writeFile(mdPath.replace(/\.md$/i, '.html'), out, 'utf8');