commit 90175299d4181db91d5fdd56f5873cc0cd3fca63
Author: Fabian Wermelinger <info@0xfab.ch>
Date: Sat, 1 Feb 2025 17:04:39 +0100
Add markdown note rendering script
Diffstat:
4 files changed, 337 insertions(+), 0 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -0,0 +1,6 @@
+/node_modules
+package.json
+package-lock.json
+*.md
+*.pdf
+*.html
diff --git a/rendernote b/rendernote
@@ -0,0 +1,217 @@
+#!/usr/bin/env bash
+
+main() {
+ if [ $# -eq 0 ]; then
+ cat <<EOF
+USAGE $0 [-pdf|-html] <markdown note> [<markdown note> ...]
+EOF
+ exit 1
+ fi
+ local convert=render_html
+ case "$1" in
+ -pdf) shift; convert=render_pdf;;
+ -html) shift; convert=render_html;;
+ esac
+ for note; do
+ ${convert} "${note}"
+ done
+}
+
+render_pdf() {
+ local input="$(mktemp)"
+ if [ -z "$(awk '/^---$/,/^---$/' "${1}")" ]; then
+ cat <<'EOF' >"${input}"
+---
+fontsize: 12pt
+papersize: a4
+linkcolor: blue
+header-includes:
+ - \usepackage[top=60pt,bottom=60pt,left=80pt,right=80pt]{geometry}
+ - \usepackage{bm}
+ - \usepackage{sectsty}
+include-before:
+ - \allsectionsfont{\sffamily}
+---
+EOF
+ fi
+ cat ${1} >>"${input}"
+ pandoc \
+ --from markdown --to pdf \
+ --highlight-style pygments \
+ --output "${1%.*}.pdf" "${input}"
+ rm -f "${input}"
+}
+
+render_html() {
+ local lua_filter="$(mktemp)"
+ local html_template="$(mktemp)"
+ local style_sheets="$(mktemp)"
+ local raw_html="$(mktemp)"
+
+ cat <<'EOF' >"${lua_filter}"
+function Math(elem)
+ assert(FORMAT:match('html'))
+ local wrap
+ if elem.mathtype == 'InlineMath' then
+ wrap = '<latexinline>' .. elem.text .. '</latexinline>'
+ else
+ wrap = '<latexdisplay>' .. elem.text .. '</latexdisplay>'
+ end
+ return pandoc.RawInline('html', wrap)
+end
+EOF
+ cat <<'EOF' >"${html_template}"
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml" lang="$lang$" xml:lang="$lang$"$if(dir)$ dir="$dir$"$endif$>
+<head>
+ <meta charset="utf-8" />
+ <meta name="generator" content="pandoc" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
+$for(author-meta)$
+ <meta name="author" content="$author-meta$" />
+$endfor$
+$if(date-meta)$
+ <meta name="dcterms.date" content="$date-meta$" />
+$endif$
+$if(keywords)$
+ <meta name="keywords" content="$for(keywords)$$keywords$$sep$, $endfor$" />
+$endif$
+$if(description-meta)$
+ <meta name="description" content="$description-meta$" />
+$endif$
+ <title>$if(title-prefix)$$title-prefix$ – $endif$$pagetitle$</title>
+ <style>
+ $styles.html()$
+ </style>
+$for(header-includes)$
+ $header-includes$
+$endfor$
+</head>
+<body>
+$for(include-before)$
+$include-before$
+$endfor$
+$if(toc)$
+<nav id="$idprefix$TOC" role="doc-toc">
+$if(toc-title)$
+<h2 id="$idprefix$toc-title">$toc-title$</h2>
+$endif$
+$table-of-contents$
+</nav>
+$endif$
+$body$
+$for(include-after)$
+$include-after$
+$endfor$
+</body>
+</html>
+EOF
+
+ pandoc \
+ --from markdown --to html \
+ --metadata title="$(basename --suffix=.md -- "${1}")" \
+ --highlight-style pygments \
+ --lua-filter "${lua_filter}" \
+ --template "${html_template}" \
+ "${1}" >"${raw_html}"
+
+ local dst="$(cd "$(dirname -- "${1}")" >/dev/null; pwd -P)/$(basename -- "${1}")"
+ local script="$(readlink -f -- "${BASH_SOURCE[0]}")"
+ script_dir="$(dirname -- "${script}")" >/dev/null
+ if [ ! -z "$(grep '<latex[a-z]*>' "${raw_html}")" ]; then
+ render_latex "${raw_html}" "${style_sheets}"
+ fi
+ echo "<link rel='stylesheet' href='${script_dir}/static/css/style.css'>" >>"${style_sheets}"
+ pandoc \
+ --from html --to html \
+ --standalone \
+ --embed-resources \
+ --template "${html_template}" \
+ --include-in-header "${style_sheets}" \
+ --output "${dst%.*}.html" "${raw_html}"
+
+ rm -f "${raw_html}" "${style_sheets}" "${html_template}" "${lua_filter}"
+}
+
+render_latex() {
+ pushd "${script_dir}" >/dev/null
+ if [ -z "$(npm ls --parseable katex)" ]; then
+ npm install katex linkedom
+ fi
+
+ cat <<EOF | node -
+const katex = require('katex');
+const {parseHTML} = require('linkedom');
+const fs = require('node:fs');
+fs.readFile('${1}', 'utf8', (err, content) => {
+ const {document} = parseHTML(content.toString());
+ const inline_items = document.querySelectorAll('latexinline');
+ const display_items = document.querySelectorAll('latexdisplay');
+ inline_items.forEach((item) => {
+ const katex_code = katex.renderToString(item.innerHTML, {
+ output: 'html',
+ displayMode: false,
+ });
+ item.outerHTML = katex_code;
+ });
+ display_items.forEach((item) => {
+ const katex_code = katex.renderToString(item.innerHTML, {
+ output: 'html',
+ displayMode: true,
+ });
+ item.outerHTML = katex_code;
+ });
+ fs.writeFile('${1}', document.toString(), err => {});
+});
+EOF
+
+ local katex_version="$(npm ls --parseable --long katex)"
+ katex_version="${katex_version##*@}"
+ local katex_css="${script_dir}/static/css/katex/${katex_version}"
+ static_katex "${katex_version}" "${katex_css}"
+ echo "<link rel='stylesheet' href='${katex_css}/math_fonts.css'>" >>"${2}"
+ echo "<link rel='stylesheet' href='${katex_css}/katex.min.css'>" >>"${2}"
+ popd >/dev/null
+}
+
+static_katex() {
+ local version="${1}"
+ local dst="${2}"
+ if [ -d "${dst}" ]; then
+ return
+ fi
+ local src="https://cdn.jsdelivr.net/npm/katex@${version}/dist"
+ mkdir -p "${dst}"
+ curl -s "${src}/katex.min.css" | sed 's/@font-face{[^}]*}//g' >"${dst}/katex.min.css"
+
+ font_type='woff'
+ fonts=(
+ "@font-face{font-family:KaTeX_AMS;font-style:normal;font-weight:400;src:url(fonts/KaTeX_AMS-Regular.${font_type}) format('${font_type}')}"
+ "@font-face{font-family:KaTeX_Caligraphic;font-style:normal;font-weight:700;src:url(fonts/KaTeX_Caligraphic-Bold.${font_type}) format('${font_type}')}"
+ "@font-face{font-family:KaTeX_Caligraphic;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Caligraphic-Regular.${font_type}) format('${font_type}')}"
+ "@font-face{font-family:KaTeX_Fraktur;font-style:normal;font-weight:700;src:url(fonts/KaTeX_Fraktur-Bold.${font_type}) format('${font_type}')}"
+ "@font-face{font-family:KaTeX_Fraktur;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Fraktur-Regular.${font_type}) format('${font_type}')}"
+ "@font-face{font-family:KaTeX_Main;font-style:normal;font-weight:700;src:url(fonts/KaTeX_Main-Bold.${font_type}) format('${font_type}')}"
+ "@font-face{font-family:KaTeX_Main;font-style:italic;font-weight:700;src:url(fonts/KaTeX_Main-BoldItalic.${font_type}) format('${font_type}')}"
+ "@font-face{font-family:KaTeX_Main;font-style:italic;font-weight:400;src:url(fonts/KaTeX_Main-Italic.${font_type}) format('${font_type}')}"
+ "@font-face{font-family:KaTeX_Main;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Main-Regular.${font_type}) format('${font_type}')}"
+ "@font-face{font-family:KaTeX_Math;font-style:italic;font-weight:700;src:url(fonts/KaTeX_Math-BoldItalic.${font_type}) format('${font_type}')}"
+ "@font-face{font-family:KaTeX_Math;font-style:italic;font-weight:400;src:url(fonts/KaTeX_Math-Italic.${font_type}) format('${font_type}')}"
+ "@font-face{font-family:KaTeX_SansSerif;font-style:normal;font-weight:700;src:url(fonts/KaTeX_SansSerif-Bold.${font_type}) format('${font_type}')}"
+ "@font-face{font-family:KaTeX_SansSerif;font-style:italic;font-weight:400;src:url(fonts/KaTeX_SansSerif-Italic.${font_type}) format('${font_type}')}"
+ "@font-face{font-family:KaTeX_SansSerif;font-style:normal;font-weight:400;src:url(fonts/KaTeX_SansSerif-Regular.${font_type}) format('${font_type}')}"
+ "@font-face{font-family:KaTeX_Script;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Script-Regular.${font_type}) format('${font_type}')}"
+ "@font-face{font-family:KaTeX_Size1;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Size1-Regular.${font_type}) format('${font_type}')}"
+ "@font-face{font-family:KaTeX_Size2;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Size2-Regular.${font_type}) format('${font_type}')}"
+ "@font-face{font-family:KaTeX_Size3;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Size3-Regular.${font_type}) format('${font_type}')}"
+ "@font-face{font-family:KaTeX_Size4;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Size4-Regular.${font_type}) format('${font_type}')}"
+ "@font-face{font-family:KaTeX_Typewriter;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Typewriter-Regular.${font_type}) format('${font_type}')}"
+ )
+ rm -f "${dst}/math_fonts.css"
+ for font in "${fonts[@]}"; do
+ file=$(echo "${font}" | grep -o "fonts/.*\.${font_type}")
+ echo ${font/${file}/"\"data:font/${font_type};charset=utf-8;base64,$(curl -s "${src}/${file}" | base64 -w 0 -)\""} >>"${dst}/math_fonts.css"
+ done
+}
+
+main "$@"
diff --git a/static/css/.gitignore b/static/css/.gitignore
@@ -0,0 +1 @@
+/katex
diff --git a/static/css/style.css b/static/css/style.css
@@ -0,0 +1,113 @@
+body {
+ font-family: 'TeXGyrePagella', serif;
+ font-size: 116%;
+ text-align: justify;
+ max-width: 700px;
+}
+
+h1 {
+ font-family: 'Lato', sans-serif;
+ font-size: 220%;
+ margin-top: 32px;
+ margin-bottom: 24px;
+ text-align: center;
+}
+
+h2, h3 {
+ font-family: 'Lato', sans-serif;
+ font-weight: bold;
+ font-style: italic;
+ font-size: 130%;
+ margin-top: 24px;
+ margin-bottom: 8px;
+}
+
+h3 {
+ font-style: normal;
+ font-size: 115%;
+ margin-top: 8px;
+ margin-bottom: 4px;
+}
+
+h4, h5, h6 {
+ font-family: 'Lato', sans-serif;
+ font-weight: normal;
+ font-style: normal;
+ font-size: 115%;
+ margin-bottom: 4px;
+}
+
+h5 {
+ font-style: italic;
+ font-size: 110%;
+}
+
+h6 {
+ font-style: italic;
+ font-size: 105%;
+}
+
+blockquote {
+ background-color: rgba(27,31,35,.1) !important;
+ padding: 8px 16px;
+ border-radius: 4px;
+}
+
+blockquote p {
+ display: inline;
+}
+
+code {
+ font-family: 'Inconsolata', monospace;
+ color: inherit;
+ background-color: rgba(27,31,35,.1);
+ border-radius: 4px;
+ padding: 2px 4px;
+}
+
+pre {
+ background-color: rgba(27,31,35,.1) !important;
+ padding: 8px;
+ border-radius: 4px;
+}
+
+pre code {
+ padding: 0;
+}
+
+table {
+ width: 100%;
+ margin-top: 1rem;
+ margin-bottom: 1rem;
+ margin-left: auto;
+ margin-right: auto;
+ display: table;
+}
+
+table tr th:empty {
+ display: none;
+}
+
+img, svg {
+ max-width: 100%;
+ display: block;
+ margin-left: auto;
+ margin-right: auto;
+ width: 100%;
+ height: auto;
+ margin-top: 1rem;
+ margin-bottom: 1rem;
+ border-radius: 4px;
+}
+
+.katex {
+ font-size: 1.1em;
+}
+
+div.sourceCode {
+ margin: 0;
+}
+
+.sourceCode {
+ overflow: auto;
+}