当前位置: 首页 > news >正文

折腾笔记[55]-使用kimi转换markdown为pdf

摘要

在macOS上使用kimi-cli的markdown-to-pdf技能,通过Typst排版引擎将Markdown文档转换为精美排版的PDF,采用ViTAX风格的中文字体配置.

声明

本文人类为第一作者, 龙虾为通讯作者.本文有AI生成内容.

markdown-to-pdf技能

SKILL.md

<hr>
<p>name: markdown-to-pdf</p>
<h2 id="description-convert-markdown-documents-to-beautifully-styled-pdf-using-typst-with-vitax-inspired-typography-use-when-the-user-needs-to-1-convert-md-files-to-pdf-2-create-documents-with-chinese-text-and-code-blocks-3-apply-modern-blog-style-typography-to-documents-defaults-to-noto-sans-cjk-sc-for-chinese-and-noto-sans-for-latin-text-">description: Convert Markdown documents to beautifully styled PDF using Typst, with ViTAX-inspired typography. Use when the user needs to (1) convert .md files to PDF, (2) create documents with Chinese text and code blocks, (3) apply modern blog-style typography to documents. Defaults to Noto Sans CJK SC for Chinese and Noto Sans for Latin text.</h2>
<h1 id="markdown-to-pdf-skill">Markdown to PDF Skill</h1>
<p>Convert Markdown documents to PDF using a local <code>markdown2typst</code> converter and the Typst typesetting engine, with a ViTAX-inspired visual style.</p>
<h2 id="prerequisites">Prerequisites</h2>
<ul>
<li><strong>Node.js</strong> (for running <code>markdown2typst</code>)</li>
<li><strong>Typst</strong> installed at <code>/home/qsbye/.cargo/bin/typst</code></li>
<li><strong>Fonts available</strong>:<ul>
<li><strong>Noto Sans CJK SC</strong> — Chinese sans-serif at <code>~/.local/share/fonts/noto-cjk/NotoSansCJK.ttc</code></li>
<li><strong>Noto Sans</strong> — Latin sans-serif at <code>~/.local/share/fonts/noto/NotoSans-*.ttf</code></li>
<li><strong>Noto Sans Mono CJK SC</strong> — Chinese monospace</li>
</ul>
</li>
</ul>
<h2 id="vitax-style-features">ViTAX Style Features</h2>
<p>The PDF output mimics the clean, modern typography of the &quot;ViTAX: Building a Vision Transformer from Scratch&quot; blog:</p>
<ul>
<li><strong>Headings</strong>: Bold, large sans-serif (Noto Sans) with generous top margin</li>
<li><strong>Body text</strong>: 11pt Noto Sans, justified, 1.4em line spacing</li>
<li><strong>Code blocks</strong>: White background, light gray border, rounded corners, monospace font</li>
<li><strong>Blockquotes</strong>: Left gray border, italic text</li>
<li><strong>Links</strong>: Blue color (#2885e2)</li>
<li><strong>Lists</strong>: Bullet markers (• ◦ ▪)</li>
<li><strong>Tables</strong>: Light header background, rounded borders</li>
</ul>
<h2 id="quick-start">Quick Start</h2>
<h3 id="convert-a-markdown-file-to-pdf">Convert a Markdown file to PDF</h3>
<pre><code class="lang-bash"><span class="hljs-regexp">/home/</span>qsbye<span class="hljs-regexp">/.config/</span>agents<span class="hljs-regexp">/skills/m</span>arkdown-to-pdf<span class="hljs-regexp">/scripts/m</span>d2pdf.sh <span class="hljs-regexp">/path/</span>to<span class="hljs-regexp">/document.md /</span>path<span class="hljs-regexp">/to/</span>output.pdf
</code></pre>
<h3 id="generate-a-document-from-scratch">Generate a document from scratch</h3>
<pre><code class="lang-bash">cat &gt; /tmp/my_doc.md &lt;&lt; <span class="hljs-string">'MDEOF'</span>
# 我的文档标题这是一段中文正文,支持**粗体**和*斜体*。## 代码示例```python
def hello():print(<span class="hljs-string">"Hello, 世界!"</span>)
</code></pre>
<blockquote>
<p>这是一个引用块,带有左侧边框样式。
MDEOF</p>
</blockquote>
<p>/home/qsbye/.config/agents/skills/markdown-to-pdf/scripts/md2pdf.sh /tmp/my_doc.md /tmp/my_doc.pdf</p>
<pre><code>
## Step-by-Step PipelineThe conversion happens <span class="hljs-keyword">in</span> <span class="hljs-number">3</span> stages:<span class="hljs-number">1.</span> **Markdown → Raw Typst**: `md2typst.js` uses `markdown2typst.min.js` to convert Markdown syntax to Typst markup.
<span class="hljs-number">2.</span> **Apply ViTAX Style**: `apply-vitax.js` prepends the `vitax-template.typ` style rules (fonts, colors, spacing, <span class="hljs-keyword">code</span> blocks, blockquotes).
<span class="hljs-number">3.</span> **Typst → PDF**: `typst compile` renders the final PDF.## Scripts| Script | Purpose |
|--------|---------|
| `scripts/md2pdf.sh` | One-shot Markdown → PDF wrapper |
| `scripts/md2typst.js` | Markdown → raw Typst |
| `scripts/apply-vitax.js` | Inject ViTAX style template into raw Typst |
| `scripts/vitax-template.typ` | Typst show-rules defining the visual style |
| `scripts/markdown2typst.min.js` | Local copy <span class="hljs-keyword">of</span> the markdown2typst library |## Default Font SetupThe template automatically configures:```typst
#set text(font: <span class="hljs-string">"Noto Sans"</span>, lang: <span class="hljs-string">"zh"</span>, region: <span class="hljs-string">"cn"</span>)
</code></pre><p>For Chinese documents, no extra font configuration is needed.</p>
<h2 id="parameters">Parameters</h2>
<p>The wrapper script accepts:</p>
<pre><code class="lang-bash">md2pdf<span class="hljs-selector-class">.sh</span> &lt;<span class="hljs-selector-tag">input</span>.md&gt; &lt;output.pdf&gt;
</code></pre>
<p>No additional flags are required. Intermediate <code>.typ</code> files are written to <code>$TMPDIR</code> and cleaned up automatically by the OS.</p>
<h2 id="troubleshooting">Troubleshooting</h2>
<h3 id="-maximum-show-rule-depth-exceeded-when-converting-files-with-horizontal-rules"><code>maximum show rule depth exceeded</code> when converting files with horizontal rules</h3>
<ul>
<li><strong>Symptom</strong>: Typst compilation fails with <code>maximum show rule depth exceeded</code>.</li>
<li><strong>Root cause</strong>: A <code>#show line</code> rule matches the <code>line()</code> call inside its own body, creating infinite recursion.</li>
<li><strong>Fix</strong>: Restrict the selector so it does not match the inner call. In <code>vitax-template.typ</code>, change:<pre><code class="lang-typst"><span class="hljs-meta">#show <span class="hljs-meta-keyword">line</span>: it =&gt; { ... }</span>
</code></pre>
to:<pre><code class="lang-typst">#show line.<span class="hljs-keyword">where</span>(length: <span class="hljs-number">60</span>%): it =&gt; { ... }
</code></pre>
</li>
<li><strong>Note</strong>: <code>line.where(block: true)</code> does <strong>not</strong> work because the <code>line</code> element has no <code>block</code> field.</li>
</ul>

scripts/md2pdf.sh

#!/bin/bash
set -eSCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"if [ $# -lt 2 ]; thenecho "Usage: md2pdf.sh <input.md> <output.pdf>"exit 1
fiINPUT_MD="$1"
OUTPUT_PDF="$2"
BASENAME="$(basename "$INPUT_MD" .md)"
TMPDIR="${TMPDIR:-/tmp}"
RAW_TYP="$TMPDIR/${BASENAME}_raw.typ"
STYLED_TYP="$TMPDIR/${BASENAME}_styled.typ"# Step 1: Markdown -> Raw Typst
node "$SCRIPT_DIR/md2typst.js" "$INPUT_MD" "$RAW_TYP"# Step 2: Apply ViTAX style template
node "$SCRIPT_DIR/apply-vitax.js" "$RAW_TYP" "$STYLED_TYP"# Step 3: Typst -> PDF
typst compile "$STYLED_TYP" "$OUTPUT_PDF"echo "PDF generated: $OUTPUT_PDF"

scripts/md2typst.js

const fs = require('fs');
const { markdown2typst } = require('./markdown2typst.min.js');const inputFile = process.argv[2];
const outputFile = process.argv[3];if (!inputFile || !outputFile) {console.error('Usage: node md2typst.js <input.md> <output.typ>');process.exit(1);
}const md = fs.readFileSync(inputFile, 'utf-8');
const typ = markdown2typst(md);
fs.writeFileSync(outputFile, typ, 'utf-8');
console.log(`Converted ${inputFile} -> ${outputFile}`);

scripts/apply-vitax.js

const fs = require('fs');
const path = require('path');const rawTypFile = process.argv[2];
const outputTypFile = process.argv[3];if (!rawTypFile || !outputTypFile) {console.error('Usage: node apply-vitax.js <raw.typ> <output.typ>');process.exit(1);
}const templatePath = path.join(__dirname, 'vitax-template.typ');
const template = fs.readFileSync(templatePath, 'utf-8');
const raw = fs.readFileSync(rawTypFile, 'utf-8');// Combine template + raw content
const combined = template + '\n\n// === CONTENT START ===\n\n' + raw + '\n';fs.writeFileSync(outputTypFile, combined, 'utf-8');
console.log(`Applied ViTAX style: ${rawTypFile} -> ${outputTypFile}`);

scripts/vitax-template.typ (核心样式)

// ViTAX Style Template for Typst
#let vitax-colors = (primary: rgb(24, 188, 156),secondary: rgb(40, 133, 226),dark: rgb(44, 62, 80),light-bg: rgb(248, 249, 250),code-bg: rgb(255, 255, 255),code-border: rgb(193, 193, 193),text-muted: rgb(134, 142, 150),
)#let vitax-heading-font = "Noto Sans"
#let vitax-body-font = "Noto Sans"
#let vitax-code-font = "Noto Sans Mono CJK SC"#set page(paper: "a4", margin: (top: 2.5cm, bottom: 2.5cm, left: 2.5cm, right: 2.5cm))
#set text(font: vitax-body-font, size: 11pt, lang: "zh", region: "cn", fill: vitax-colors.dark)
#set par(leading: 1.4em, justify: true)// Headings
#set heading(numbering: none)
#show heading: it => { set text(font: vitax-heading-font, weight: 700); block(above: 1.5em, below: 0.8em, it) }
#show heading.where(level: 1): it => { set text(font: vitax-heading-font, weight: 700, size: 2em); block(above: 1.5em, below: 1em, it) }// Code blocks
#show raw.where(block: true): it => {block(width: 100%, fill: vitax-colors.code-bg, stroke: 0.5pt + vitax-colors.code-border,radius: 0.5em, inset: 1em, text(font: vitax-code-font, size: 0.85em, it))
}// Tables
#show table: it => {set text(font: vitax-body-font, size: 0.95em)block(stroke: 0.5pt + vitax-colors.code-border, radius: 0.3em, inset: 0pt, it)
}
#show table.cell: it => {if it.y == 0 { set text(weight: 700); table.cell(fill: vitax-colors.light-bg, it) } else { it }
}

