Add wiki pandoc files (Makefile+filters)
parent
ea671c0be9
commit
9e8d5422e7
|
@ -0,0 +1,38 @@
|
||||||
|
entries := $(wildcard *.md)
|
||||||
|
|
||||||
|
target=wiki
|
||||||
|
exports= \
|
||||||
|
$(entries:.md=.html) \
|
||||||
|
# $(target).pdf \
|
||||||
|
|
||||||
|
VIMWIKI_DIR=/home/h/.wiki
|
||||||
|
|
||||||
|
all: $(exports)
|
||||||
|
|
||||||
|
%.html:%.md
|
||||||
|
pandoc \
|
||||||
|
-f markdown \
|
||||||
|
-t html5 \
|
||||||
|
-c pandoc.css \
|
||||||
|
--pdf-engine=xelatex \
|
||||||
|
--bibliography $(VIMWIKI_DIR)/references.bib \
|
||||||
|
--citeproc \
|
||||||
|
--lua-filter=$(VIMWIKI_DIR)/filters/html-links.lua \
|
||||||
|
--lua-filter=$(VIMWIKI_DIR)/filters/diagram-generator.lua \
|
||||||
|
--lua-filter=$(VIMWIKI_DIR)/filters/lilypond.lua \
|
||||||
|
--mathjax \
|
||||||
|
--extract-media=diagrams \
|
||||||
|
-s -o $@ $<
|
||||||
|
|
||||||
|
$(target).pdf:$(entries)
|
||||||
|
pandoc \
|
||||||
|
-f markdown $(entries) \
|
||||||
|
--pdf-engine=xelatex \
|
||||||
|
--bibliography $(VIMWIKI_DIR)/references.bib \
|
||||||
|
--citeproc \
|
||||||
|
--table-of-contents \
|
||||||
|
-o $@
|
||||||
|
|
||||||
|
# clean:
|
||||||
|
# rm -f *.html
|
||||||
|
# rm wiki.pdf
|
|
@ -0,0 +1,396 @@
|
||||||
|
--[[
|
||||||
|
diagram-generator – create images and figures from code blocks.
|
||||||
|
|
||||||
|
This Lua filter is used to create images with or without captions
|
||||||
|
from code blocks. Currently PlantUML, GraphViz, Tikz, and Python
|
||||||
|
can be processed. For further details, see README.md.
|
||||||
|
|
||||||
|
Copyright: © 2018-2020 John MacFarlane <jgm@berkeley.edu>,
|
||||||
|
2018 Florian Schätzig <florian@schaetzig.de>,
|
||||||
|
2019 Thorsten Sommer <contact@sommer-engineering.com>,
|
||||||
|
2019-2020 Albert Krewinkel <albert+pandoc@zeitkraut.de>
|
||||||
|
License: MIT – see LICENSE file for details
|
||||||
|
]]
|
||||||
|
-- Module pandoc.system is required and was added in version 2.7.3
|
||||||
|
PANDOC_VERSION:must_be_at_least '2.7.3'
|
||||||
|
|
||||||
|
local system = require 'pandoc.system'
|
||||||
|
local utils = require 'pandoc.utils'
|
||||||
|
local stringify = utils.stringify
|
||||||
|
local with_temporary_directory = system.with_temporary_directory
|
||||||
|
local with_working_directory = system.with_working_directory
|
||||||
|
|
||||||
|
-- The PlantUML path. If set, uses the environment variable PLANTUML or the
|
||||||
|
-- value "plantuml.jar" (local PlantUML version). In order to define a
|
||||||
|
-- PlantUML version per pandoc document, use the meta data to define the key
|
||||||
|
-- "plantuml_path".
|
||||||
|
local plantuml_path = os.getenv("PLANTUML") or "plantuml.jar"
|
||||||
|
|
||||||
|
-- The Inkscape path. In order to define an Inkscape version per pandoc
|
||||||
|
-- document, use the meta data to define the key "inkscape_path".
|
||||||
|
local inkscape_path = os.getenv("INKSCAPE") or "inkscape"
|
||||||
|
|
||||||
|
-- The Python path. In order to define a Python version per pandoc document,
|
||||||
|
-- use the meta data to define the key "python_path".
|
||||||
|
local python_path = os.getenv("PYTHON") or "python"
|
||||||
|
|
||||||
|
-- The Python environment's activate script. Can be set on a per document
|
||||||
|
-- basis by using the meta data key "activatePythonPath".
|
||||||
|
local python_activate_path = os.getenv("PYTHON_ACTIVATE")
|
||||||
|
|
||||||
|
-- The Java path. In order to define a Java version per pandoc document,
|
||||||
|
-- use the meta data to define the key "java_path".
|
||||||
|
local java_path = os.getenv("JAVA_HOME")
|
||||||
|
if java_path then
|
||||||
|
java_path = java_path .. package.config:sub(1,1) .. "bin"
|
||||||
|
.. package.config:sub(1,1) .. "java"
|
||||||
|
else
|
||||||
|
java_path = "java"
|
||||||
|
end
|
||||||
|
|
||||||
|
-- The dot (Graphviz) path. In order to define a dot version per pandoc
|
||||||
|
-- document, use the meta data to define the key "dot_path".
|
||||||
|
local dot_path = os.getenv("DOT") or "dot"
|
||||||
|
|
||||||
|
-- The pdflatex path. In order to define a pdflatex version per pandoc
|
||||||
|
-- document, use the meta data to define the key "pdflatex_path".
|
||||||
|
local pdflatex_path = os.getenv("PDFLATEX") or "pdflatex"
|
||||||
|
|
||||||
|
-- The asymptote path. There is also the metadata variable
|
||||||
|
-- "asymptote_path".
|
||||||
|
local asymptote_path = os.getenv ("ASYMPTOTE") or "asy"
|
||||||
|
|
||||||
|
-- The default format is SVG i.e. vector graphics:
|
||||||
|
local filetype = "svg"
|
||||||
|
local mimetype = "image/svg+xml"
|
||||||
|
|
||||||
|
-- Check for output formats that potentially cannot use SVG
|
||||||
|
-- vector graphics. In these cases, we use a different format
|
||||||
|
-- such as PNG:
|
||||||
|
if FORMAT == "docx" then
|
||||||
|
filetype = "png"
|
||||||
|
mimetype = "image/png"
|
||||||
|
elseif FORMAT == "pptx" then
|
||||||
|
filetype = "png"
|
||||||
|
mimetype = "image/png"
|
||||||
|
elseif FORMAT == "rtf" then
|
||||||
|
filetype = "png"
|
||||||
|
mimetype = "image/png"
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Execute the meta data table to determine the paths. This function
|
||||||
|
-- must be called first to get the desired path. If one of these
|
||||||
|
-- meta options was set, it gets used instead of the corresponding
|
||||||
|
-- environment variable:
|
||||||
|
function Meta(meta)
|
||||||
|
plantuml_path = stringify(
|
||||||
|
meta.plantuml_path or meta.plantumlPath or plantuml_path
|
||||||
|
)
|
||||||
|
inkscape_path = stringify(
|
||||||
|
meta.inkscape_path or meta.inkscapePath or inkscape_path
|
||||||
|
)
|
||||||
|
python_path = stringify(
|
||||||
|
meta.python_path or meta.pythonPath or python_path
|
||||||
|
)
|
||||||
|
python_activate_path =
|
||||||
|
meta.activate_python_path or meta.activatePythonPath or python_activate_path
|
||||||
|
python_activate_path = python_activate_path and stringify(python_activate_path)
|
||||||
|
java_path = stringify(
|
||||||
|
meta.java_path or meta.javaPath or java_path
|
||||||
|
)
|
||||||
|
dot_path = stringify(
|
||||||
|
meta.path_dot or meta.dotPath or dot_path
|
||||||
|
)
|
||||||
|
pdflatex_path = stringify(
|
||||||
|
meta.pdflatex_path or meta.pdflatexPath or pdflatex_path
|
||||||
|
)
|
||||||
|
asymptote_path = stringify(
|
||||||
|
meta.asymptote_path or meta.asymptotePath or asymptote_path
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Call plantuml.jar with some parameters (cf. PlantUML help):
|
||||||
|
local function plantuml(puml, filetype)
|
||||||
|
return pandoc.pipe(
|
||||||
|
java_path,
|
||||||
|
{"-jar", plantuml_path, "-t" .. filetype, "-pipe", "-charset", "UTF8"},
|
||||||
|
puml
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Call dot (GraphViz) in order to generate the image
|
||||||
|
-- (thanks @muxueqz for this code):
|
||||||
|
local function graphviz(code, filetype)
|
||||||
|
return pandoc.pipe(dot_path, {"-T" .. filetype}, code)
|
||||||
|
end
|
||||||
|
|
||||||
|
--
|
||||||
|
-- TikZ
|
||||||
|
--
|
||||||
|
|
||||||
|
--- LaTeX template used to compile TikZ images. Takes additional
|
||||||
|
--- packages as the first, and the actual TikZ code as the second
|
||||||
|
--- argument.
|
||||||
|
local tikz_template = [[
|
||||||
|
\documentclass{standalone}
|
||||||
|
\usepackage{tikz}
|
||||||
|
%% begin: additional packages
|
||||||
|
%s
|
||||||
|
%% end: additional packages
|
||||||
|
\begin{document}
|
||||||
|
%s
|
||||||
|
\end{document}
|
||||||
|
]]
|
||||||
|
|
||||||
|
-- Returns a function which takes the filename of a PDF or SVG file
|
||||||
|
-- and a target filename, and writes the input as the given format.
|
||||||
|
-- Returns `nil` if conversion into the target format is not possible.
|
||||||
|
local function convert_with_inkscape(filetype)
|
||||||
|
-- Build the basic Inkscape command for the conversion
|
||||||
|
local inkscape_output_args
|
||||||
|
if filetype == 'png' then
|
||||||
|
inkscape_output_args = '--export-png="%s" --export-dpi=300'
|
||||||
|
elseif filetype == 'svg' then
|
||||||
|
inkscape_output_args = '--export-plain-svg="%s"'
|
||||||
|
else
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
return function (pdf_file, outfile)
|
||||||
|
local inkscape_command = string.format(
|
||||||
|
'"%s" --without-gui --file="%s" ' .. inkscape_output_args,
|
||||||
|
inkscape_path,
|
||||||
|
pdf_file,
|
||||||
|
outfile
|
||||||
|
)
|
||||||
|
io.stderr:write(inkscape_command .. '\n')
|
||||||
|
local command_output = io.popen(inkscape_command)
|
||||||
|
-- TODO: print output when debugging.
|
||||||
|
command_output:close()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Compile LaTeX with Tikz code to an image
|
||||||
|
local function tikz2image(src, filetype, additional_packages)
|
||||||
|
local convert = convert_with_inkscape(filetype)
|
||||||
|
-- Bail if there is now known way from PDF to the target format.
|
||||||
|
if not convert then
|
||||||
|
error(string.format("Don't know how to convert pdf to %s.", filetype))
|
||||||
|
end
|
||||||
|
return with_temporary_directory("tikz2image", function (tmpdir)
|
||||||
|
return with_working_directory(tmpdir, function ()
|
||||||
|
-- Define file names:
|
||||||
|
local file_template = "%s/tikz-image.%s"
|
||||||
|
local tikz_file = file_template:format(tmpdir, "tex")
|
||||||
|
local pdf_file = file_template:format(tmpdir, "pdf")
|
||||||
|
local outfile = file_template:format(tmpdir, filetype)
|
||||||
|
|
||||||
|
-- Build and write the LaTeX document:
|
||||||
|
local f = io.open(tikz_file, 'w')
|
||||||
|
f:write(tikz_template:format(additional_packages or '', src))
|
||||||
|
f:close()
|
||||||
|
|
||||||
|
-- Execute the LaTeX compiler:
|
||||||
|
pandoc.pipe(pdflatex_path, {'-output-directory', tmpdir, tikz_file}, '')
|
||||||
|
|
||||||
|
convert(pdf_file, outfile)
|
||||||
|
|
||||||
|
-- Try to open and read the image:
|
||||||
|
local img_data
|
||||||
|
local r = io.open(outfile, 'rb')
|
||||||
|
if r then
|
||||||
|
img_data = r:read("*all")
|
||||||
|
r:close()
|
||||||
|
else
|
||||||
|
-- TODO: print warning
|
||||||
|
end
|
||||||
|
|
||||||
|
return img_data
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Run Python to generate an image:
|
||||||
|
local function py2image(code, filetype)
|
||||||
|
|
||||||
|
-- Define the temp files:
|
||||||
|
local outfile = string.format('%s.%s', os.tmpname(), filetype)
|
||||||
|
local pyfile = os.tmpname()
|
||||||
|
|
||||||
|
-- Replace the desired destination's file type in the Python code:
|
||||||
|
local extendedCode = string.gsub(code, "%$FORMAT%$", filetype)
|
||||||
|
|
||||||
|
-- Replace the desired destination's path in the Python code:
|
||||||
|
extendedCode = string.gsub(extendedCode, "%$DESTINATION%$", outfile)
|
||||||
|
|
||||||
|
-- Write the Python code:
|
||||||
|
local f = io.open(pyfile, 'w')
|
||||||
|
f:write(extendedCode)
|
||||||
|
f:close()
|
||||||
|
|
||||||
|
-- Execute Python in the desired environment:
|
||||||
|
local pycmd = python_path .. ' ' .. pyfile
|
||||||
|
local command = python_activate_path
|
||||||
|
and python_activate_path .. ' && ' .. pycmd
|
||||||
|
or pycmd
|
||||||
|
os.execute(command)
|
||||||
|
|
||||||
|
-- Try to open the written image:
|
||||||
|
local r = io.open(outfile, 'rb')
|
||||||
|
local imgData = nil
|
||||||
|
|
||||||
|
-- When the image exist, read it:
|
||||||
|
if r then
|
||||||
|
imgData = r:read("*all")
|
||||||
|
r:close()
|
||||||
|
else
|
||||||
|
io.stderr:write(string.format("File '%s' could not be opened", outfile))
|
||||||
|
error 'Could not create image from python code.'
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Delete the tmp files:
|
||||||
|
os.remove(pyfile)
|
||||||
|
os.remove(outfile)
|
||||||
|
|
||||||
|
return imgData
|
||||||
|
end
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Asymptote
|
||||||
|
--
|
||||||
|
|
||||||
|
local function asymptote(code, filetype)
|
||||||
|
local convert
|
||||||
|
if filetype ~= 'svg' and filetype ~= 'png' then
|
||||||
|
error(string.format("Conversion to %s not implemented", filetype))
|
||||||
|
end
|
||||||
|
return with_temporary_directory(
|
||||||
|
"asymptote",
|
||||||
|
function(tmpdir)
|
||||||
|
return with_working_directory(
|
||||||
|
tmpdir,
|
||||||
|
function ()
|
||||||
|
local asy_file = "pandoc_diagram.asy"
|
||||||
|
local svg_file = "pandoc_diagram.svg"
|
||||||
|
local f = io.open(asy_file, 'w')
|
||||||
|
f:write(code)
|
||||||
|
f:close()
|
||||||
|
|
||||||
|
pandoc.pipe(asymptote_path, {"-f", "svg", "-o", "pandoc_diagram", asy_file}, "")
|
||||||
|
|
||||||
|
local r
|
||||||
|
if filetype == 'svg' then
|
||||||
|
r = io.open(svg_file, 'rb')
|
||||||
|
else
|
||||||
|
local png_file = "pandoc_diagram.png"
|
||||||
|
convert_with_inkscape("png")(svg_file, png_file)
|
||||||
|
r = io.open(png_file, 'rb')
|
||||||
|
end
|
||||||
|
|
||||||
|
local img_data
|
||||||
|
if r then
|
||||||
|
img_data = r:read("*all")
|
||||||
|
r:close()
|
||||||
|
else
|
||||||
|
error("could not read asymptote result file")
|
||||||
|
end
|
||||||
|
return img_data
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Executes each document's code block to find matching code blocks:
|
||||||
|
function CodeBlock(block)
|
||||||
|
|
||||||
|
-- Predefine a potential image:
|
||||||
|
local fname = nil
|
||||||
|
|
||||||
|
-- Using a table with all known generators i.e. converters:
|
||||||
|
local converters = {
|
||||||
|
plantuml = plantuml,
|
||||||
|
graphviz = graphviz,
|
||||||
|
tikz = tikz2image,
|
||||||
|
py2image = py2image,
|
||||||
|
asymptote = asymptote,
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Check if a converter exists for this block. If not, return the block
|
||||||
|
-- unchanged.
|
||||||
|
local img_converter = converters[block.classes[1]]
|
||||||
|
if not img_converter then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Call the correct converter which belongs to the used class:
|
||||||
|
local success, img = pcall(img_converter, block.text,
|
||||||
|
filetype, block.attributes["additionalPackages"] or nil)
|
||||||
|
|
||||||
|
-- Was ok?
|
||||||
|
if success and img then
|
||||||
|
-- Hash the figure name and content:
|
||||||
|
fname = pandoc.sha1(img) .. "." .. filetype
|
||||||
|
|
||||||
|
-- Store the data in the media bag:
|
||||||
|
pandoc.mediabag.insert(fname, mimetype, img)
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
-- an error occured; img contains the error message
|
||||||
|
io.stderr:write(tostring(img))
|
||||||
|
io.stderr:write('\n')
|
||||||
|
error 'Image conversion failed. Aborting.'
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Case: This code block was an image e.g. PlantUML or dot/Graphviz, etc.:
|
||||||
|
if fname then
|
||||||
|
|
||||||
|
-- Define the default caption:
|
||||||
|
local caption = {}
|
||||||
|
local enableCaption = nil
|
||||||
|
|
||||||
|
-- If the user defines a caption, use it:
|
||||||
|
if block.attributes["caption"] then
|
||||||
|
caption = pandoc.read(block.attributes.caption).blocks[1].content
|
||||||
|
|
||||||
|
-- This is pandoc's current hack to enforce a caption:
|
||||||
|
enableCaption = "fig:"
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Create a new image for the document's structure. Attach the user's
|
||||||
|
-- caption. Also use a hack (fig:) to enforce pandoc to create a
|
||||||
|
-- figure i.e. attach a caption to the image.
|
||||||
|
local imgObj = pandoc.Image(caption, fname, enableCaption)
|
||||||
|
|
||||||
|
-- Now, transfer the attribute "name" from the code block to the new
|
||||||
|
-- image block. It might gets used by the figure numbering lua filter.
|
||||||
|
-- If the figure numbering gets not used, this additional attribute
|
||||||
|
-- gets ignored as well.
|
||||||
|
if block.attributes["name"] then
|
||||||
|
imgObj.attributes["name"] = block.attributes["name"]
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Transfer the identifier from the code block to the new image block
|
||||||
|
-- to enable downstream filters like pandoc-crossref. This allows a figure
|
||||||
|
-- block starting with:
|
||||||
|
--
|
||||||
|
-- ```{#fig:pumlExample .plantuml caption="This is an image, created by **PlantUML**."}
|
||||||
|
--
|
||||||
|
-- to be referenced as @fig:pumlExample outside of the figure.
|
||||||
|
if block.identifier then
|
||||||
|
imgObj.identifier = block.identifier
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Finally, put the image inside an empty paragraph. By returning the
|
||||||
|
-- resulting paragraph object, the source code block gets replaced by
|
||||||
|
-- the image:
|
||||||
|
return pandoc.Para{ imgObj }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Normally, pandoc will run the function in the built-in order Inlines ->
|
||||||
|
-- Blocks -> Meta -> Pandoc. We instead want Meta -> Blocks. Thus, we must
|
||||||
|
-- define our custom order:
|
||||||
|
return {
|
||||||
|
{Meta = Meta},
|
||||||
|
{CodeBlock = CodeBlock},
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
-- https://stackoverflow.com/questions/40993488/convert-markdown-links-to-html-with-pandoc
|
||||||
|
function Link(el)
|
||||||
|
el.target = string.gsub(el.target, "%.md", ".html")
|
||||||
|
return el
|
||||||
|
end
|
|
@ -0,0 +1,40 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="generator" content="pandoc" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
|
||||||
|
<title>lilypond</title>
|
||||||
|
<style>
|
||||||
|
code{white-space: pre-wrap;}
|
||||||
|
span.smallcaps{font-variant: small-caps;}
|
||||||
|
span.underline{text-decoration: underline;}
|
||||||
|
div.column{display: inline-block; vertical-align: top; width: 50%;}
|
||||||
|
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
|
||||||
|
ul.task-list{list-style: none;}
|
||||||
|
</style>
|
||||||
|
<link rel="stylesheet" href="pandoc.css" />
|
||||||
|
<!--[if lt IE 9]>
|
||||||
|
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
|
||||||
|
<![endif]-->
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p>if PANDOC_VERSION and PANDOC_VERSION.must_be_at_least then – Actually, this check is redundant since `Version’ objects were – introduced in pandoc v2.7.3, but I’ve left it in for clarity. PANDOC_VERSION:must_be_at_least(“2.7.3”) else error(“pandoc version >=2.7.3 is required”) end</p>
|
||||||
|
<p>local OPTIONS = { image_directory = “.” }</p>
|
||||||
|
<p>local SPECIAL_CLASSES = { [“lilypond”] = true, [“ly-fragment”] = true, [“ly-norender”] = true }</p>
|
||||||
|
<p>local SPECIAL_ATTRIBUTES = { [“ly-caption”] = true, [“ly-name”] = true, [“ly-resolution”] = true, [“ly-title”] = true }</p>
|
||||||
|
<p>– pandoc.system.with_temporary_directory had a different (undocumented) – name in the 2.7.3 release. local with_temporary_directory = tostring(PANDOC_VERSION) == “2.7.3” and pandoc.system.with_temp_directory or pandoc.system.with_temporary_directory</p>
|
||||||
|
– This is the extra boilerplate that’s added to code snippets when the – <code>ly-fragment' class is present. It's adapted from what</code>lilypond-book’ – does. (The file `lilypond-book-preamble.ly’ is placed on the include – path as part of the default LilyPond installation.) local function wrap_fragment(src) return table.concat( { [[
|
||||||
|
<p>“lilypond-book-preamble.ly”]], [[]], src, }, “” ) end</p>
|
||||||
|
<p>local function generate_image(name, input, dpi) local fullname = name .. “.png” return fullname, with_temporary_directory( “lilypond-lua-XXXXX,” function (tmp_dir) return pandoc.system.with_working_directory( tmp_dir, function () pandoc.pipe( “lilypond,” { “–silent,” “–png,” dpi and “-dresolution=” .. dpi or "“,”–output=" .. name, “-” }, input ) local fh = io.open(fullname, ‘rb’) local data = fh:read(’*all’) fh:close() return data end ) end ) end</p>
|
||||||
|
<p>local function process_lilypond(elem, inline) local code = elem.text local fragment = elem.classes:includes(“ly-fragment”) or inline local input = fragment and wrap_fragment(code) or code local dpi = elem.attributes[“ly-resolution”] local name = elem.attributes[“ly-name”] or pandoc.sha1(code)</p>
|
||||||
|
<p>local image_filename, image_data = generate_image(name, input, dpi) local src = OPTIONS.image_directory .. ‘/’ .. image_filename pandoc.mediabag.insert(src, “image/png,” image_data)</p>
|
||||||
|
<p>local caption = elem.attributes[“ly-caption”] or “Musical notation” – The “fig:” prefix causes this image to be rendered as a proper figure – in HTML ouput (this is a rather ugly pandoc feature and may be replaced – by something more elegant in the future). local fudge = inline and "" or “fig:” – Strip newlines, indendation, etc. from the code for a more readable title. local title = fudge .. (elem.attributes[“ly-title”] or code:gsub(“%s+,” " "))</p>
|
||||||
|
<p>– Strip most of the LilyPond-related attributes from this code element, for – tidiness. local classes = elem.classes:filter( function (cls) return not SPECIAL_CLASSES[cls] end ) table.insert( classes, – Add one special class for styling/manipulation purposes. inline and “lilypond-image-inline” or “lilypond-image-standalone” ) local attributes = elem.attributes for a, t in pairs(SPECIAL_ATTRIBUTES) do attributes[a] = nil end local attrs = pandoc.Attr(elem.identifier, classes, attributes)</p>
|
||||||
|
<p>return pandoc.Image(caption, src, title, attrs) end</p>
|
||||||
|
<p>– Update `OPTIONS’ based on the document metadata. local function meta_transformer(md) local ly_block = md.lilypond or {} local dir_conf = ly_block.image_directory OPTIONS.image_directory = dir_conf and pandoc.utils.stringify(dir_conf) or OPTIONS.image_directory md.lilypond = nil return md end</p>
|
||||||
|
<p>local function code_transformer(elem) if elem.classes:includes(“lilypond”) and not elem.classes:includes(“ly-norender”) then return process_lilypond(elem, true) else return elem end end</p>
|
||||||
|
<p>local function code_block_transformer(elem) if elem.classes:includes(“lilypond”) and not elem.classes:includes(“ly-norender”) then – When replacing a block element we must wrap the generated image – in a <code>Para' since</code>Image’ is an inline element. return pandoc.Para({process_lilypond(elem, false)}) else return elem end end</p>
|
||||||
|
<p>– Make sure the metadata transformation runs first so that the code – transformations operate with the correct options. return { {Meta = meta_transformer}, {Code = code_transformer, CodeBlock = code_block_transformer}, }</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,154 @@
|
||||||
|
if PANDOC_VERSION and PANDOC_VERSION.must_be_at_least then
|
||||||
|
-- Actually, this check is redundant since `Version' objects were
|
||||||
|
-- introduced in pandoc v2.7.3, but I've left it in for clarity.
|
||||||
|
PANDOC_VERSION:must_be_at_least("2.7.3")
|
||||||
|
else
|
||||||
|
error("pandoc version >=2.7.3 is required")
|
||||||
|
end
|
||||||
|
|
||||||
|
local OPTIONS = {
|
||||||
|
image_directory = ".",
|
||||||
|
}
|
||||||
|
|
||||||
|
local SPECIAL_CLASSES = {
|
||||||
|
["lilypond"] = true,
|
||||||
|
["ly-fragment"] = true,
|
||||||
|
["ly-norender"] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
local SPECIAL_ATTRIBUTES = {
|
||||||
|
["ly-caption"] = true,
|
||||||
|
["ly-name"] = true,
|
||||||
|
["ly-resolution"] = true,
|
||||||
|
["ly-title"] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
-- pandoc.system.with_temporary_directory had a different (undocumented)
|
||||||
|
-- name in the 2.7.3 release.
|
||||||
|
local with_temporary_directory = tostring(PANDOC_VERSION) == "2.7.3"
|
||||||
|
and pandoc.system.with_temp_directory
|
||||||
|
or pandoc.system.with_temporary_directory
|
||||||
|
|
||||||
|
-- This is the extra boilerplate that's added to code snippets when the
|
||||||
|
-- `ly-fragment' class is present. It's adapted from what `lilypond-book'
|
||||||
|
-- does. (The file `lilypond-book-preamble.ly' is placed on the include
|
||||||
|
-- path as part of the default LilyPond installation.)
|
||||||
|
local function wrap_fragment(src)
|
||||||
|
return table.concat(
|
||||||
|
{
|
||||||
|
[[\include "lilypond-book-preamble.ly"]],
|
||||||
|
[[\paper { indent = 0\mm }]],
|
||||||
|
src,
|
||||||
|
},
|
||||||
|
"\n"
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function generate_image(name, input, dpi)
|
||||||
|
local fullname = name .. ".png"
|
||||||
|
return fullname, with_temporary_directory(
|
||||||
|
"lilypond-lua-XXXXX",
|
||||||
|
function (tmp_dir)
|
||||||
|
return pandoc.system.with_working_directory(
|
||||||
|
tmp_dir,
|
||||||
|
function ()
|
||||||
|
pandoc.pipe(
|
||||||
|
"lilypond",
|
||||||
|
{
|
||||||
|
"--silent",
|
||||||
|
"--png", dpi and "-dresolution=" .. dpi or "",
|
||||||
|
"--output=" .. name, "-"
|
||||||
|
},
|
||||||
|
input
|
||||||
|
)
|
||||||
|
local fh = io.open(fullname, 'rb')
|
||||||
|
local data = fh:read('*all')
|
||||||
|
fh:close()
|
||||||
|
return data
|
||||||
|
end
|
||||||
|
)
|
||||||
|
end
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function process_lilypond(elem, inline)
|
||||||
|
local code = elem.text
|
||||||
|
local fragment = elem.classes:includes("ly-fragment") or inline
|
||||||
|
local input = fragment
|
||||||
|
and wrap_fragment(code)
|
||||||
|
or code
|
||||||
|
local dpi = elem.attributes["ly-resolution"]
|
||||||
|
local name = elem.attributes["ly-name"] or pandoc.sha1(code)
|
||||||
|
|
||||||
|
local image_filename, image_data = generate_image(name, input, dpi)
|
||||||
|
local src = OPTIONS.image_directory .. '/' .. image_filename
|
||||||
|
pandoc.mediabag.insert(src, "image/png", image_data)
|
||||||
|
|
||||||
|
local caption = elem.attributes["ly-caption"] or "Musical notation"
|
||||||
|
-- The "fig:" prefix causes this image to be rendered as a proper figure
|
||||||
|
-- in HTML ouput (this is a rather ugly pandoc feature and may be replaced
|
||||||
|
-- by something more elegant in the future).
|
||||||
|
local fudge = inline and "" or "fig:"
|
||||||
|
-- Strip newlines, indendation, etc. from the code for a more readable title.
|
||||||
|
local title = fudge .. (elem.attributes["ly-title"]
|
||||||
|
or code:gsub("%s+", " "))
|
||||||
|
|
||||||
|
-- Strip most of the LilyPond-related attributes from this code element, for
|
||||||
|
-- tidiness.
|
||||||
|
local classes = elem.classes:filter(
|
||||||
|
function (cls)
|
||||||
|
return not SPECIAL_CLASSES[cls]
|
||||||
|
end
|
||||||
|
)
|
||||||
|
table.insert(
|
||||||
|
classes,
|
||||||
|
-- Add one special class for styling/manipulation purposes.
|
||||||
|
inline and "lilypond-image-inline"
|
||||||
|
or "lilypond-image-standalone"
|
||||||
|
)
|
||||||
|
local attributes = elem.attributes
|
||||||
|
for a, t in pairs(SPECIAL_ATTRIBUTES) do
|
||||||
|
attributes[a] = nil
|
||||||
|
end
|
||||||
|
local attrs = pandoc.Attr(elem.identifier, classes, attributes)
|
||||||
|
|
||||||
|
return pandoc.Image(caption, src, title, attrs)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Update `OPTIONS' based on the document metadata.
|
||||||
|
local function meta_transformer(md)
|
||||||
|
local ly_block = md.lilypond or {}
|
||||||
|
local dir_conf = ly_block.image_directory
|
||||||
|
OPTIONS.image_directory = dir_conf
|
||||||
|
and pandoc.utils.stringify(dir_conf)
|
||||||
|
or OPTIONS.image_directory
|
||||||
|
md.lilypond = nil
|
||||||
|
return md
|
||||||
|
end
|
||||||
|
|
||||||
|
local function code_transformer(elem)
|
||||||
|
if elem.classes:includes("lilypond")
|
||||||
|
and not elem.classes:includes("ly-norender") then
|
||||||
|
return process_lilypond(elem, true)
|
||||||
|
else
|
||||||
|
return elem
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function code_block_transformer(elem)
|
||||||
|
if elem.classes:includes("lilypond")
|
||||||
|
and not elem.classes:includes("ly-norender") then
|
||||||
|
-- When replacing a block element we must wrap the generated image
|
||||||
|
-- in a `Para' since `Image' is an inline element.
|
||||||
|
return pandoc.Para({process_lilypond(elem, false)})
|
||||||
|
else
|
||||||
|
return elem
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Make sure the metadata transformation runs first so that the code
|
||||||
|
-- transformations operate with the correct options.
|
||||||
|
return {
|
||||||
|
{Meta = meta_transformer},
|
||||||
|
{Code = code_transformer, CodeBlock = code_block_transformer},
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
local system = require 'pandoc.system'
|
||||||
|
|
||||||
|
local tikz_doc_template = [[
|
||||||
|
\documentclass{standalone}
|
||||||
|
\usepackage{xcolor}
|
||||||
|
\usepackage{tikz}
|
||||||
|
\begin{document}
|
||||||
|
\nopagecolor
|
||||||
|
%s
|
||||||
|
\end{document}
|
||||||
|
]]
|
||||||
|
|
||||||
|
local function tikz2image(src, filetype, outfile)
|
||||||
|
system.with_temporary_directory('tikz2image', function (tmpdir)
|
||||||
|
system.with_working_directory(tmpdir, function()
|
||||||
|
local f = io.open('tikz.tex', 'w')
|
||||||
|
f:write(tikz_doc_template:format(src))
|
||||||
|
f:close()
|
||||||
|
os.execute('pdflatex tikz.tex')
|
||||||
|
if filetype == 'pdf' then
|
||||||
|
os.rename('tikz.pdf', outfile)
|
||||||
|
else
|
||||||
|
os.execute('pdf2svg tikz.pdf ' .. outfile)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
extension_for = {
|
||||||
|
html = 'svg',
|
||||||
|
html4 = 'svg',
|
||||||
|
html5 = 'svg',
|
||||||
|
latex = 'pdf',
|
||||||
|
beamer = 'pdf' }
|
||||||
|
|
||||||
|
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 starts_with(start, str)
|
||||||
|
return str:sub(1, #start) == start
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function RawBlock(el)
|
||||||
|
if starts_with('\\begin{tikzpicture}', el.text) then
|
||||||
|
local filetype = extension_for[FORMAT] or 'svg'
|
||||||
|
local fname = system.get_working_directory() .. '/' ..
|
||||||
|
pandoc.sha1(el.text) .. '.' .. filetype
|
||||||
|
if not file_exists(fname) then
|
||||||
|
tikz2image(el.text, filetype, fname)
|
||||||
|
end
|
||||||
|
return pandoc.Para({pandoc.Image({}, fname)})
|
||||||
|
else
|
||||||
|
return el
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue