Render AsciiDoc diagram blocks as images - #2099
Draft
jorgembfigueira wants to merge 1 commit into
Draft
Conversation
Diagram blocks in AsciiDoc files -- [plantuml], [graphviz], [mermaid] and the
other types asciidoctor-diagram supports -- were emitted as an untagged <pre>,
losing the diagram language entirely. Markdown does better: commonmarker's
github_pre_lang already emits <pre lang="plantuml">, so the language survives
for a client-side renderer to pick up. This brings AsciiDoc in line and, when
the optional asciidoctor-diagram gem is installed, renders the diagram outright.
A diagram block now resolves in two steps:
1. If asciidoctor-diagram is installed, render locally and inline the result
as a data URI, so the output stays self-contained and does not depend on
where the generated file landed.
2. Otherwise fall back to a source block that keeps the diagram language on
the <pre>, which is also what happens when the toolchain a diagram type
needs (a JVM for PlantUML, Graphviz for dot, ...) is missing.
Nothing is enabled by default: without the optional gem the behaviour is step 2,
so a plain install gains the language tag and depends on no new toolchain.
Notes on the implementation:
- The extensions live in a scoped registry, never the global one. Requiring
asciidoctor-diagram's top level registers 37 extension groups globally as a
side effect, which would change how unrelated code in the same process
converts documents, so the individual per-type extension files are required
instead.
- 'data-uri' is passed per block rather than as a document attribute. It is
what makes asciidoctor-diagram report an absolute path for the generated
file, while setting it document-wide would also make Asciidoctor try to
inline every ordinary image:: in the file.
- Under safe mode :secure Asciidoctor confines file writes to the base dir, so
the conversion is handed a scratch directory. Generated images are inlined
and the directory is discarded, keeping rendering string-in, string-out.
- A data URI only survives a sanitizer that allows the data: protocol on
img/src, which the stock html-pipeline config does not. This is documented
in the README and pinned by a test, as it is the difference between a
rendered diagram and a broken image for consumers that sanitize.
asciidoctor-diagram is a development dependency (672K) but the PlantUML JAR gem
(20M) deliberately is not: the tests stub the rendering backend, so CI needs no
JVM and no diagram toolchain.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Diagram blocks in AsciiDoc files —
[plantuml],[graphviz],[mermaid]and the other types asciidoctor-diagram supports — are currently emitted as an untagged<pre>, losing the diagram language entirely:Markdown already does better.
commonmarker'sgithub_pre_langemits<pre lang="plantuml">for a fenced block, exactly as it does formermaid, so the language survives for a client-side renderer to pick up. This brings AsciiDoc in line, and renders the diagram outright when the optional gem is installed.What changes
A diagram block resolves in two steps:
If
asciidoctor-diagramis installed, render locally and inline the result as a data URI, so the output stays self-contained and does not depend on where the generated file landed:Otherwise, fall back to a source block that keeps the diagram language on the
<pre>. This is also what happens when the toolchain a diagram type needs (a JVM for PlantUML, Graphviz fordot, thed2binary, …) is missing, so a missing toolchain degrades instead of erroring:Nothing is enabled by default. Without the optional gem the behaviour is step 2, so a plain install gains the language tag and depends on no new service and no new toolchain. This also covers C4 —
!include <C4/C4_Container>plus the C4 macros renders through the PlantUML processor.Notes on the implementation
Four things that are less obvious, each pinned by a test:
Scoped extension registry, never the global one. Requiring
asciidoctor-diagram's top level registers 37 extension groups globally as a side effect, which would change how unrelated code in the same process converts documents. The individualasciidoctor-diagram/<type>/extensionfiles are required instead — those register nothing.data-uriis passed per block, not as a document attribute. It is what makes asciidoctor-diagram report an absolute path for the generated file, which is how the bytes are found. Setting it document-wide would also make Asciidoctor try to inline every ordinaryimage::in the file.A scratch directory as
base_dir. Undersafe: :secureAsciidoctor confines file writes to the base dir, soimagesoutdirpointing elsewhere is ignored. Handing the conversion a scratch directory keeps generated images out of the caller's working directory; they are inlined and the directory is discarded, so rendering stays string-in, string-out.Data URIs need the sanitizer's cooperation. The stock
html-pipelineconfig allows onlyhttp,httpsand:relativeonimg/src, so it keeps the<img>and drops thesrc, leaving a broken image. Consumers that sanitize need to allow thedata:protocol; this is documented in the README and pinned by a test, since it is the difference between a rendered diagram and a broken image.Testing
asciidoctor-diagramis added as a development dependency (672K). The PlantUML JAR gem (20M) deliberately is not: the tests stub the rendering backend, so CI needs no JVM and no diagram toolchain.The suite passes at the repository's 100% line and branch coverage gate. I also verified real rendering locally, outside the test suite: a C4 container diagram produced a 6123-byte SVG containing the expected participants and relationships, and
format=pngproduced adata:image/pngURI.Open questions for maintainers
options:rather than gem presence? Right now installingasciidoctor-diagramis what turns rendering on. An explicit option would make it impossible to enable by accident; happy to switch.LOCAL_EXTENSIONS. I mapped the 13 types that are conventionally written as delimited blocks. Trimming to just PlantUML/Graphviz/Mermaid would be a smaller diff if preferred.Marked as a draft: I would rather agree the shape of the above before polishing.