运行效果

  1. 运行md2pdf.sh document.md output.pdf三步流水线转换
  2. Markdown → Typst → 应用ViTAX样式 → PDF
  3. 输出PDF支持中文、代码块、表格、引用等丰富排版
http://www.jsqmd.com/news/740280/

相关文章:

  • CF1608F MEX counting
  • Virtuoso ADE XL参数扫描实战:用gmid曲线指导MOS管尺寸优化(以IC618为例)
  • OTA校验失败、CRC对不上、版本号错乱——C语言固件升级链路11个关键断点调试技巧,工程师私藏手册
  • 折腾笔记[52]-使用kimi发送消息到matrix房间
  • 为内容创作平台集成 Taotoken 提供多样化的文本生成风格
  • 为什么你的Horovod训练总OOM?20年HPC架构师首次公开:4层内存泄漏配置链路与实时诊断脚本
  • MultiTimer vs. FreeRTOS软件定时器:在资源受限的STM32F4上,我为什么选择了它?
  • WorkshopDL:无需Steam客户端,轻松下载Steam创意工坊模组的终极方案
  • 别再死磕YOLOv5了!用CLIP+CRIS结构,手把手教你实现文本驱动的目标检测
  • 2026届学术党必备的十大AI辅助论文方案横评
  • 20260430
  • DataChain:构建面向对象存储的数据上下文层,实现AI时代数据处理革命
  • Stata数据合并保姆级避坑指南:从CSV导入到merge命令的完整流程
  • Windows 11 24H2 LTSC 微软商店一键安装完整指南:如何3分钟恢复完整应用生态
  • 杭州萧山区在职提升学历哪家好?萧山箭金学堂等五大机构深度测评榜 - 浙江行业评测
  • 3分钟搞定Android Studio中文界面:新手必备的完整免费汉化指南
  • 别再到处找了!电气AI项目数据集保姆级导航(含无人机巡检、负荷预测等60+资源)
  • 模型部署前必看:用Netron快速检查ONNX、TensorFlow模型结构,避开这些坑
  • FPGA新手避坑指南:用Verilog写自己的‘软’ROM存储波形,真的比用IP核好吗?
  • AI_10_Coze_Multi-Agent多智能体
  • python sanic
  • Taotoken模型广场如何帮助开发者根据场景选择合适大模型
  • python fastapi
  • 别再死记硬背命令了!用CREO 8.0参数化设计,一个矿泉水瓶模型搞定阵列、扫描、骨架模型三大核心
  • 超越基础UNet:在DRIVE数据集上尝试改进,聊聊我的损失函数调优与数据增强心得
  • Windows平台风扇控制技术深度解析:FanControl架构与实战配置指南
  • 如何实现AI到PSD的无损转换?Ai2Psd脚本终极指南
  • 微积分自学笔记(13):向量与空间解析几何
  • 长期使用 Taotoken 后对其计费透明性与账单追溯功能的评价
  • 从Kaggle金牌方案里,我扒出了3种给神经网络‘组队’的野路子(模型融合实战)