Skip to content

Adding support for aasvg graphics. #216

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Mar 11, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/render.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ jobs:
*.upb
*.convert.pdf
*.mermaid.pdf
*.aasvg.pdf
key: latex-${{ inputs.input }}-${{ inputs.container-version }}-${{ github.run_id }}
restore-keys: latex-${{ inputs.input }}-${{ inputs.container-version }}

Expand Down
4 changes: 3 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -238,15 +238,17 @@ RUN tlmgr update --self && tlmgr install \
zref

RUN apt install -y \
aasvg \
dbus \
imagemagick \
librsvg2-bin \
libxss1 \
openbox \
wget \
xorg \
xvfb

ENV DRAWIO_RELEASE=24.7.8
ENV DRAWIO_RELEASE=26.0.16

# TARGETPLATFORM is linux/arm64 or linux/amd64. The release for amd64 is called drawio-amd64-23.1.5.deb.
RUN export DRAWIO_DEB=drawio-${TARGETPLATFORM#linux/}-${DRAWIO_RELEASE}.deb && \
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ The interesting dependencies are:
(Pandoc/LaTeX template, with some TCG-specific modifications)
* [Mermaid](https://mermaid-js.github.io/mermaid/#/) /
[mermaid-filter](https://github.com/raghur/mermaid-filter) (for rendering diagrams)
* [aasvg](https://github.com/martinthomson/aasvg) (for rendering ASCII art diagrams)

# How to Use

Expand Down
4 changes: 4 additions & 0 deletions build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -670,6 +670,7 @@ do_latex() {
--no-highlight
--template=tcg.tex
--lua-filter=mermaid-filter.lua
--lua-filter=aasvg-filter.lua
--lua-filter=informative-sections.lua
--lua-filter=convert-images.lua
--lua-filter=center-images.lua
Expand Down Expand Up @@ -745,6 +746,7 @@ do_pdf() {
# Copy converted images so they can be cached as well.
cp *.convert.pdf "${SOURCE_DIR}" 2>/dev/null
cp *.mermaid.pdf "${SOURCE_DIR}" 2>/dev/null
cp *.aasvg.pdf "${SOURCE_DIR}" 2>/dev/null
echo "Elapsed time: $(($end-$start)) seconds"
# Write any LaTeX errors to stderr.
>&2 grep -A 5 "! " "${logfile}"
Expand Down Expand Up @@ -779,6 +781,7 @@ do_docx() {
--embed-resources
--standalone
--lua-filter=mermaid-filter.lua
--lua-filter=aasvg-filter.lua
--lua-filter=convert-images.lua
--lua-filter=parse-html.lua
--lua-filter=apply-classes-to-tables.lua
Expand Down Expand Up @@ -819,6 +822,7 @@ do_html() {
--embed-resources
--standalone
--lua-filter=mermaid-filter.lua
--lua-filter=aasvg-filter.lua
--lua-filter=parse-html.lua
--lua-filter=apply-classes-to-tables.lua
--lua-filter=landscape-pages.lua
Expand Down
84 changes: 84 additions & 0 deletions filter/aasvg-filter.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
-- Turn aasvg-classed code blocks into figures, retaining other classes on the
-- code block as classes on the figure.

function runCommandWithInput(command, input)
local pipe = io.popen(command, "w")
if not pipe then
return false
end
pipe:write(input)
pipe:flush()
pipe:close()
return true
end

function runCommandSuppressOutput(command)
-- N.B.: we are using io.popen so we can suppress the output of the command.
local pipe = io.popen(command)
if not pipe then
return false
end
pipe:flush()
local output = pipe:read("*all")
pipe:close()
return true
end

function getContentsHash(contents)
return pandoc.sha1(contents):sub(1,10)
end

function fileExists(file)
local f = io.open(file)
if f then
f:close()
return true
end
return false
end

function aasvgFigure(code, caption, attrs)
local filename_base = getContentsHash('code=' .. code .. 'caption=' .. pandoc.utils.stringify(caption) .. 'attrs=' .. pandoc.utils.stringify(attrs)) .. '.aasvg'
local filename_svg = filename_base .. '.svg'
local filename_pdf = filename_base .. '.pdf'

if fileExists(filename_pdf) then
print(string.format('%s already exists; not re-rendering it', filename_pdf))
else
print(string.format('rendering %s using aasvg ...', filename_svg))
if not runCommandWithInput(string.format(
"aasvg --fill > %s 2>&1", filename_svg), code) then
print(string.format('failed to convert ASCII art SVG (aasvg) diagram to %s using aasvg, falling back to letting LaTeX try to pick it up', filename_svg))
return false
end

print(string.format('converting %s to %s using rsvg-convert ...', filename_svg, filename_pdf))
if not runCommandSuppressOutput(string.format("rsvg-convert --format=pdf --keep-aspect-ratio --output %s %s 2>&1", filename_pdf, filename_svg)) then
print(string.format('failed to convert %s to %s using rsvg-convert, falling back to letting LaTeX try to pick it up', filename_svg, filename_pdf))
return false
end
end

local img = pandoc.Image(caption, filename_pdf)
return pandoc.Figure(img, caption, attrs)
end

function CodeBlock(el)
local isAasvg = false
local figure_classes = pandoc.List({})
for i, class in ipairs(el.classes) do
if class == 'aasvg' then
isAasvg = true
else
figure_classes:insert(class)
end
end
if isAasvg then
local caption = {long = pandoc.Plain(pandoc.Str(el.attributes.caption))}
local attrs = pandoc.Attr(el.identifier, figure_classes)
el.identifier = nil
el.classes = {'aasvg'}
return aasvgFigure(el.text, caption, attrs)
end
return el
end
30 changes: 20 additions & 10 deletions filter/convert-images.lua
Original file line number Diff line number Diff line change
Expand Up @@ -42,41 +42,51 @@ end
-- Wrap calls to drawio in xvfb-run. Note that --no-sandbox has to be the last argument.
-- https://github.com/jgraph/drawio-desktop/issues/249
function drawio(source, dest)
print(string.format('converting %s using drawio...', source))
print(string.format('converting %s using drawio ...', source))
if not runCommandSuppressOutput(string.format("xvfb-run -a drawio -x -f pdf --crop -o %s %s --no-sandbox 2>&1", dest, source)) then
print(string.format('failed to convert %s to %s using drawio, falling back to letting latex try to pick it up', source, dest))
print(string.format('failed to convert %s to %s using drawio, falling back to letting LaTeX try to pick it up', source, dest))
return false
end
return true
end

function imagemagick(source, dest)
print(string.format('converting %s using imagemagick...', source))
print(string.format('converting %s using ImageMagick ...', source))
if not runCommandSuppressOutput(string.format("convert -density 300 %s %s 2>&1", source, dest)) then
print(string.format('failed to convert %s to %s using imagemagick, falling back to letting latex try to pick it up', source, dest))
print(string.format('failed to convert %s to %s using ImageMagick, falling back to letting LaTeX try to pick it up', source, dest))
return false
end
return true
end

function svg(source, dest)
print(string.format('converting %s using rsvg-convert...', source))
if not runCommandSuppressOutput(string.format("rsvg-convert --format=pdf --keep-aspect-ratio --output %s %s 2>&1", dest, source)) then
print(string.format('failed to convert %s to %s using rsvg-convert, falling back to letting LaTeX try to pick it up', source, dest))
return false
end
return true
end

function string:hassuffix(suffix)
return self:sub(-#suffix) == suffix
return self:sub(-#suffix):lower() == suffix:lower()
end

function converterFor(filename)
if filename:hassuffix('.drawio') or filename:hassuffix('.drawio.svg') then
return drawio
end
if filename:hassuffix('.jpg') or filename:hassuffix('.png') or filename:hassuffix('.svg') then
elseif filename:hassuffix('.jpg') or filename:hassuffix('.png') or filename:hassuffix('.webp') then
return imagemagick
elseif filename:hassuffix('.svg') then
return svg
end
return nil
end

function Image (img)
-- Try to convert anything that is not a pdf, jpg, or png.
-- This allows us to support file types that latex doesn't (e.g., SVG),
-- as well as speed up the latex render iterations.
-- This allows us to support file types that LaTeX doesn't (e.g., SVG),
-- as well as speed up the LaTeX render iterations.
local converter = converterFor(img.src)
if converter then
local new_filename = img.src .. '.' .. getFileHash(img.src) .. '.convert.pdf'
Expand All @@ -96,4 +106,4 @@ function Image (img)
print(string.format(" not converting %s", img.src))
end
return img
end
end
2 changes: 1 addition & 1 deletion filter/informative-quote-blocks.lua
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@ function BlockQuote(el)
pandoc.RawBlock('latex', '\\end{mdframed}')
}))
return result
end
end
4 changes: 2 additions & 2 deletions filter/mermaid-filter.lua
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ function mermaidFigure(code, caption, attrs)
if fileExists(filename) then
print(string.format('%s already exists; not re-rendering it', filename))
else
print(string.format('rendering %s using Mermaid...', filename))
print(string.format('rendering %s using Mermaid ...', filename))
if not runCommandWithInput(string.format(
"mmdc --configFile /resources/filters/mermaid-config.json --puppeteerConfigFile ./.puppeteer.json --width 2000 --height 2000 --backgroundColor transparent --pdfFit --input - --output %s 2>&1", filename), code) then
print(string.format('failed to convert %s to %s using drawio, falling back to letting latex try to pick it up', source, dest))
print(string.format('failed to convert Mermaid diagram to %s using mmdc, falling back to letting LaTeX try to pick it up', filename))
return false
end
end
Expand Down
Loading