markdown-note-render

Convert Markdown documents to PDF or static HTML
git clone https://git.0xfab.ch/markdown-note-render.git
Log | Files | Refs

rendernote (8646B)


      1 #!/usr/bin/env bash
      2 
      3 main() {
      4     if [ $# -eq 0 ]; then
      5         cat <<EOF
      6 USAGE $0 [-pdf|-html] <markdown note> [<markdown note> ...]
      7 EOF
      8         exit 1
      9     fi
     10     local convert=render_html
     11     case "$1" in
     12         -pdf)  shift; convert=render_pdf;;
     13         -html) shift; convert=render_html;;
     14     esac
     15     for note; do
     16         ${convert} "${note}"
     17     done
     18 }
     19 
     20 render_pdf() {
     21     local input="$(mktemp)"
     22     if [ -z "$(awk '/^---$/,/^---$/' "${1}")" ]; then
     23         cat <<'EOF' >"${input}"
     24 ---
     25 fontsize: 12pt
     26 papersize: a4
     27 linkcolor: blue
     28 header-includes:
     29   - \usepackage[top=60pt,bottom=60pt,left=80pt,right=80pt]{geometry}
     30   - \usepackage{bm}
     31   - \usepackage{sectsty}
     32 include-before:
     33   - \allsectionsfont{\sffamily}
     34 ---
     35 EOF
     36     fi
     37     cat ${1} >>"${input}"
     38     pandoc \
     39         --from markdown --to pdf \
     40         --highlight-style pygments \
     41         --output "${1%.*}.pdf" "${input}"
     42     rm -f "${input}"
     43 }
     44 
     45 render_html() {
     46     local lua_filter="$(mktemp)"
     47     local html_template="$(mktemp)"
     48     local style_sheets="$(mktemp)"
     49     local raw_html="$(mktemp)"
     50 
     51     cat <<'EOF' >"${lua_filter}"
     52 function Math(elem)
     53     assert(FORMAT:match('html'))
     54     local wrap
     55     if elem.mathtype == 'InlineMath' then
     56         wrap = '<latexinline>' .. elem.text .. '</latexinline>'
     57     else
     58         wrap = '<latexdisplay>' .. elem.text .. '</latexdisplay>'
     59     end
     60     return pandoc.RawInline('html', wrap)
     61 end
     62 EOF
     63     cat <<'EOF' >"${html_template}"
     64 <!DOCTYPE html>
     65 <html xmlns="http://www.w3.org/1999/xhtml" lang="$lang$" xml:lang="$lang$"$if(dir)$ dir="$dir$"$endif$>
     66 <head>
     67   <meta charset="utf-8" />
     68   <meta name="generator" content="pandoc" />
     69   <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
     70 $for(author-meta)$
     71   <meta name="author" content="$author-meta$" />
     72 $endfor$
     73 $if(date-meta)$
     74   <meta name="dcterms.date" content="$date-meta$" />
     75 $endif$
     76 $if(keywords)$
     77   <meta name="keywords" content="$for(keywords)$$keywords$$sep$, $endfor$" />
     78 $endif$
     79 $if(description-meta)$
     80   <meta name="description" content="$description-meta$" />
     81 $endif$
     82   <title>$if(title-prefix)$$title-prefix$ – $endif$$pagetitle$</title>
     83   <style>
     84     $styles.html()$
     85   </style>
     86 $for(header-includes)$
     87   $header-includes$
     88 $endfor$
     89 </head>
     90 <body>
     91 $for(include-before)$
     92 $include-before$
     93 $endfor$
     94 $if(toc)$
     95 <nav id="$idprefix$TOC" role="doc-toc">
     96 $if(toc-title)$
     97 <h2 id="$idprefix$toc-title">$toc-title$</h2>
     98 $endif$
     99 $table-of-contents$
    100 </nav>
    101 $endif$
    102 $body$
    103 $for(include-after)$
    104 $include-after$
    105 $endfor$
    106 </body>
    107 </html>
    108 EOF
    109 
    110     pandoc \
    111         --from markdown --to html \
    112         --metadata title="$(basename --suffix=.md -- "${1}")" \
    113         --highlight-style pygments \
    114         --lua-filter "${lua_filter}" \
    115         --template "${html_template}" \
    116         "${1}" >"${raw_html}"
    117 
    118     local dst="$(cd "$(dirname -- "${1}")" >/dev/null; pwd -P)/$(basename -- "${1}")"
    119     local script="$(readlink -f -- "${BASH_SOURCE[0]}")"
    120     script_dir="$(dirname -- "${script}")" >/dev/null
    121     if [ ! -z "$(grep '<latex[a-z]*>' "${raw_html}")" ]; then
    122         render_latex "${raw_html}" "${style_sheets}"
    123     fi
    124     echo "<link rel='stylesheet' href='${script_dir}/static/css/style.css'>" >>"${style_sheets}"
    125     pandoc \
    126         --from html --to html \
    127         --standalone \
    128         --embed-resources \
    129         --template "${html_template}" \
    130         --include-in-header "${style_sheets}" \
    131         --output "${dst%.*}.html" "${raw_html}"
    132 
    133     rm -f "${raw_html}" "${style_sheets}" "${html_template}" "${lua_filter}"
    134 }
    135 
    136 render_latex() {
    137     pushd "${script_dir}" >/dev/null
    138     if [ -z "$(npm ls --parseable katex)" ]; then
    139         npm install katex linkedom
    140     fi
    141 
    142     cat <<EOF | node -
    143 const katex = require('katex');
    144 const {parseHTML} = require('linkedom');
    145 const fs = require('node:fs');
    146 fs.readFile('${1}', 'utf8', (err, content) => {
    147     const {document} = parseHTML(content.toString());
    148     const inline_items = document.querySelectorAll('latexinline');
    149     const display_items = document.querySelectorAll('latexdisplay');
    150     inline_items.forEach((item) => {
    151         const katex_code = katex.renderToString(item.innerHTML, {
    152             output: 'html',
    153             displayMode: false,
    154         });
    155         item.outerHTML = katex_code;
    156     });
    157     display_items.forEach((item) => {
    158         const katex_code = katex.renderToString(item.innerHTML, {
    159             output: 'html',
    160             displayMode: true,
    161         });
    162         item.outerHTML = katex_code;
    163     });
    164     fs.writeFile('${1}', document.toString(), err => {});
    165 });
    166 EOF
    167 
    168     local katex_version="$(npm ls --parseable --long katex)"
    169     katex_version="${katex_version##*@}"
    170     local katex_css="${script_dir}/static/css/katex/${katex_version}"
    171     static_katex "${katex_version}" "${katex_css}"
    172     echo "<link rel='stylesheet' href='${katex_css}/math_fonts.css'>" >>"${2}"
    173     echo "<link rel='stylesheet' href='${katex_css}/katex.min.css'>" >>"${2}"
    174     popd >/dev/null
    175 }
    176 
    177 static_katex() {
    178     local version="${1}"
    179     local dst="${2}"
    180     if [ -d "${dst}" ]; then
    181         return
    182     fi
    183     local src="https://cdn.jsdelivr.net/npm/katex@${version}/dist"
    184     mkdir -p "${dst}"
    185     curl -s "${src}/katex.min.css" | sed 's/@font-face{[^}]*}//g' >"${dst}/katex.min.css"
    186 
    187     font_type='woff'
    188     fonts=(
    189         "@font-face{font-family:KaTeX_AMS;font-style:normal;font-weight:400;src:url(fonts/KaTeX_AMS-Regular.${font_type}) format('${font_type}')}"
    190         "@font-face{font-family:KaTeX_Caligraphic;font-style:normal;font-weight:700;src:url(fonts/KaTeX_Caligraphic-Bold.${font_type}) format('${font_type}')}"
    191         "@font-face{font-family:KaTeX_Caligraphic;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Caligraphic-Regular.${font_type}) format('${font_type}')}"
    192         "@font-face{font-family:KaTeX_Fraktur;font-style:normal;font-weight:700;src:url(fonts/KaTeX_Fraktur-Bold.${font_type}) format('${font_type}')}"
    193         "@font-face{font-family:KaTeX_Fraktur;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Fraktur-Regular.${font_type}) format('${font_type}')}"
    194         "@font-face{font-family:KaTeX_Main;font-style:normal;font-weight:700;src:url(fonts/KaTeX_Main-Bold.${font_type}) format('${font_type}')}"
    195         "@font-face{font-family:KaTeX_Main;font-style:italic;font-weight:700;src:url(fonts/KaTeX_Main-BoldItalic.${font_type}) format('${font_type}')}"
    196         "@font-face{font-family:KaTeX_Main;font-style:italic;font-weight:400;src:url(fonts/KaTeX_Main-Italic.${font_type}) format('${font_type}')}"
    197         "@font-face{font-family:KaTeX_Main;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Main-Regular.${font_type}) format('${font_type}')}"
    198         "@font-face{font-family:KaTeX_Math;font-style:italic;font-weight:700;src:url(fonts/KaTeX_Math-BoldItalic.${font_type}) format('${font_type}')}"
    199         "@font-face{font-family:KaTeX_Math;font-style:italic;font-weight:400;src:url(fonts/KaTeX_Math-Italic.${font_type}) format('${font_type}')}"
    200         "@font-face{font-family:KaTeX_SansSerif;font-style:normal;font-weight:700;src:url(fonts/KaTeX_SansSerif-Bold.${font_type}) format('${font_type}')}"
    201         "@font-face{font-family:KaTeX_SansSerif;font-style:italic;font-weight:400;src:url(fonts/KaTeX_SansSerif-Italic.${font_type}) format('${font_type}')}"
    202         "@font-face{font-family:KaTeX_SansSerif;font-style:normal;font-weight:400;src:url(fonts/KaTeX_SansSerif-Regular.${font_type}) format('${font_type}')}"
    203         "@font-face{font-family:KaTeX_Script;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Script-Regular.${font_type}) format('${font_type}')}"
    204         "@font-face{font-family:KaTeX_Size1;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Size1-Regular.${font_type}) format('${font_type}')}"
    205         "@font-face{font-family:KaTeX_Size2;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Size2-Regular.${font_type}) format('${font_type}')}"
    206         "@font-face{font-family:KaTeX_Size3;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Size3-Regular.${font_type}) format('${font_type}')}"
    207         "@font-face{font-family:KaTeX_Size4;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Size4-Regular.${font_type}) format('${font_type}')}"
    208         "@font-face{font-family:KaTeX_Typewriter;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Typewriter-Regular.${font_type}) format('${font_type}')}"
    209     )
    210     rm -f "${dst}/math_fonts.css"
    211     for font in "${fonts[@]}"; do
    212         file=$(echo "${font}" | grep -o "fonts/.*\.${font_type}")
    213         echo ${font/${file}/"\"data:font/${font_type};charset=utf-8;base64,$(curl -s "${src}/${file}" | base64 -w 0 -)\""} >>"${dst}/math_fonts.css"
    214     done
    215 }
    216 
    217 main "$@"