Add md-to-pdf.sh: convert Markdown to PDF via python-markdown + weasyprint
Companion to docs/md-to-html.sh, for cases needing a standalone PDF rather than a themed HTML page.main
parent
c1e54f4f75
commit
fa78fc9a66
|
|
@ -0,0 +1,86 @@
|
|||
#!/usr/bin/env bash
|
||||
# md-to-pdf.sh — Convert a Markdown file to PDF.
|
||||
#
|
||||
# Usage:
|
||||
# md-to-pdf.sh input.md [output.pdf]
|
||||
#
|
||||
# output.pdf defaults to input's basename with a .pdf extension, written
|
||||
# next to input.md.
|
||||
#
|
||||
# Requires: python3 with the 'markdown' and 'weasyprint' packages
|
||||
# sudo pacman -S python-markdown python-weasyprint
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
if [[ $# -lt 1 ]]; then
|
||||
echo "Usage: $0 input.md [output.pdf]" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
src="$1"
|
||||
[[ -f "$src" ]] || { echo "md-to-pdf: file not found: $src" >&2; exit 1; }
|
||||
|
||||
out="${2:-${src%.*}.pdf}"
|
||||
|
||||
for mod in markdown weasyprint; do
|
||||
python3 -c "import $mod" 2>/dev/null || {
|
||||
echo "md-to-pdf: missing python module '$mod' — install with:" >&2
|
||||
echo " sudo pacman -S python-markdown python-weasyprint" >&2
|
||||
exit 1
|
||||
}
|
||||
done
|
||||
|
||||
title=$(grep -m1 '^# ' "$src" | sed 's/^# //' || true)
|
||||
[[ -z "$title" ]] && title="$(basename "${src%.*}")"
|
||||
|
||||
python3 - "$src" "$out" "$title" <<'PYEOF'
|
||||
import sys
|
||||
import markdown
|
||||
from weasyprint import HTML
|
||||
|
||||
src, out, title = sys.argv[1], sys.argv[2], sys.argv[3]
|
||||
|
||||
with open(src, encoding="utf-8") as fh:
|
||||
text = fh.read()
|
||||
|
||||
body_html = markdown.markdown(
|
||||
text,
|
||||
extensions=["tables", "fenced_code", "toc", "def_list"],
|
||||
)
|
||||
|
||||
css = """
|
||||
@page { size: A4; margin: 2cm; }
|
||||
body {
|
||||
font-family: 'DejaVu Sans', sans-serif;
|
||||
font-size: 11pt;
|
||||
line-height: 1.5;
|
||||
color: #1a1a1a;
|
||||
}
|
||||
h1, h2, h3, h4 { font-weight: 700; line-height: 1.25; margin: 1.4em 0 0.5em; }
|
||||
h1 { font-size: 1.9em; border-bottom: 2px solid #333; padding-bottom: 0.2em; }
|
||||
h2 { font-size: 1.4em; border-bottom: 1px solid #999; padding-bottom: 0.15em; }
|
||||
h3 { font-size: 1.15em; }
|
||||
p, li { margin: 0.4em 0; }
|
||||
code, pre {
|
||||
font-family: 'DejaVu Sans Mono', monospace;
|
||||
font-size: 0.85em;
|
||||
background: #f2f2f2;
|
||||
border-radius: 3px;
|
||||
}
|
||||
code { padding: 0.1em 0.35em; }
|
||||
pre { padding: 0.8em 1em; overflow-x: auto; white-space: pre-wrap; }
|
||||
pre code { padding: 0; background: none; }
|
||||
table { width: 100%; border-collapse: collapse; margin: 1em 0; font-size: 0.85em; }
|
||||
th, td { border: 1px solid #999; padding: 0.4em 0.6em; text-align: left; vertical-align: top; }
|
||||
th { background: #e5e5e5; }
|
||||
tr:nth-child(even) td { background: #fafafa; }
|
||||
blockquote { border-left: 4px solid #999; margin: 1em 0; padding: 0.3em 1em; color: #444; }
|
||||
a { color: #1a4fa0; }
|
||||
"""
|
||||
|
||||
html = f"<!DOCTYPE html><html><head><meta charset='utf-8'><title>{title}</title>" \
|
||||
f"<style>{css}</style></head><body>{body_html}</body></html>"
|
||||
|
||||
HTML(string=html, base_url=src).write_pdf(out)
|
||||
print(f"Wrote {out}")
|
||||
PYEOF
|
||||
Loading…
Reference in New Issue