PlantUML advanced features

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 <object type="image/svg+xml" data="yourDiagram.svg"/>. 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 another optimized-for-16-by-9.puml file which !includes mydiagram.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 of classes.iuml, or hides 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 !includes 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 as
Container(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)
@endumlCode 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.

Leave a Comment