S SmartDocs
系列: traffic light bash 158 行 · 更新于 2026-07-09

convert_to_pdf.sh

traffic_light/convert_to_pdf.sh

#!/bin/bash
# Convert Markdown -> styled HTML -> PDF (via headless Chrome)
# Usage: ./convert_to_pdf.sh input.md [output.pdf]

set -euo pipefail

INPUT="${1:?Usage: $0 input.md [output.pdf]}"
BASENAME="$(basename "${INPUT%.md}")"
OUTPUT="${2:-${BASENAME}.pdf}"
WORKDIR="$(mktemp -d)"
HTML_BODY="$WORKDIR/body.html"
HTML_FULL="$WORKDIR/full.html"

trap 'rm -rf "$WORKDIR"' EXIT

# 1) Markdown -> HTML body (GitHub Flavored Markdown)
md2html --github "$INPUT" -o "$HTML_BODY"

# 2) Wrap with full HTML + CSS optimized for Chinese + code blocks + tables
cat > "$HTML_FULL" <<'EOF'
<!DOCTYPE html>
<html lang="zh-Hant">
<head>
<meta charset="UTF-8">
<title>__TITLE__</title>
<style>
  @page {
    size: A4;
    margin: 18mm 16mm 20mm 16mm;
    @bottom-right { content: counter(page) " / " counter(pages); }
  }
  html { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
  body {
    font-family: "PingFang TC", "Heiti TC", "Microsoft JhengHei",
                 "Noto Sans CJK TC", "Helvetica Neue", Helvetica, Arial, sans-serif;
    font-size: 10.5pt;
    line-height: 1.55;
    color: #1f1f1f;
    max-width: 100%;
  }
  h1, h2, h3, h4, h5, h6 { color: #0a3d62; line-height: 1.25; margin-top: 1.4em; }
  h1 { font-size: 22pt; border-bottom: 2px solid #0a3d62; padding-bottom: 6px; }
  h2 { font-size: 16pt; border-bottom: 1px solid #d0d7de; padding-bottom: 4px; margin-top: 1.6em; }
  h3 { font-size: 13pt; }
  h4 { font-size: 11.5pt; }
  h1, h2 { page-break-after: avoid; }
  h3, h4 { page-break-after: avoid; }
  p, ul, ol, table { page-break-inside: avoid; }

  a { color: #0969da; text-decoration: none; }

  /* Inline code */
  code {
    font-family: "SF Mono", "JetBrains Mono", "Menlo", "Consolas", monospace;
    font-size: 9pt;
    background: #f3f4f6;
    color: #c7254e;
    padding: 1px 5px;
    border-radius: 3px;
  }

  /* Code block */
  pre {
    font-family: "SF Mono", "JetBrains Mono", "Menlo", "Consolas", monospace;
    font-size: 8.5pt;
    line-height: 1.4;
    background: #f6f8fa;
    border: 1px solid #d0d7de;
    border-radius: 6px;
    padding: 10px 12px;
    overflow-x: auto;
    white-space: pre;
    page-break-inside: avoid;
    color: #1f1f1f;
  }
  pre code {
    background: transparent;
    color: inherit;
    padding: 0;
    font-size: inherit;
  }

  /* Tables */
  table {
    border-collapse: collapse;
    width: 100%;
    margin: 10px 0;
    font-size: 9.5pt;
  }
  th, td {
    border: 1px solid #d0d7de;
    padding: 6px 9px;
    vertical-align: top;
    text-align: left;
  }
  th { background: #f1f5f9; color: #0a3d62; font-weight: 600; }
  tr:nth-child(even) td { background: #fafbfc; }

  /* Block quote */
  blockquote {
    border-left: 4px solid #0a3d62;
    background: #f0f6fb;
    margin: 10px 0;
    padding: 8px 14px;
    color: #1f2937;
  }

  /* Horizontal rule */
  hr { border: none; border-top: 1px solid #d0d7de; margin: 22px 0; }

  /* Lists */
  ul, ol { padding-left: 22px; }
  li { margin: 2px 0; }

  /* Cover */
  .cover {
    text-align: center;
    padding-top: 30mm;
    page-break-after: always;
  }
  .cover h1 { border: none; font-size: 26pt; }
  .cover p { font-size: 13pt; color: #555; margin-top: 12px; }
</style>
</head>
<body>
__BODY__
</body>
</html>
EOF

# Inject body + title
TITLE="$BASENAME"
python3 - "$HTML_FULL" "$HTML_BODY" "$TITLE" <<'PY'
import sys, pathlib
full_path, body_path, title = sys.argv[1], sys.argv[2], sys.argv[3]
body = pathlib.Path(body_path).read_text(encoding="utf-8")
full = pathlib.Path(full_path).read_text(encoding="utf-8")
full = full.replace("__BODY__", body).replace("__TITLE__", title)
pathlib.Path(full_path).write_text(full, encoding="utf-8")
PY

# 3) HTML -> PDF via headless Chrome
CHROME="/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"

OUTPUT_ABS="$(cd "$(dirname "$OUTPUT")" && pwd)/$(basename "$OUTPUT")"

"$CHROME" \
  --headless=new \
  --disable-gpu \
  --no-pdf-header-footer \
  --print-to-pdf="$OUTPUT_ABS" \
  --print-to-pdf-no-header \
  --no-sandbox \
  --virtual-time-budget=10000 \
  "file://$HTML_FULL" 2>/dev/null

echo "PDF generated: $OUTPUT_ABS"
ls -lh "$OUTPUT_ABS"