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 "$@"