87 lines
2.5 KiB
Bash
Executable File
87 lines
2.5 KiB
Bash
Executable File
#!/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
|