With PlantUML you can create diagrams as plain text code. This article covers several advanced features, e.g. how to choose the right output file format, or using the include-mechanism effectively. I also explain how you can generate code from PlantUML diagrams (and vice versa), or how to work on a model-layer by extending PlantUML’s syntax.
Introduction
PlantUML is a Java-based tool that lets you express diagrams as code. As my introduction article explains, you can use PlantUML for any kind of UML diagram and beyond, including mind maps or Gantt charts.
This article is a random collection of advanced features you may find useful. The first two sections should be of interest to everyone, while the remaining sections are only for specific audiences.
Choosing the right output format
PlantUML offers many different output formats. If you use the CLI (java -jar plantuml.jar /path/to/file.puml
) then the default output format is PNG. However, PNG is almost never useful, and it has several disadvantages:
- It becomes blurry when you scale it up
- Large diagrams (with many elements) become huge (in terms of pixel dimensions and file size), and your viewer might choke on the file
- If elements of your diagram contain clickable URLs, you’d have to use PlantUML’s
pipemap
option (docs) to generate image maps, and embed then into your HTML file, which is a chore!
I recommend the following formats, depending on the use case:
- If you want to use the diagram in LaTeX, use the LaTex/Tikz format (
-tlatex
on the CLI) - If you want to print the diagram, do not use PlantUML’s PDF (
-tpdf
) export, but export the file as SVG (-tsvg
). From personal experience, rendering PDFs with PlantUML is cumbersome, especially when it comes to setting up the right aspect ratio, dimensions or margins. It is easier to export your diagram as SVG and then create & configure a PDF file (including page dimensions, margins, etc.) with other tools:- If you just need to print your diagram once (or rarely), you can simply drag the SVG file into your favorite document editor (e.g. MS Word, LibreOffice, …), configure the page size and margins there, and scale & print the diagram without any loss in quality.
- If you need an automated solution, search the Internet for CLI tools that let you convert / embed SVG (in) to PDF files. See e.g. here for a discussion of different approaches.
- In all other cases, I recommend to export SVG. Your web browser will be able to navigate even large SVG files without problems.
Embedding SVG into HTML with clickable URLs
If you want to embed a SVG diagram file into HTML-based documentation, and your diagram contains clickable URLs, do not use <img src="...">
to embed the image, but . With the <img>
tag, links are not clickable! To see how you can influence the link target and hover color, take a look at the docs explaining the SVG-specific skinparam
options.
Use !include to your advantage
In the introduction article I introduced the !include
command, which you can use to load other files, such as standard library sprites. The !include
command is actually a PlantUML preprocessor command, which also supports including only specific sections of the included file. I highly recommend reading the official docs, in particular the sections “Including files or URL” and “Including Subpart”. Applying !include
to your own files is a very useful mechanism for different use cases, such as:
- Use the same styling for all your diagrams: you can declare a
common-styles.iuml
file that defines various styling aspects (see my previous article), to achieve visual consistency across all your diagrams - Format the diagram to match a specific purpose or output medium: for instance, you could define your elements in
mydiagram.puml
and create anotheroptimized-for-16-by-9.puml
file which!include
smydiagram.puml
, followed by various layout tricks (as presented in my previous article) to produce a diagram that fits on a 16:9 screen. - Reuse model elements in various diagrams to achieve consistency: for instance, you could define a complete class model of your application in
classes.iuml
and then produce several different diagrams which only include subparts ofclasses.iuml
, orhide
s specific classes, attributes or methods (see sections “Hide attributes, methods” and “Hide classes” in the class diagram docs).
Naming conventions for included files
I have often observed that people name their common definition or style files with the .iuml
extension (instead of .puml
) to indicate that this file is not supposed to produce diagrams on its own, but is meant to be included by other PlantUML files. There is no technical necessity to do this, but it helps others identify the purpose of your file more quickly.
Working with code
You can use PlantUML for the object design phase of your system, in which you decompose your system’s functionality into classes, attributes and methods. There are third-party tools which convert source code to PlantUML class diagrams, and vice versa.
See here and here for generators which build source code from your PlantUML class diagram. Both generators support various programming languages out of the box, including C++, C#, Java, Python, etc.
The generators which reverse-engineer class diagrams from code are typically specific to a certain programming language. Just search the Internet for something like “plantuml from <programming language> code”. You will find tools like py2uml / pynsource (Python), PlantUML Builder (Java), or PlantUmlClassDiagramGenerator (C#). In case these generators are not configurable and produce too many classes (e.g. useless classes like Object
or Boolean
), or are missing classes or connections, you can fix that with the help of !include
. Create an additional puml
file which !include
s the generated puml
file and manually fixes things, e.g. by using PlantUML’s hide
keyword (see class diagram docs), or adding missing connections.
Creating your own model elements
In some cases you want to express diagram elements using a custom, model-like language. PlantUML lets you define functions for this purpose. A concrete example is the C4 model support which is part of PlantUML’s standard library. After calling !include <C4/C4_Container.puml>
you can use syntax such asContainer(backend_api, "API Application", "Java, Docker Container", "Provides ...")
to declare a C4 container, as if you were working on the model layer. The first argument defines an alias, the second one sets the title, the third one a technology, and the fourth one is a detailed description. Under the hood, the C4_Container.puml file defines that this call is translated to an ordinary rectangle
element in which the arguments to that Container(...)
function call are placed, and some common styling is applied to it.
The pre-processor docs provide an introduction with examples illustrating how you can build such functions yourself. You should definitely be fluent in PlantUMLs basic markup syntax first, before attempting to create your own functions. As the docs recommend, you should no longer use !define
(or !definelong
), but the more modern !procedure
syntax to create your own functions which produce output shown in the diagrams. Procedures also lets you create default values for arguments, reducing the need for copy&pasting near-identical function procedures. Note that there are also “functions” in PlantUML, which can compute and return values based on their input arguments, but their returned value won’t be printed in the diagram. In essence, defining your own functions (which produce output) works like this:
- Look at the pre-processor docs examples for procedures to learn how to write functions which output diagram elements.
- Since
!
is the preprocessor symbol (similar to the#
symbol in C/C++), each line that does not start with!
is being returned as output by the procedure. As the examples in the docs illustrate, you can define local variables, while-loops and if/then/else conditions.
The following example illustrates a self-made model named connected_network
that lets you define up to four fully connected nodes:
@startuml
' Define the procedure
!unquoted procedure $connected_network($alias, $n1, $n2="", $n3="", $n4="")
storage $alias {
circle $n1
!if ($n2 != "")
circle $n2
$n1 -- $n2
!endif
!if ($n3 != "")
circle $n3
$n1 -- $n3
$n2 -[norank]- $n3
!endif
!if ($n4 != "")
circle $n4
$n1 -[norank]- $n4
$n2 -[norank]- $n4
$n3 -[norank]- $n4
!endif
}
!endprocedure
' Call the procedure
$connected_network(myalias, 1, 2, 3, 4)
@enduml
Code language: plaintext (plaintext)
You could also call $connected_network()
with fewer parameters, because the three last ones are optional.
Conclusion
If you read this article and my two previous ones, and experimented with examples, you should quickly become very efficient in creating and changing diagrams with PlantUML. Its preprocessor makes it even more powerful, because it allows you to customize the markup to include your own model descriptions, and lets you work in a modular way using !include
. The ability to generate code from class diagrams (and vice versa) makes PlantUML a very useful tool to understand the structure of software quickly, or save time when planning redesigns.