From 268ee14434da95634735ed5eb3537440c1cc2f82 Mon Sep 17 00:00:00 2001 From: Matt Cuffaro Date: Sat, 14 Dec 2024 07:16:23 -0500 Subject: [PATCH] first pass at adding lua+tikzcd support --- .stylua.toml | 7 +++ _preamble.qmd | 74 ++++++++++++++++++++++++++++++ _quarto.yml | 3 ++ filters.lua | 104 +++++++++++++++++++++++++++++++++++++++++++ templates/tikz.tex | 8 ++++ templates/tikzcd.tex | 48 ++++++++++++++++++++ templates/tikzit.tex | 84 ++++++++++++++++++++++++++++++++++ 7 files changed, 328 insertions(+) create mode 100644 .stylua.toml create mode 100644 _preamble.qmd create mode 100644 filters.lua create mode 100644 templates/tikz.tex create mode 100644 templates/tikzcd.tex create mode 100644 templates/tikzit.tex diff --git a/.stylua.toml b/.stylua.toml new file mode 100644 index 0000000..bfcffff --- /dev/null +++ b/.stylua.toml @@ -0,0 +1,7 @@ +column_width = 120 +line_endings = "Unix" +indent_type = "Spaces" +indent_width = 2 +quote_style = "AutoPreferDouble" +call_parentheses = "None" +collapse_simple_statement = "Always" diff --git a/_preamble.qmd b/_preamble.qmd new file mode 100644 index 0000000..44dff69 --- /dev/null +++ b/_preamble.qmd @@ -0,0 +1,74 @@ +:::{.hidden} +$$ + +\newcommand{\N}{\mathbb{N}} +\newcommand{\Z}{\mathbb{Z}} +\newcommand{\R}{\mathbb{R}} +\newcommand{\C}{\mathbb{C}} + + +\newcommand{\then}{\operatorname{⨟}} +\newcommand{\cat}[1]{\mathsf{#1}} +\newcommand{\CAT}[1]{\mathsf{#1}} +\newcommand{\Sch}[1]{\mathsf{Sch}(#1)} +\newcommand{\op}{\mathrm{op}} +\newcommand{\Kl}{\operatorname{Kl}} +\newcommand{\Set}{\CAT{Set}} +\newcommand{\FinSet}{\CAT{FinSet}} +\newcommand{\catSet}[1]{\cat{#1}\text{-}\Set} +\newcommand{\Hom}{\operatorname{Hom}} +\newcommand{\dom}{\operatorname{dom}} +\newcommand{\codom}{\operatorname{codom}} +\newcommand{\id}{\operatorname{id}} + +\newcommand{\Sub}{\operatorname{Sub}} +\newcommand{\non}{\mathord{\sim}} + + +\newcommand{\src}{\operatorname{src}} +\newcommand{\tgt}{\operatorname{tgt}} +\newcommand{\inv}{\operatorname{inv}} +\newcommand{\refl}{\operatorname{refl}} +\newcommand{\vert}{\operatorname{vert}} + +\newcommand{\Graph}{\CAT{Graph}} +\newcommand{\SGraph}{\CAT{SGraph}} +\newcommand{\RGraph}{\CAT{RGraph}} +\newcommand{\HGraph}{\CAT{HGraph}} +\newcommand{\RotSys}{\CAT{RotSys}} + + +\newcommand{\dbl}[1]{\mathbb{#1}} +\newcommand{\Prof}{\mathbb{P}\mathsf{rof}} +\newcommand{\pto}{⇸} + + +\newcommand{\Attr}{\operatorname{Attr}} +\newcommand{\Arr}{\operatorname{Arr}} +\newcommand{\edec}{\operatorname{edec}} +\newcommand{\vdec}{\operatorname{vdec}} + + +\newcommand{\PortGraph}{\cat{PortGraph}} +\newcommand{\CPG}{\cat{CPG}} +\newcommand{\SIPortGraph}{\cat{SIPortGraph}} +\newcommand{\OpenCPG}{\cat{OpenCPG}} +\newcommand{\DWD}{\cat{DWD}} +\newcommand{\UWD}{\cat{UWD}} +\newcommand{\WD}{\cat{WD}} +\newcommand{\UPortGraph}{\cat{UPortGraph}} +\newcommand{\Pin}{P_{\textrm{in}}} +\newcommand{\Pout}{P_{\textrm{out}}} +\newcommand{\Box}{\textrm{Box}} +\newcommand{\inbox}{\operatorname{box_{in}}} +\newcommand{\outbox}{\operatorname{box_{out}}} +\newcommand{\wire}{\operatorname{wire}} + +\newcommand{\E}{\mathrm{Expr}} +\newcommand{\catC}{\cat{C}} +\newcommand{\catD}{\cat{D}} + + +\newcommand{\Arena}[2]{\begin{pmatrix}#2 \\ #1\end{pmatrix}} +$$ +::: diff --git a/_quarto.yml b/_quarto.yml index f54597e..089bb79 100644 --- a/_quarto.yml +++ b/_quarto.yml @@ -25,3 +25,6 @@ format: monofont: JuliaMono-Regular from: markdown+tex_math_single_backslash + +filters: + - filters.lua diff --git a/filters.lua b/filters.lua new file mode 100644 index 0000000..8ef8618 --- /dev/null +++ b/filters.lua @@ -0,0 +1,104 @@ +local system = require "pandoc.system" + +local rootdir = os.getenv "QUARTO_PROJECT_DIR" +local cachedir = rootdir .. "/.svg-cache" + +local thisfile = io.open(rootdir .. "/filters.lua") +local thiscontent = thisfile:read "*all" +thisfile:close() +os.execute("mkdir -p " .. cachedir) + +function make_templates(template_paths) + local templates = {} + + for name, path in pairs(template_paths) do + local fullpath = rootdir .. "/" .. path + local file = io.open(fullpath, "r") + local content = file:read "*all" + file:close() + + local before_marker, after_marker = content:match "^(.*)@CONTENT(.*)$" + if before_marker and after_marker then templates[name] = { before_marker, after_marker } end + end + + return templates +end + +local tikz_template_paths = { + tikzcd = "templates/tikzcd.tex", + tikz = "templates/tikz.tex", + tikzit = "templates/tikzit.tex", +} + +local tikz_templates = make_templates(tikz_template_paths) + +function trim(s) return (s:gsub("^%s*(.-)%s*$", "%1")) end + +local function tikz2image(template) + return function(src, outfile) + system.with_temporary_directory("tikz2image", function(tmpdir) + system.with_working_directory(tmpdir, function() + local f = io.open("tikz.tex", "w") + f:write(template[1] .. trim(src) .. template[2]) + f:close() + print() + print "processing:" + print(src) + local texres = os.execute "lualatex -halt-on-error --output-format=dvi tikz.tex > texlog" + if not texres then + print "latex errored: log is" + os.execute "cat texlog" + else + os.execute "dvisvgm -TS1.7 -b papersize --font-format=woff2,autohint tikz.dvi > /dev/null" + print("output to: " .. outfile) + os.execute("mv tikz.svg " .. outfile) + end + end) + end) + end +end + +local function graphviz2image(src, outfile) + system.with_temporary_directory("graphviz2image", function(tmpdir) + system.with_working_directory(tmpdir, function() + local f = io.open("graphviz.dot", "w") + f:write(trim(src)) + f:close() + os.execute "dot -Tsvg graphviz.dot -o graphviz.svg" + os.execute("mv graphviz.svg " .. outfile) + end) + end) +end + +local function file_exists(name) + local f = io.open(name, "r") + if f ~= nil then + io.close(f) + return true + else + return false + end +end + +local function memoize_svg(input, builder, key) + local svgdir = system.get_working_directory() .. "/_svgs" + os.execute("mkdir -p " .. svgdir) + local fbasename = pandoc.sha1(input .. key .. thiscontent) .. ".svg" + local fname = svgdir .. "/" .. fbasename + if not file_exists(fname) then builder(input, fname) end + return pandoc.Image({}, "_svgs/" .. fbasename) +end + +function CodeBlock(el) + local tikz_template = tikz_templates[el.classes[1]] + if tikz_template ~= nil then + return pandoc.Div( + memoize_svg(el.text, tikz2image(tikz_template), tikz_template[1] .. tikz_template[2]), + { class = "tikzcd" } + ) + elseif el.classes[1] == "graphviz" then + return pandoc.Div(memoize_svg(el.text, graphviz2image, "graphviz"), { class = "graphvizz" }) + else + return el + end +end diff --git a/templates/tikz.tex b/templates/tikz.tex new file mode 100644 index 0000000..77d184e --- /dev/null +++ b/templates/tikz.tex @@ -0,0 +1,8 @@ +\documentclass[tikz]{standalone} +\usetikzlibrary{graphs, graphdrawing} +\usegdlibrary{trees} +\begin{document} +\begin{tikzpicture} + @CONTENT +\end{tikzpicture} +\end{document} diff --git a/templates/tikzcd.tex b/templates/tikzcd.tex new file mode 100644 index 0000000..d2c8bf7 --- /dev/null +++ b/templates/tikzcd.tex @@ -0,0 +1,48 @@ +\documentclass[tikz]{standalone} +\usepackage{amsmath,amssymb} +\usetikzlibrary{decorations.markings} +\usetikzlibrary{cd} +\tikzset{mid vert/.style={/utils/exec=\tikzset{every node/.append style={outer sep=0.8ex}}, + postaction=decorate,decoration={markings, + mark=at position 0.5 with {\draw[-] (0,#1) -- (0,-#1);}}}, + mid vert/.default=0.75ex} + +% =============================================================================================== +% *** quiver *** +% A package for drawing commutative diagrams exported from https://q.uiver.app. +% +% This package is currently a wrapper around the `tikz-cd` package, importing necessary TikZ +% libraries, and defining a new TikZ style for curves of a fixed height. +% +% Version: 1.4.2 +% Authors: +% - varkor (https://github.com/varkor) +% - AndréC (https://tex.stackexchange.com/users/138900/andr%C3%A9c) + +% `calc` is necessary to draw curved arrows. +\usetikzlibrary{calc} +% `pathmorphing` is necessary to draw squiggly arrows. +\usetikzlibrary{decorations.pathmorphing} + +% A TikZ style for curved arrows of a fixed height, due to AndréC. +\tikzset{curve/.style={settings={#1},to path={(\tikztostart) + .. controls ($(\tikztostart)!\pv{pos}!(\tikztotarget)!\pv{height}!270:(\tikztotarget)$) + and ($(\tikztostart)!1-\pv{pos}!(\tikztotarget)!\pv{height}!270:(\tikztotarget)$) + .. (\tikztotarget)\tikztonodes}}, + settings/.code={\tikzset{quiver/.cd,#1} + \def\pv##1{\pgfkeysvalueof{/tikz/quiver/##1}}}, + quiver/.cd,pos/.initial=0.35,height/.initial=0} + +% TikZ arrowhead/tail styles. +\tikzset{tail reversed/.code={\pgfsetarrowsstart{tikzcd to}}} +\tikzset{2tail/.code={\pgfsetarrowsstart{Implies[reversed]}}} +\tikzset{2tail reversed/.code={\pgfsetarrowsstart{Implies}}} +% TikZ arrow styles. +\tikzset{no body/.style={/tikz/dash pattern=on 0 off 1mm}} +% =============================================================================================== + +\begin{document} +\begin{tikzcd} + @CONTENT +\end{tikzcd} +\end{document} diff --git a/templates/tikzit.tex b/templates/tikzit.tex new file mode 100644 index 0000000..e9833e8 --- /dev/null +++ b/templates/tikzit.tex @@ -0,0 +1,84 @@ +\documentclass[tikz]{standalone} +\usepackage{amsmath} +% Official TikZiT style file: https://tikzit.github.io/tikzit.sty + +\usetikzlibrary{backgrounds} +\usetikzlibrary{arrows} +\usetikzlibrary{shapes,shapes.geometric,shapes.misc} + +% this style is applied by default to any tikzpicture included via \tikzfig +\tikzstyle{tikzfig}=[baseline=-0.25em,scale=0.5] + +% these are dummy properties used by TikZiT, but ignored by LaTex +\pgfkeys{/tikz/tikzit fill/.initial=0} +\pgfkeys{/tikz/tikzit draw/.initial=0} +\pgfkeys{/tikz/tikzit shape/.initial=0} +\pgfkeys{/tikz/tikzit category/.initial=0} + +% standard layers used in .tikz files +\pgfdeclarelayer{edgelayer} +\pgfdeclarelayer{nodelayer} +\pgfsetlayers{background,edgelayer,nodelayer,main} + +% style for blank nodes +\tikzstyle{none}=[inner sep=0mm] + +% include a .tikz file +\newcommand{\tikzfig}[1]{% +{\tikzstyle{every picture}=[tikzfig] +\IfFileExists{#1.tikz} + {\input{#1.tikz}} + {% + \IfFileExists{./figures/#1.tikz} + {\input{./figures/#1.tikz}} + {\tikz[baseline=-0.5em]{\node[draw=red,font=\color{red},fill=red!10!white] {\textit{#1}};}}% + }}% +} + +% the same as \tikzfig, but in a {center} environment +\newcommand{\ctikzfig}[1]{% +\begin{center}\rm + \tikzfig{#1} +\end{center}} + +% fix strange self-loops, which are PGF/TikZ default +\tikzstyle{every loop}=[] + +% TiKZ style file generated by TikZiT. You may edit this file manually, +% but some things (e.g. comments) may be overwritten. To be readable in +% TikZiT, the only non-comment lines must be of the form: +% \tikzstyle{NAME}=[PROPERTY LIST] + +% Node styles +\tikzstyle{variable_node}=[fill={rgb,255: red,108; green,154; blue,195}, draw=black, shape=circle, text=white] +\tikzstyle{block}=[fill={rgb,255: red,226; green,143; blue,65}, draw=black, shape=rectangle, text=white] +\tikzstyle{Ob}=[fill=none, draw=none, shape=rectangle] +\tikzstyle{table}=[fill={rgb,255: red,224; green,229; blue,205}, draw={rgb,255: red,95; green,96; blue,98}, shape=rectangle, rounded corners] +\tikzstyle{title}=[fill=white, draw={rgb,255: red,128; green,190; blue,99}, shape=rectangle, text=black, line width=1mm, minimum width=8cm, minimum height=1cm] +\tikzstyle{blockprime}=[fill={rgb,255: red,108; green,154; blue,195}, draw=black, shape=rectangle, text=white] +\tikzstyle{Hom3by3}=[fill=white, draw=black, shape=rectangle, minimum height=20mm] +\tikzstyle{finsetdot}=[fill=black, draw=black, shape=circle] +\tikzstyle{Hom1x3}=[fill=white, draw=black, shape=rectangle] +\tikzstyle{new edge style 1}=[line width=0.6mm] +\tikzstyle{Hom1x3_rounded}=[fill=white, draw=black, shape=rectangle, rounded corners] +\tikzstyle{outerbox6x17}=[fill=none, draw=black, shape=rectangle, minimum height=6cm, minimum width=17cm, rounded corners] +\tikzstyle{outerbox}=[fill=none, draw=black, shape=rectangle, rounded corners, minimum height=4cm, minimum width=6cm] +\tikzstyle{add}=[fill=white, draw=black, shape=circle] +\tikzstyle{rel_dagger}=[fill=white, draw=black, shape=trapezium, rounded corners, shape border rotate=90, minimum width=1.5cm] +\tikzstyle{rel}=[fill=white, draw=black, shape=trapezium, rounded corners, shape border rotate=270, minimum width=1.5cm] +\tikzstyle{mhom3x1}=[fill=white, draw=black, shape=rectangle, rounded corners, minimum height=10mm] + +% Edge styles +\tikzstyle{new edge style 0}=[->, draw={rgb,255: red,127; green,129; blue,131}, line width=0.6mm] +\tikzstyle{arrow}=[draw={rgb,255: red,177; green,179; blue,182}, ->, line width=0.4mm] +\tikzstyle{undirected}=[-, draw={rgb,255: red,177; green,179; blue,182}, line width=0.6mm] +\tikzstyle{cospan_edge}=[->, draw=black, dashed, line width=0.4mm] +\tikzstyle{undirected edge}=[-, line width=0.6mm, draw={rgb,255: red,177; green,179; blue,182}] +\tikzstyle{blueline}=[-, draw={rgb,255: red,108; green,154; blue,195}, line width=0.7mm] +\tikzstyle{functor}=[->, draw=black, line width=1.2mm] + +\begin{document} +\begin{tikzpicture} + @CONTENT +\end{tikzpicture} +\end{document}