In a recent project I worked there was a need to generate PDF documents.
The document itself required to display information about a complex domain hierarchical object, containing ~100+ attributes and other child objects.
Before the existing application came to place, most users were manually creating Word documents to handle this requirement (with all the copy-pasting and human errors that one can imagine).
Existing solutions
The JVM ecosystem offers many possibilities to generate printable documents, to name a few:
While these libraries certainly offer battle-tested solutions and many features (some have been around for ~20 years !) for small or even medium project I find them a bit excessive.
(Disclaimer: I only have experience with the first two, that you won't find in my CV 😀)
I also have found that either the documentation is outdated, the learning curve is steep, the code is very low-level, only commercial licence is available or a combination of all the above.
Why Asciidoc can be good choice
Asciidoc is a lightweight markup language, where you only focus on content rather than layout.
It's offer a mature ecosystem to writing articles, documentation, books, and so on, with output formats ranging from HTML
to ePub
(and of course PDF
).
It can easily integrate images, diagrams, code.
I have been using it for years for the technical documentation of personal and non-personal projects.
Show me the code
If you are in a hurry you can check the code directly on Github
Integrating Asciidoctor
into your app is as simple as adding a maven (or gradle) dependency
<dependency>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctorj</artifactId>
<version>${asciidoctorj.version}</version>
</dependency>
<dependency>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctorj-pdf</artifactId>
<version>${asciidoctorj.pdf.version}</version>
</dependency>
Now let's see how you can generate a document :
try (Asciidoctor asciidoctor = Asciidoctor.Factory.create()) { //<1>
Attributes attributes = Attributes.builder()//<2>
.attribute("pdf-theme", theme) // optional theme
.attribute("doctype", "book")
.attribute("icons", "font")
.build();
Options options = Options.builder()
.backend("pdf")//<3>
.attributes(attributes)
.toFile(outputLocation.toFile()).build();
asciidoctor.convert(asciidocContent, options);//<4>
}
- Create Asciidoctor factory
- Define common attributes (you can even define a theme, more on that later)
- Set PDF backend
- Do the actual conversion, where
asciidocContent
is a string containing a ASCIIDOC template
That's pretty much it !
The asciidocContent
can come from a "static" file on your classpath (or anywhere really).
For more dynamic document, it is possible to use any templating engine (in our project we used mustache).
It is possible to provide customization to the default theme, while it is not as powerful as other solutions, it can be good enough for most needs.
Several examples can be found on github.
Themes can also be packaged as jar
for easier distribution.
The documentation is quite detailed.
Caveats
While this solution has served me well, it does come with some limitations:
- Customization can be quite limited depending on your needs.
- While diagrams as text is very practical, it does currently need an external dependency (graphviz, mermaid) present on your
PATH
. - Last but not least, the dependency that wraps a JRuby runtime does not work well in UBER jars (nested jars)
- For Spring Boot apps, this can be solved via
requiresUnpack
option ofspring-boot-maven-plugin
. ➡️Documentation - For Quarkus, this is not possible yet #issue
- For Spring Boot apps, this can be solved via
a quick demo project demonstrating the possibilities of this solution, with a custom theme,
is available on github.
Further reading
- A good "Getting started" guide on baeldung
- https://blog.ninja-squad.com/2022/01/06/generate-pdf-documents-in-
Top comments (0)