Full Code of plantuml-stdlib/C4-PlantUML for AI

master ef4d84fc7841 cached
116 files
588.0 KB
214.2k tokens
18 symbols
1 requests
Download .txt
Showing preview only (624K chars total). Download the full file or copy to clipboard to get everything.
Repository: plantuml-stdlib/C4-PlantUML
Branch: master
Commit: ef4d84fc7841
Files: 116
Total size: 588.0 KB

Directory structure:
gitextract_88wt1q1u/

├── .gitattributes
├── .github/
│   ├── stale.yml
│   └── workflows/
│       └── run-percy-tests.yml
├── .gitignore
├── .scripts/
│   ├── HowToCreateANewRelease.md
│   ├── plantuml-stdlib_C4_README.txt
│   ├── plantuml-stdlib_README_section.txt
│   ├── readme_release_header.txt
│   └── transform_files.py
├── C4.puml
├── C4_Component.puml
├── C4_Container.puml
├── C4_Context.puml
├── C4_Deployment.puml
├── C4_Dynamic.puml
├── C4_Sequence.puml
├── LICENSE
├── LayoutOptions.md
├── README.md
├── Themes.md
├── percy/
│   ├── C4_Component Diagram Sample - bigbankplc.puml
│   ├── C4_Container Diagram Sample - bigbankplc-icons.puml
│   ├── C4_Container Diagram Sample - bigbankplc-newStyle.puml
│   ├── C4_Container Diagram Sample - bigbankplc-styles.puml
│   ├── C4_Container Diagram Sample - bigbankplc.puml
│   ├── C4_Container Diagram Sample - message bus.puml
│   ├── C4_Container Diagram Sample - techtribesjs.puml
│   ├── C4_Context Diagram Sample - bigbankplc-landscape.puml
│   ├── C4_Context Diagram Sample - bigbankplc.puml
│   ├── C4_Context Diagram Sample - enterprise.puml
│   ├── C4_Deployment Diagram Sample - bigbankplc-details.puml
│   ├── C4_Deployment Diagram Sample - bigbankplc.puml
│   ├── C4_Dynamic Diagram Sample - bigbankplc.puml
│   ├── C4_Sequence Diagram Sample - bigbankplc.puml
│   ├── TestAllAddTags.puml
│   ├── TestAllElementsWithLegend.puml
│   ├── TestAllPlantUmlElements.puml
│   ├── TestBoundaryTagSupport.puml
│   ├── TestEmptyLabel.puml
│   ├── TestFloatingLegend.puml
│   ├── TestLabelsWithLineBreak.puml
│   ├── TestLayDirections.puml
│   ├── TestLayoutLandscape.puml
│   ├── TestLayoutLandscapeDynamic.puml
│   ├── TestLayoutLandscapeWithLay.puml
│   ├── TestLayoutLandscapeWithLayNoRotate.puml
│   ├── TestLegend.puml
│   ├── TestLegendDetailsNone.puml
│   ├── TestLegendDetailsNormal.puml
│   ├── TestLegendDetailsSmall.puml
│   ├── TestLegendHidden.puml
│   ├── TestLegendSprite.puml
│   ├── TestLegendTitle.puml
│   ├── TestNewStyle.puml
│   ├── TestPersonOutline.puml
│   ├── TestPersonPortrait.puml
│   ├── TestPredefinedSprites.puml
│   ├── TestProperty.puml
│   ├── TestPropertyMissingColumns.puml
│   ├── TestRelations.puml
│   ├── TestRelationsDynamic.puml
│   ├── TestRelationsTags.puml
│   ├── TestSequenceSkinparams.puml
│   ├── TestSketchStyle.puml
│   ├── TestSystemPersonSupportType.puml
│   ├── TestTagSupportsSprite.puml
│   ├── TestTagSupportsSpriteTechn.puml
│   ├── TestThemeC4_All.puml
│   ├── TestThemeC4_FirstTest.puml
│   ├── TestThemeC4_united.puml
│   ├── TestThemeSupport.puml
│   ├── TestThemeTemplate.puml
│   └── TestVersion.puml
├── samples/
│   ├── C4CoreDiagrams.md
│   ├── C4_Component Diagram Sample - bigbankplc.puml
│   ├── C4_Container Diagram Sample - bigbankplc-icons.puml
│   ├── C4_Container Diagram Sample - bigbankplc-styles.puml
│   ├── C4_Container Diagram Sample - bigbankplc-themes.puml
│   ├── C4_Container Diagram Sample - bigbankplc.puml
│   ├── C4_Container Diagram Sample - message bus.puml
│   ├── C4_Container Diagram Sample - techtribesjs.puml
│   ├── C4_Context Diagram Sample - bigbankplc-landscape.puml
│   ├── C4_Context Diagram Sample - bigbankplc.puml
│   ├── C4_Context Diagram Sample - enterprise.puml
│   ├── C4_Deployment Diagram Sample - bigbankplc-details.puml
│   ├── C4_Deployment Diagram Sample - bigbankplc.puml
│   ├── C4_Dynamic Diagram Sample - bigbankplc.puml
│   ├── C4_Dynamic Diagram Sample - message bus - old format.puml
│   ├── C4_Dynamic Diagram Sample - message bus.puml
│   ├── C4_Sequence Diagram Sample - bigbankplc.puml
│   └── C4_Sequence Diagram Sample - complex.puml
└── themes/
    ├── puml-theme-C4Language_chinese.puml
    ├── puml-theme-C4Language_danish.puml
    ├── puml-theme-C4Language_dutch.puml
    ├── puml-theme-C4Language_english.puml
    ├── puml-theme-C4Language_french.puml
    ├── puml-theme-C4Language_german.puml
    ├── puml-theme-C4Language_italian.puml
    ├── puml-theme-C4Language_japanese.puml
    ├── puml-theme-C4Language_korean.puml
    ├── puml-theme-C4Language_portuguese.puml
    ├── puml-theme-C4Language_russian.puml
    ├── puml-theme-C4Language_spanish.puml
    ├── puml-theme-C4Language_ukrainian.puml
    ├── puml-theme-C4_FirstTest.puml
    ├── puml-theme-C4_blue.puml
    ├── puml-theme-C4_blue_new.puml
    ├── puml-theme-C4_brown.puml
    ├── puml-theme-C4_brown_new.puml
    ├── puml-theme-C4_green.puml
    ├── puml-theme-C4_green_new.puml
    ├── puml-theme-C4_sandstone.puml
    ├── puml-theme-C4_superhero.puml
    ├── puml-theme-C4_united.puml
    ├── puml-theme-C4_violet.puml
    └── puml-theme-C4_violet_new.puml

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitattributes
================================================
# define GitHub repository languages
*.puml linguist-language=PlantUML
*.iuml linguist-language=PlantUML
*.plantuml linguist-language=PlantUML

*.puml linguist-detectable=true
*.iuml linguist-detectable=true
*.plantuml linguist-detectable=true
*.md linguist-detectable=false


================================================
FILE: .github/stale.yml
================================================
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 60
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 7
# Issues with these labels will never be considered stale
exemptLabels:
  - not-stale
  - pinned
  - security
# Label to use when marking an issue as stale
staleLabel: stale
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
  This issue has been automatically marked as stale because it has not had
  activity in the past 60 days. It will be closed in seven days if no 
  further activity occurs. Thank you for your contributions.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false


================================================
FILE: .github/workflows/run-percy-tests.yml
================================================
name: Percy Tests
on:
  push:
    paths:
    - '*.puml'
    - 'percy/**'
    - '.github/workflows/run-percy-tests.yml'
  pull_request:
    branches:
    - '**'
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@master
      - name: Setup Graphviz
        uses: ts-graphviz/setup-graphviz@v2
      - name: Process diagrams
        uses: Timmy/plantuml-action@v1
        with:
          args: '-v percy -o _parsed -DRELATIVE_INCLUDE="./.."'
      - name: Upload
        run: npx @percy/cli upload percy/_parsed
        env:
          PERCY_TOKEN: ${{ secrets.PERCY_TOKEN }}


================================================
FILE: .gitignore
================================================
################################################################################
# This .gitignore file was automatically created by Microsoft(R) Visual Studio.
################################################################################

/.idea
/.vs
/.vscode


================================================
FILE: .scripts/HowToCreateANewRelease.md
================================================
# How to create a new release (WIP)

The idea is to
- create a new release in the "release/$release_version" branch (tagged with `$release_version` and `latest`)
- and a MR that the master branch can be updated with next beta version
- create in PlantUML/PlantUML-stdlib a MR with the released version

## 0. Preparation

The process requires following 3 versions:

- **$release_version**: version which should be created (e.g. `v2.6.0`)
- **$next_version**: version of the next beta which should be stared
  as soon the release is created (e.g. `v2.7.0`).  
  The master branch will be updated with a `beta1` of this version and C4Version() returns `2.7.0beta1`.  
  If it is unknown/undefined it is calculated via the release_version. It is the next patch (release patch!=0) or subversion (release patch==0).
- **$deployed_version**: this is the next "plantuml(/plantuml-stdlib)" version
  which should be updated with this release (e.g. "V1.2023.2")  
  If it is unknown/undefined it is calculated via the running PlantUML web service

### 0.0 Create a new issue with the title `Release $release_version` \(e.g. `Release v2.6.0`)

and a body like in https://github.com/plantuml-stdlib/C4-PlantUML/issues/248

### 0.1 Check that all open issues of the related `$release_version milestone` are fixed

### 0.2 Check that all other open changes are done

Update copyright year, contrib files, URLS, .... if required

### 0.* ...

### 0.x Check which is the next released version of the PlantUML(/PlantUML-stdlib)

it is used as $deployed_version and written in the released README.md  
If it is unknown it can be calculated via `CalculateDeployedVersion` (details see below)

## 1. create new release in branch `release/$release_version` (based on master)

Atm following steps are semi-automated and can be executed in a bash shell:

### 1.0. define the relevant versions as environment variabels and create the `release/$release_version` branch

\(in following sample the `release_version` = `v2.6.0`; `next_version` = `v2.7.0` and `deployed_version` = `V1.2022.15`)

```bash
export release_version=v2.6.0
export next_version=v2.7.0
export deployed_version=V1.2022.15
```

If the deployed_version is unknown it can be calculate via following (don't forget to set the environment variable after the call)

```bash
python ./.scripts/transform_files.py CalculateDeployedVersion
```

As soon all versions are defined the `release/$release_version` branch \(e.g. `release/v2.6.0`) can be created based on master branch

```bash
git pull
git checkout master
git branch release/$release_version
git checkout release/$release_version
```

### 1.1. Update `C4Version()` in C4.puml with the new release (e.g. `2.6.0`; without `v`)

```bash
python ./.scripts/transform_files.py UpdateC4WithReleaseVersion
```

### 1.2. Update all include paths and create a release version of the README.md

Following script calls

- Update all include paths with the release version tag based branch
- Update includes of all image urls with the release version tag based in *.md
  (after that images displays the correct C4Version() number and uses only the release-tag path in all includes)
- Update README.md with the new release header and title (based on the `readme_release_header.txt` template)

```bash
python ./.scripts/transform_files.py UpdateAllIncludes
python ./.scripts/transform_files.py UpdateAllImages
python ./.scripts/transform_files.py ReplaceREADMEHeader
```

These changes can/should be checked and if everything is ok it can be committed.

Following commit all changes and tag it locally with `$release_version` (e.g. `v2.5.0`) 

```bash
git checkout release/$release_version
git add -u *.md
git add -u *.puml
git add -u **/*.md
git add -u **/*.puml
git commit -m "Create release (branch) $release_version"
git tag "$release_version"
```

And if everything is ok it can be pushed too

```bash
git checkout release/$release_version
git push -u origin release/$release_version
```

## 2. Create `Release $release_version` \(e.g. `Release v2.6.0`) itself

This is done manually \(incl. an additional check...)

**Important:** As soon the release is finished check that 
- 'latest' tag is re-assigned to the new release branch
- and '$release_version' tag is assigned to the new release branch

As soon the version is released the release branch has to be write protected

## 3. Update master branch with $next_version beta1 version

### 3.1. create a `start-$next_version-beta1` branch \(e.g. `start-v2.7.0-beta1`) based on master branch

```bash
git pull
git checkout master
git branch start-$next_version-beta1
git checkout start-$next_version-beta1
```

Following script update `C4Version()` in C4.puml with the next beta release (e.g. `2.7.0beta1`; without `v`)

```bash
python ./.scripts/transform_files.py UpdateC4WithNextBeta
```

Following commit all changes

```bash
git checkout start-$next_version-beta1
git add -u C4.puml
git commit -m "Update version with first beta of $next_version ($release_version was created based on previous commit)"
```

And if everything is ok it can be pushed too

```bash
git checkout start-$next_version-beta1
git push -u origin start-$next_version-beta1
```

### 3.2. Create a MR into master

This is done manually \(incl. an additional check...)

## 4. Create in PlantUML/PlantUML-stdlib a MR based on `release/$release_version` branch

> !!! CHECK that the correct release branch is activated  !!!

> It is assumed that following calls are started in "C4-PlantUML repository" folder
(and not in the "plantuml-stdlib repository" folder)

The process requires following information too:

- **$deploy_repository_folder**: folder with the local `PlantUML/PlantUML-stdlib` git repositiory.  
  E.g. if it is parallel to the C4-PlantUML repository the it could be `../plantuml-stdlib`.

### 4.1. create in a PlantUML/PlantUML-stdlib fork a `C4$release_version` branch (e.g. `C4v2.6.0`)

```bash
export release_version=v2.6.0
export next_version=v2.7.0
export deployed_version=V1.2022.15

export deploy_repository_folder=../plantuml-stdlib

git pull
git checkout release/$release_version

git -C $deploy_repository_folder pull
git -C $deploy_repository_folder checkout master
git -C $deploy_repository_folder branch C4$release_version
git -C $deploy_repository_folder checkout C4$release_version
```

### 4.2. prepare the C4_*.puml, themes and README.md files

```bash
python ./.scripts/transform_files.py CreatePlantUMLStdlibC4Folder $deploy_repository_folder/stdlib/C4
```

### 4.3. Commit changes with comment "Update C4-PlantUML to $release_version"

```bash
git -C $deploy_repository_folder add -u C4/**
git -C $deploy_repository_folder commit -m "Update C4-PlantUML to $release_version"
```

### 4.4. create a MR "Update C4-PlantUML to $next_version"

This is done manually \(incl. an additional check...)


================================================
FILE: .scripts/plantuml-stdlib_C4_README.txt
================================================
---
name: C4
display_name: C4 (C4-PlantUML)
description: The C4 library enables a simple way of describing and communicate software architectures with an intuitive language.
author: Ricardo Niepel, kirchsth and contributors
version: {release version without v}
release: https://github.com/plantuml-stdlib/C4-PlantUML/tree/release/v{release version without v}
license: MIT
source: https://github.com/plantuml-stdlib/C4-PlantUML
origin: https://c4model.com
---
**C4 specific stdlib properties:**  
![name: C4](https://img.shields.io/badge/name-C4-black)
![display_name: C4 (C4-PlantUML)](https://img.shields.io/badge/display__name-C4_(C4--PlantUML)-black)  
![version: {release version without v}](https://img.shields.io/badge/version-{release version without v}-black)
[![release: https://github.com/plantuml-stdlib/C4-PlantUML/tree/release/v{release version without v}][Release Badge]][Release Link]  
![description: The C4 library enables a simple way of describing and communicate software architectures with an intuitive language.](https://img.shields.io/badge/description-The_C4_library_enables_a_simple_way_of_describing_and_communicate_software_architectures_with_an_intuitive_language.-black)  
[![license: MIT][License Badge]][License Link]
![author: Ricardo Niepel, kirchsth and contributors](https://img.shields.io/badge/author-Ricardo_Niepel,_kirchsth_and_contributors-black)  
[![source: https://github.com/plantuml-stdlib/C4-PlantUML][Source Badge]][Source Link]
[![origin: https://c4model.com][Origin Badge]][Origin Link]  

[Release Badge]: https://img.shields.io/badge/release-https://github.com/plantuml--stdlib/C4--PlantUML/tree/release/v{release version without v}-blue
[Release Link]: https://github.com/plantuml-stdlib/C4-PlantUML/tree/release/v{release version without v}
[License Badge]: https://img.shields.io/badge/license-MIT-green
[License Link]: https://github.com/plantuml-stdlib/C4-PlantUML/blob/master/LICENSE
[Source Badge]: https://img.shields.io/badge/source-https://github.com/plantuml--stdlib/C4--PlantUML-blue
[Source Link]: https://github.com/plantuml-stdlib/C4-PlantUML
[Origin Badge]: https://img.shields.io/badge/origin-https://c4model.com-blue
[Origin Link]: https://c4model.com

**Support and community:**  
[![issues: C4][Issues Badge]][Issues Link]
[![open master commits][Open Badge]][Open Link]  
[![discussions: C4][Discussions Badge]][Discussions Link]  

[Issues Badge]: https://img.shields.io/badge/issues-https://github.com/plantuml--stdlib/C4--PlantUML/issues-orange
[Issues Link]: https://github.com/plantuml-stdlib/C4-PlantUML/issues
[Open Badge]: https://img.shields.io/github/commits-difference/plantuml-stdlib/C4-PlantUML?base=release%2Fv{release version without v}&head=master&label=Open%20master%20commits&color=orange
[Open Link]: https://github.com/plantuml-stdlib/C4-PlantUML/compare/v{release version without v}...master
[Discussions Badge]: https://img.shields.io/badge/discussions-https://github.com/plantuml--stdlib/C4--PlantUML/discussions-orange
[Discussions Link]: https://github.com/plantuml-stdlib/C4-PlantUML/discussions

# C4 library (C4-PlantUML) [C4]

The C4 library enables a simple way of describing and communicate software architectures with an intuitive language.

It is the PlantUML integrated version of [C4-PlantUML](https://github.com/plantuml-stdlib/C4-PlantUML) and has the big advantage that it can be used without additional external includes.
(E.g. container diagrams can be drawn with `!include <C4/C4_Container>` and no `!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Container.puml` is required.)

## Example of usage:

```plantuml
@startuml
!include <C4/C4_Container>
LAYOUT_LEFT_RIGHT()

Person(admin, "Administrator")
System_Boundary(c1, "Sample System") {
    Container(web_app, "Web Application", "C#, ASP.NET Core 2.1 MVC", "Allows users to compare multiple Twitter timelines")
}
System(twitter, "Twitter")

Rel(admin, web_app, "Uses", "HTTPS")
Rel(web_app, twitter, "Gets tweets from", "HTTPS")

SHOW_LEGEND()
@enduml
```

<br/>

**renders following image:**

[![Example](https://www.plantuml.com/plantuml/png/JL1TQy9047o_Nx5DNn8GYyN7KanJgmMhOivAdyAPRE7WFiBT1f7I_zvDjTfxMUvcPcTk9f5KeCuQSQDTRRe6uQ4OtnNZgl2Eb7OO7iKY_rXjPRMOliXgypgRopGJOeqXUfUgncetW2JlfuuK5FcGPA8yHa9RFVdEDIeSqth4f5BPrY2Si2I3Bm5yBaxf0VULQbjcxd0FUTiQNIlItYNyLDmE82_Nm-LKiYGWt0z7yFPUz5XkZ3z4w2A62EIXzhPLJB6T8TrRoeCcmW2aBHhsYXpn-nmofHF8Uyuq1iK6pT_dhh6saPKyvrAkooJx9LtGwvePKkGhzkCpUFjV8ihvQiTTpgRBP-vnWgxX-dy0)](https://www.plantuml.com/plantuml/uml/JL1TQy9047o_Nx5DNn8GYyN7KanJgmMhOivAdyAPRE7WFiBT1f7I_zvDjTfxMUvcPcTk9f5KeCuQSQDTRRe6uQ4OtnNZgl2Eb7OO7iKY_rXjPRMOliXgypgRopGJOeqXUfUgncetW2JlfuuK5FcGPA8yHa9RFVdEDIeSqth4f5BPrY2Si2I3Bm5yBaxf0VULQbjcxd0FUTiQNIlItYNyLDmE82_Nm-LKiYGWt0z7yFPUz5XkZ3z4w2A62EIXzhPLJB6T8TrRoeCcmW2aBHhsYXpn-nmofHF8Uyuq1iK6pT_dhh6saPKyvrAkooJx9LtGwvePKkGhzkCpUFjV8ihvQiTTpgRBP-vnWgxX-dy0)


================================================
FILE: .scripts/plantuml-stdlib_README_section.txt
================================================
<!-- start C4 section -->
![name: C4](https://img.shields.io/badge/name-C4-black)
![display_name: C4 (C4-PlantUML)](https://img.shields.io/badge/display__name-C4_(C4--PlantUML)-black)
[![version: {release version without v}][Version Badge]][Version Link]
![license: MIT](https://img.shields.io/badge/license-MIT-green)
![author: Ricardo Niepel, kirchsth and contributors](https://img.shields.io/badge/author-Ricardo_Niepel,_kirchsth_and_contributors-black)  
[![all properties see: ./stdlib/C4/README.md][See Badge]][See Link]  

[Version Badge]: https://img.shields.io/badge/version-{release version without v}-blue
[Version Link]: https://github.com/plantuml-stdlib/C4-PlantUML/tree/release/v{release version without v}
[See Badge]: https://img.shields.io/badge/all_stdlib_specific_properties_see-./stdlib/C4/README.md-blue
[See Link]: ./stdlib/C4/README.md
<!-- end C4 section -->

================================================
FILE: .scripts/readme_release_header.txt
================================================
[![release][Release Badge]][Release Page]
[![license MIT][License Badge]][License Page]
 &nbsp; &nbsp; &nbsp;
[![integrated in PlantUML][Integrated Badge]][Integrated Page]
 &nbsp; &nbsp; &nbsp;
[![commits since][Commits Since Badge]][Commit Page]

[Release Badge]: https://img.shields.io/badge/release-{release version}-blue
[Release Page]: https://github.com/plantuml-stdlib/C4-PlantUML/releases/{release version}
[License Badge]: https://img.shields.io/github/license/plantuml-stdlib/C4-PlantUML
[License Page]: https://github.com/plantuml-stdlib/C4-PlantUML/blob/master/LICENSE
[Integrated Badge]: https://img.shields.io/badge/C4--PlantUML%20%20{release version}%20integrated%20in%20PlantUML%20Standard%20Library-{deploy into version}-orange
[Integrated Page]: https://plantuml.com/stdlib#062f75176513a666

[Commits Since Badge]: https://img.shields.io/github/commits-since/plantuml-stdlib/C4-PlantUML/latest?label=new%20unreleased%20changes%20in%20master%20branch
[Commit Page]: https://github.com/plantuml-stdlib/C4-PlantUML/commits

# C4-PlantUML ({release version})

================================================
FILE: .scripts/transform_files.py
================================================
#!/usr/bin/python

# If a new version is released, version number and other topics of the source
# has to be updated.
# This script simplifies the update from master to a specific release (branch/tag)

# IMPORTANT:
#
# - It is assumed that the script is stared in repository root with relative path
#   python ./.scripts/transform_files <transformation>
#
# - It is assumed that
#   - "release_version"      (e.g. "v2.6.0"),
#   - "next_version"         (e.g. "v2.7.0")
#                            If it is undefined it will be calculated via the release_version.
#                            It is the next patch (release patch!=0) or subversion (release patch==0).
#   - and "deployed_version" This is the next "plantuml(/plantuml-stdlib)" version
#                            which should be updated with this release (e.g. "V1.2023.2")
#                            If it is undefined it is calculated via the running PlantUML web service
#   are defined as environment variable (or they will be calculated if possible)

# Supported transformations/functions are
#
# - CalculateDeployedVersion
#
# - UpdateC4WithReleaseVersion
# - UpdateAllIncludes
# - UpdateAllImages
# - ReplaceREADMEHeader
# - UpdateC4WithNextBeta
#
# - CreatePlantUMLStdlibC4Folder [<plantuml-stdlib/C4 target folder>]

import os
import re
import sys
import glob

import zlib
import base64
import string

import requests

# >>>>> plant uml decoder from ryardley/plant_uml_decoder.py
# >>>>> https://gist.github.com/ryardley/64816f5097003a35f9726aab676920d0

plantuml_alphabet = (
    string.digits + string.ascii_uppercase + string.ascii_lowercase + "-_"
)
base64_alphabet = string.ascii_uppercase + string.ascii_lowercase + string.digits + "+/"
b64_to_plantuml = bytes.maketrans(
    base64_alphabet.encode("utf-8"), plantuml_alphabet.encode("utf-8")
)
plantuml_to_b64 = bytes.maketrans(
    plantuml_alphabet.encode("utf-8"), base64_alphabet.encode("utf-8")
)


def plantuml_encode(plantuml_text):
    """zlib compress the plantuml text and encode it for the plantuml server"""
    zlibbed_str = zlib.compress(plantuml_text.encode("utf-8"))
    compressed_string = zlibbed_str[2:-4]
    return (
        base64.b64encode(compressed_string).translate(b64_to_plantuml).decode("utf-8")
    )


def plantuml_decode(plantuml_url):
    """decode plantuml encoded url back to plantuml text"""
    data = base64.b64decode(plantuml_url.translate(plantuml_to_b64).encode("utf-8"))
    dec = zlib.decompressobj()  # without check the crc.
    header = b"x\x9c"
    return dec.decompress(header + data).decode("utf-8")


# <<<<< plant uml decoder from ryardley/plant_uml_decoder.py


def read_environment_variable(env_var, is_required=True):
    if env_var not in os.environ:
        if is_required:
            sys.stderr.write(
                f"the required environment variable {env_var} is not defined\n"
            )
            sys.exit(3)
        else:
            return ""
    return os.environ[env_var]


# It is assumed that "release_version", "next_version" and "deployed_version"
# are defined as environment variable.
# If next_version is not defined then it is calculated based on release_version
# If deployed_version is not defined then it is calculated based on the running PlantUML web server
def read_environment_variables():
    global release_version
    release_version = read_environment_variable("release_version")
    if release_version[0] != "v":
        sys.stderr.write(
            f"release version {release_version} has to start with 'v' (and use a format like vX.Y.Z)"
        )
        sys.exit(2)
    release_match = re.search(
        r"^v(?P<v1>[0-9]+)\.(?P<v2>[0-9]+)\.(?P<v3>[0-9]+)$", release_version
    )
    if not release_match:
        sys.stderr.write(
            f"release version {release_version} has to use a format like v[0-9]+.[0-9]+.[0-9]+, e.g. v2.6.0)"
        )
        sys.exit(2)

    global next_version
    next_version = read_environment_variable("next_version", False)
    if next_version == "":
        v1 = int(release_match["v1"])
        v2 = int(release_match["v2"])
        v3 = int(release_match["v3"])
        next_version = calculate_next_version(release_version, v1, v2, v3)

    global deployed_version
    deployed_version = read_environment_variable("deployed_version", False)
    if deployed_version == "":
        deployed_version = read_next_plantuml_version()


def replace_first_regex_in_file(file_path, search, replace):
    r = re.compile(search)
    with open(file_path, "r") as file:
        filedata = file.read()
        filedata = r.sub(replace, filedata, 1)
    with open(file_path, "w") as file:
        file.write(filedata)


def replace_in_file(file_path, orig, replace):
    with open(file_path, "r") as file:
        filedata = file.read()
        filedata = filedata.replace(orig, replace)
    with open(file_path, "w") as file:
        file.write(filedata)


def replace_first_regex_copy_file(
    source_path, target_path, compiled_search_regex, replace
):
    with open(source_path, "r") as file:
        filedata = file.read()
    filedata = compiled_search_regex.sub(replace, filedata, 1)
    with open(target_path, "w") as file:
        file.write(filedata)


def replace_regex_copy_file(
    source_path, target_path, compiled_search_regex, replace
):
    with open(source_path, "r") as file:
        filedata = file.read()
    filedata = compiled_search_regex.sub(replace, filedata)
    with open(target_path, "w") as file:
        file.write(filedata)


# Calculates the next version (inclusive starting v) based on the give version values.
# If v3 == 0 then v2 is increased otherwise v3
def calculate_next_version(release, v1, v2, v3):
    print(f"calculates the next_version based on given release_version {release} ...")
    if v3 == 0:
        v2 = v2 + 1
    else:
        v3 = v3 + 1
    version = f"v{v1}.{v2}.{v3}"
    print(
        f"The calculated next_version = {version}. It can be used as next_version environment variable with following statement"
    )
    print(f"    export next_version={version}")
    return version


# Calculates the next released PlantUML version that it can be used as deployed_version environment variable
# based on http://www.plantuml.com/plantuml/svg/SoWkIImgAStDuSf8JKn9BL9GBKijAixCpzFGv798pKi1oW00 response
# This function returns "V" + the parsed version number  (e.g. "V1.2022.16")
def read_next_plantuml_version():
    # the idea is that the PlantUML version is extracted out of the svg result of "header %version()".
    # %version() stores beta of next version.
    # the returned SVG response stores the version inclusive beta in a text element; e.fg. "...<text ...>1.2022.16beta2</text>..."
    # and this function returns "V" + the parsed version number  (e.g. "V1.2022.16")
    print(
        "calculates the next deployed_version based on the running PlantUML web server response ..."
    )
    resp = requests.get(
        "http://www.plantuml.com/plantuml/svg/SoWkIImgAStDuSf8JKn9BL9GBKijAixCpzFGv798pKi1oW00"
    )
    if resp.status_code != 200:
        sys.stderr.write(
            "cannot read the svg response (with the next release version) from the PlantUML web server; please check http://www.plantuml.com/plantuml/svg/SoWkIImgAStDuSf8JKn9BL9GBKijAixCpzFGv798pKi1oW00"
        )
        sys.exit(4)

    # As an alternative it could be calculated via https://www.planttext.com/api/plantuml/txt/SoWkIImgIIvApSz9vL8jIoqgpipFqz3aSaZDIu680W00
    # It would return "1.2022.15beta6\n". (details see https://forum.plantuml.net/17179/ascii-art-title-produces-java-lang-illegalstateexception?show=17184#a17184)

    svgbody = resp.content
    svg = svgbody.decode("utf-8")
    # this regex ignore beta2 of the text section too: "<text [^>]+>(?P<version>[0-9\.]+)"
    r = re.compile(r"<text [^>]+>(?P<version>[0-9\.]+)")
    v = r.search(svg)["version"]
    version = "V" + v

    print(
        f"The next PlantUML version = {version}. It can be used as deployed_version environment variable with following statement"
    )
    print(f"    export deployed_version={version}")

    return version


def update_c4_release_version():
    # $c4Version is defined without starting 'v'
    print(
        f"updating C4Version() definition in C4.puml with new release_version {release_version[1:]} ..."
    )
    replace_first_regex_in_file(
        "C4.puml", r'!\$c4Version  =  ".+"', f'!$c4Version  =  "{release_version[1:]}"'
    )
    print("C4Version() updated")


def update_c4_next_beta_version():
    # $c4Version is defined without starting 'v'
    print(
        f"updating C4Version() definition in C4.puml with new release_version {next_version[1:]} ..."
    )
    replace_first_regex_in_file(
        "C4.puml", r'!\$c4Version  =  ".+"', f'!$c4Version  =  "{next_version[1:]}beta1"'
    )
    print("C4Version() updated")


def update_all_includes():
    # reference tag version is with starting 'v'
    print(f"updating include/theme paths with new tag version {release_version} ...")
    files = glob.glob("./**/*", recursive=True)
    for file in files:
        if file.endswith(".puml") or file.endswith(".md"):
            print(f"    {file}")
            replace_in_file(
                file,
                "!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/",
                f"!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/{release_version}/",
            )
            replace_in_file(
                file,
                "from https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/",
                f"from https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/{release_version}/",
            )
            replace_in_file(
                file,
                "](https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/themes/",
                f"](https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/{release_version}/themes/",
            )
    print(f"include/theme paths updated")


def process_url_match(m: re.Match[str]):
    base = m.group("base")
    out_format = m.group("format")
    base64 = m.group("base64")
    text = plantuml_decode(base64)

    new_path = f"!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/{release_version}/"
    replaced = text.replace(
        "!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/",
        new_path,
    )
    replaced = replaced.replace(
        "from https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/",
        f"from https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/{release_version}/",
    )
    replaced = replaced.replace(
        "](https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/themes/",
        f"](https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/{release_version}/themes/",
    )

    if new_path not in replaced:
        global found_not_replaced_include
        found_not_replaced_include = True
        print(
            f"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\ninclude could not be replaced in base64\n{base64}\nthe extracted source is\n{text}\n------------------------------------"
        )
        updated_base64 = base64
    else:
        updated_base64 = plantuml_encode(replaced)

    return f"{base}/{out_format}/{updated_base64}"


def replace_images_in_file(file_path):
    # extract all base64 entries
    r = re.compile(
        "(?P<base>https://www\\.plantuml\\.com/plantuml)/(?P<format>(png|uml|svg))/(?P<base64>([^ )]*))"
    )
    with open(file_path, "r") as file:
        filedata = file.read()
        filedata = r.sub(process_url_match, filedata)
    with open(file_path, "w") as file:
        file.write(filedata)


def update_all_images():
    # reference tag version is with starting 'v'
    print(
        f"updating include paths with new tag version {release_version} in images of all *.md files ..."
    )

    global found_not_replaced_include
    found_not_replaced_include = False

    files = glob.glob("./**/*", recursive=True)
    for file in files:
        if file.endswith(".md"):
            print(f"    {file}")
            replace_images_in_file(file)

    if found_not_replaced_include:
        sys.stderr.write(
            "!!!!!! not all images urls could be updated in the *.md files (details see log)\n"
        )
        sys.exit(3)

    print("include paths in images updated")


def replace_readme_header():
    print(f"updating README.md with new version {release_version} and badges ...")
    # remove whole part before "# C4-PlantUML" in README.md
    r = re.compile(r"[^\#]+# C4-PlantUML", re.M)
    with open("README.md", "r") as file:
        filedata = file.read()
    filedata = r.sub("# C4-PlantUML", filedata)

    with open("./.scripts/readme_release_header.txt", "r") as header_file:
        header = header_file.read()
        header = header.replace("{release version}", release_version).replace(
            "{deploy into version}", deployed_version
        )

    filedata = filedata.replace("# C4-PlantUML", header)

    with open("README.md", "w") as file:
        file.write(filedata)

    print("release README.md updated with new version and badges")


def create_plantuml_stdlib_c4_folder(target_path):
    print(
        f"prepare C4 folder of plantuml-stdlib repository in folder {target_path} ..."
    )
    # remove whole begin inclusive "!endif" in the specific C4_*.puml files
    inclusive_endif = re.compile(r"'[^']+!endif", re.M)

    os.makedirs(target_path, exist_ok=True)
    replace_first_regex_copy_file(
        "C4.puml",
        os.path.join(target_path, "C4.puml"),
        re.compile("DOES NOT EXIST"),
        "DOES NOT EXIST",
    )
    replace_first_regex_copy_file(
        "C4_Component.puml",
        os.path.join(target_path, "C4_Component.puml"),
        inclusive_endif,
        "!include <C4/C4_Container>",
    )
    replace_first_regex_copy_file(
        "C4_Container.puml",
        os.path.join(target_path, "C4_Container.puml"),
        inclusive_endif,
        "!include <C4/C4_Context>",
    )
    replace_first_regex_copy_file(
        "C4_Context.puml",
        os.path.join(target_path, "C4_Context.puml"),
        inclusive_endif,
        "!include <C4/C4>",
    )
    replace_first_regex_copy_file(
        "C4_Deployment.puml",
        os.path.join(target_path, "C4_Deployment.puml"),
        inclusive_endif,
        "!include <C4/C4_Container>",
    )
    replace_first_regex_copy_file(
        "C4_Dynamic.puml",
        os.path.join(target_path, "C4_Dynamic.puml"),
        inclusive_endif,
        "!include <C4/C4_Component>",
    )
    replace_first_regex_copy_file(
        "C4_Sequence.puml",
        os.path.join(target_path, "C4_Sequence.puml"),
        inclusive_endif,
        "!include <C4/C4_Component>",
    )

    themes_path = target_path+"/themes"
    os.makedirs(themes_path, exist_ok=True)
    paths = glob.glob("./themes/puml-theme-C4_*.puml")
    for path in paths:
        file = os.path.basename(path)
        if file == "puml-theme-C4_FirstTest.puml":
            continue
        # print(f"    {path}")
        replace_first_regex_copy_file(
            path,
            os.path.join(themes_path, file),
            re.compile("DOES NOT EXIST"),
            "DOES NOT EXIST",
        )

    replace_regex_copy_file(
        "./.scripts/plantuml-stdlib_C4_README.txt",
        os.path.join(target_path, "README.md"),
        re.compile(r"\{release version without v\}"),
        release_version[1:],
    )

    with open("./.scripts/plantuml-stdlib_README_section.txt", "r") as f:
        section = f.read()
    updatedSection = re.sub(r"\{release version without v\}", release_version[1:], section)
    with open(os.path.join(target_path, "./../../README.md"), "r") as f:
        content = f.read()
    pattern = r'<!-- start C4 section -->.*?<!-- end C4 section -->'
    updated_content = re.sub(pattern, updatedSection, content, flags=re.DOTALL)
    with open(os.path.join(target_path, "./../../README.md"), "w") as f:
        f.write(updated_content)

    print(f"all C4 related plantuml-stdlib files copied into {target_path}.")


if not (
    len(sys.argv) == 2
    or (len(sys.argv) == 3 and sys.argv[1] == "CreatePlantUMLStdlibC4Folder")
):
    u = "Usage: python ./.scripts/transform_files.py <transformation>\n"
    sys.stderr.write("Usage: python ./.scripts/transform_files.py <transformation>\n")
    sys.exit(1)

if sys.argv[0] != "./.scripts/transform_files.py":
    u = "script has to be started in repository root with relative path: ./.scripts/transform_files <transformation>\n"
    sys.stderr.write(u)
    sys.exit(1)

if sys.argv[1] == "UpdateC4WithReleaseVersion":
    read_environment_variables()
    update_c4_release_version()
elif sys.argv[1] == "UpdateAllIncludes":
    read_environment_variables()
    update_all_includes()
elif sys.argv[1] == "UpdateAllImages":
    read_environment_variables()
    update_all_images()
elif sys.argv[1] == "ReplaceREADMEHeader":
    read_environment_variables()
    replace_readme_header()
elif sys.argv[1] == "UpdateC4WithNextBeta":
    read_environment_variables()
    update_c4_next_beta_version()
elif sys.argv[1] == "CalculateDeployedVersion":
    calculated_deployed_version = read_next_plantuml_version()
elif sys.argv[1] == "CreatePlantUMLStdlibC4Folder":
    read_environment_variables()
    if len(sys.argv) == 3:
        create_plantuml_stdlib_c4_folder(sys.argv[2])
    else:
        create_plantuml_stdlib_c4_folder(".plantuml_stdlib_c4")
else:
    sys.stderr.write(f"{sys.argv[1]} is an unsupported transformation\n")
    sys.exit(1)


================================================
FILE: C4.puml
================================================
' C4-PlantUML

' Global pre-settings
' ##################################
' NEW_C4_STYLE
'   If NEW_C4_STYLE is set BEFORE the first C4_* file is loaded, new C4 layout style is used
'   NEW_C4_STYLE can be set via
'     !NEW_C4_STYLE = 1
'   or with additional command line argument -DNEW_C4_STYLE=1
!global NEW_C4_STYLE ?= 0

' ROUNDED_STYLE
'   If ROUNDED_STYLE is set BEFORE the first C4_* file is loaded, rectangles with rounded corners are used as default shape
'   ROUNDED_STYLE can be set via
'     !ROUNDED_STYLE = 1
'   or with additional command line argument -DROUNDED_STYLE=1
!if (NEW_C4_STYLE == 1)
  !global ROUNDED_STYLE ?= 1
!else
  !global ROUNDED_STYLE ?= 0
!endif

' ENABLE_ALL_PLANT_ELEMENTS
'   If ENABLE_ALL_PLANT_ELEMENTS is set BEFORE the first C4_* file is loaded, nearly "all" PlantUML elements can be used like
'     Component(StorageA, "Storage A ", $baseShape="storage")
'   ENABLE_ALL_PLANT_ELEMENTS can be set via
'     !ENABLE_ALL_PLANT_ELEMENTS = 1
'   or with additional command line argument -DENABLE_ALL_PLANT_ELEMENTS=1

' NO_LAY_ROTATE
' C4-PlantUML v2.12 fixed a missing rotation bug in Lay_* calls in combination with LAYOUT_LANDSCAPE() call
' (details see https://github.com/plantuml-stdlib/C4-PlantUML/issues/376)
' If older diagrams should remain unchanged the bugfix can be deactivated with following statement
'     !NO_LAY_ROTATE = 1
' or with follwing additional command line argument
'     -DNO_LAY_ROTATE=1
' like
'    java -jar plantuml.jar -DNO_LAY_ROTATE=1 ...
!global NO_LAY_ROTATE ?= 0

'Version
' ##################################
!function C4Version()
  ' 2 spaces and ' are used as unique marker, that the release scripts makes the correct version update
  !$c4Version  =  "2.14.0beta1"
  !return $c4Version
!end function

!procedure C4VersionDetails()
rectangle C4VersionDetailsArea <<legendArea>> [
| PlantUML | **%version()** |
| C4-PlantUML | **C4Version()** |
]
!end procedure

' Colors
' ##################################
!$ELEMENT_FONT_COLOR ?= "#FFFFFF"

!$ARROW_COLOR ?= "#666666"
!$ARROW_FONT_COLOR ?= $ARROW_COLOR

!$BOUNDARY_COLOR ?= "#444444"
!$BOUNDARY_BG_COLOR ?= "transparent"
!$BOUNDARY_BORDER_STYLE ?= "dashed"
' boundary symbols written in the same line, typically only 50% of the size in element
!$BOUNDARY_IMAGE_SIZE_FACTOR ?= 0.5
!$BOUNDARY_DESCR_MAX_CHAR_WIDTH ?= 35

!$LEGEND_TITLE_COLOR ?= "#000000"
!$LEGEND_FONT_COLOR ?= "#FFFFFF"
!$LEGEND_BG_COLOR ?= "transparent"
!$LEGEND_BORDER_COLOR ?= "transparent"
' %darken(darkkhaki,50), #khaki
!$LEGEND_DARK_COLOR ?= "#66622E"
!$LEGEND_LIGHT_COLOR ?= "#khaki"

!$SKETCH_BG_COLOR ?= "#EEEBDC"
!$SKETCH_FONT_COLOR ?= ""
!$SKETCH_WARNING_COLOR ?= "red"
!$SKETCH_FONT_NAME ?= "Comic Sans MS"

' Labels
' ##################################

!$BOUNDARY_LEGEND_TEXT ?= "boundary"

!$LEGEND_TITLE_TEXT ?= "Legend"
!$LEGEND_SHADOW_TEXT ?= "shadow"
!$LEGEND_NO_SHADOW_TEXT ?= "no shadow"
!$LEGEND_NO_FONT_BG_TEXT ?= "last text and back color"
!$LEGEND_NO_FONT_TEXT ?= "last text color"
!$LEGEND_NO_BG_TEXT ?= "last back color"
!$LEGEND_NO_LINE_TEXT ?= "last line color"
!$LEGEND_SHARP_CORNER ?= "box"
!$LEGEND_ROUNDED_BOX ?= "rounded box"
!$LEGEND_EIGHT_SIDED ?= "eight sided"
!$LEGEND_DOTTED_LINE ?= "dotted"
!$LEGEND_DASHED_LINE ?= "dashed"
!$LEGEND_BOLD_LINE ?= "bold"
!$LEGEND_SOLID_LINE ?= "solid"

!$LEGEND_BOUNDARY ?= "boundary"
!$LEGEND_BOUNDARY_PRE_PART ?= ""
!$LEGEND_BOUNDARY_POST_PART ?= " " + $LEGEND_BOUNDARY

' ignore (boundary) transparent atm, that the legend is smaller
' !$LEGEND_BOUNDARY_TRANSPARENT_INCL_COMA ?= "transparent, "
!$LEGEND_BOUNDARY_TRANSPARENT_INCL_COMA ?= ""
' (boundary) dashed should not be ignored atm
!$LEGEND_BOUNDARY_DASHED_INCL_COMA ?= "dashed, "
' !$LEGEND_BOUNDARY_DASHED_INCL_COMA ?= ""

!$LEGEND_THICKNESS ?= "thickness"

!$SKETCH_FOOTER_WARNING ?= "Warning:"
!$SKETCH_FOOTER_TEXT ?= "Created for discussion, needs to be validated"

' Styling
' ##################################

!$STEREOTYPE_FONT_SIZE ?= 12
!global $TRANSPARENT_STEREOTYPE_FONT_SIZE = $STEREOTYPE_FONT_SIZE/2
!$TECHN_FONT_SIZE ?= 12

!$ARROW_FONT_SIZE ?= 12

!$LEGEND_DETAILS_SMALL_SIZE ?= 10
!$LEGEND_DETAILS_NORMAL_SIZE ?= 14
!global $LEGEND_DETAILS_SIZE = $LEGEND_DETAILS_SMALL_SIZE

' element symbols typically 4 times too big in legend
!$LEGEND_IMAGE_SIZE_FACTOR ?= 0.25

!$ROUNDED_BOX_SIZE ?= 25
!$EIGHT_SIDED_SIZE ?= 18

' Default element wrap width (of an element)
!$DEFAULT_WRAP_WIDTH ?= 200
' Maximum size in pixels, of a message (in a sequence diagram?)
!$MAX_MESSAGE_SIZE ?= 150
' PlantUML supports no DETERMINISTIC/automatic line breaks of "PlantUML line" (C4 Relationships)
' therefore Rel...() implements an automatic line break based on spaces (like in all other objects).
' If a $type contains \n then these are used (and no automatic space based line breaks are done)
' $REL_TECHN_MAX_CHAR_WIDTH defines the automatic line break position
!$REL_TECHN_MAX_CHAR_WIDTH ?= 35
!$REL_DESCR_MAX_CHAR_WIDTH ?= 32

' internal
' ##################################

!global $SHARP_CORNER = "sharpCorner"
!global $ROUNDED_BOX = "roundedBox"
!global $EIGHT_SIDED = "eightSided"
!if (ROUNDED_STYLE == 1)
  !$DEFAULT_SHAPE ?= $ROUNDED_BOX
!else
  !$DEFAULT_SHAPE ?= $SHARP_CORNER
!endif

!global $DOTTED_LINE = "dotted"
!global $DASHED_LINE = "dashed"
!global $BOLD_LINE = "bold"
' solid is not defined in plantUML, but works as reset of all other styles too
!global $SOLID_LINE = "solid"

!global $LEGEND_DETAILS_NONE = "none"
!global $LEGEND_DETAILS_NORMAL = "normal"
!global $LEGEND_DETAILS_SMALL = "small"

skinparam defaultTextAlignment center

skinparam wrapWidth $DEFAULT_WRAP_WIDTH
skinparam maxMessageSize $MAX_MESSAGE_SIZE

skinparam LegendFontColor $LEGEND_FONT_COLOR
skinparam LegendBackgroundColor $LEGEND_BG_COLOR
skinparam LegendBorderColor $LEGEND_BORDER_COLOR

skinparam rectangle<<legendArea>> {
    backgroundcolor $LEGEND_BG_COLOR
    bordercolor $LEGEND_BORDER_COLOR
}

skinparam rectangle {
    StereotypeFontSize $STEREOTYPE_FONT_SIZE
}

skinparam database {
    StereotypeFontSize $STEREOTYPE_FONT_SIZE
}

skinparam queue {
    StereotypeFontSize $STEREOTYPE_FONT_SIZE
}

skinparam participant {
    StereotypeFontSize $STEREOTYPE_FONT_SIZE
}

skinparam arrow {
    Color $ARROW_COLOR
    FontColor $ARROW_FONT_COLOR
    FontSize $ARROW_FONT_SIZE
}

skinparam person {
    StereotypeFontSize $STEREOTYPE_FONT_SIZE
}

skinparam actor {
    StereotypeFontSize $STEREOTYPE_FONT_SIZE
    style awesome
}

!if %variable_exists("ENABLE_ALL_PLANT_ELEMENTS")
skinparam agent {
    StereotypeFontSize $STEREOTYPE_FONT_SIZE
}
skinparam artifact {
    StereotypeFontSize $STEREOTYPE_FONT_SIZE
}
skinparam boundary {
    StereotypeFontSize $STEREOTYPE_FONT_SIZE
}
skinparam card {
    StereotypeFontSize $STEREOTYPE_FONT_SIZE
}
skinparam circle {
    StereotypeFontSize $STEREOTYPE_FONT_SIZE
}
skinparam cloud {
    StereotypeFontSize $STEREOTYPE_FONT_SIZE
}
skinparam collections {
    StereotypeFontSize $STEREOTYPE_FONT_SIZE
}
skinparam control {
    StereotypeFontSize $STEREOTYPE_FONT_SIZE
}
skinparam entity {
    StereotypeFontSize $STEREOTYPE_FONT_SIZE
}
skinparam file {
    StereotypeFontSize $STEREOTYPE_FONT_SIZE
}
skinparam folder {
    StereotypeFontSize $STEREOTYPE_FONT_SIZE
}
skinparam frame {
    StereotypeFontSize $STEREOTYPE_FONT_SIZE
}
skinparam hexagon {
    StereotypeFontSize $STEREOTYPE_FONT_SIZE
}
skinparam interface {
    StereotypeFontSize $STEREOTYPE_FONT_SIZE
}
skinparam label {
    StereotypeFontSize $STEREOTYPE_FONT_SIZE
}
skinparam stack {
    StereotypeFontSize $STEREOTYPE_FONT_SIZE
}
skinparam storage {
    StereotypeFontSize $STEREOTYPE_FONT_SIZE
}
skinparam usecase {
    StereotypeFontSize $STEREOTYPE_FONT_SIZE
}
skinparam person {
    StereotypeFontSize $STEREOTYPE_FONT_SIZE
}
!endif

' Some boundary skinparams have to be set as package skinparams too (PlantUML uses internal packages)
' UpdateBoundaryStyle() called in boundary section below
skinparam rectangle<<boundary>> {
    StereotypeFontSize $TRANSPARENT_STEREOTYPE_FONT_SIZE
    StereotypeFontColor $BOUNDARY_BG_COLOR
    BorderStyle $BOUNDARY_BORDER_STYLE
}

skinparam package {
    StereotypeFontSize $TRANSPARENT_STEREOTYPE_FONT_SIZE
    StereotypeFontColor $BOUNDARY_BG_COLOR
    FontStyle plain
    BackgroundColor $BOUNDARY_BG_COLOR
}

' PlantUML compatibility utilities
' ##################################

' PlantUML v1.2025.1beta6 introduced a new %breakline() function.
' This should be used instead of the old %newline(), if a command ends.
' (%newline() should be only used in multiline labels,...)
!function $bl()
  !if (%function_exists("%breakline"))
    !return %breakline()
  !endif
  !return %newline()
!endfunction

' Legend and Tags
' ##################################
!global $tagDefaultLegend = ""
!global $tagCustomLegend = ""

' rel specific
!unquoted function $toStereos($tags)
  !if (%strlen($tags) == 0)
    !return ''
  !endif
  !$stereos = ''
  !$brPos = %strpos($tags, "+")
  !while ($brPos >= 0)
    !$tag = %substr($tags, 0, $brPos)
    !$stereos = $stereos + '<<' + $tag + '>>'
%set_variable_value("$" + $tag + "_LineLegend", %true())
    !$tags = %substr($tags, $brPos+1)
    !$brPos = %strpos($tags, "+")
  !endwhile
  !if (%strlen($tags) > 0)
    !$stereos = $stereos + '<<' + $tags + '>>'
%set_variable_value("$" + $tags + "_LineLegend", %true())
  !endif
  !return $stereos
!endfunction

' if $sprite/$techn is an empty argument, try to calculate it via the defined $tag
!unquoted function $toRelArg($arg, $tags, $varPostfix)
  !if ($arg > "")
    !return $arg
  !endif

  !if (%strlen($tags) == 0)
       !return $arg
  !endif
  !$brPos = %strpos($tags, "+")
  !while ($brPos >= 0)
    !$tag = %substr($tags, 0, $brPos)
    !$newArg = %get_variable_value("$" + $tag + $varPostfix)
    !if ($newArg > "")
       !return $newArg
    !endif
    !$tags = %substr($tags, $brPos+1)
    !$brPos = %strpos($tags, "+")
  !endwhile
  !if (%strlen($tags) > 0)
    !$newArg = %get_variable_value("$" + $tags + $varPostfix)
    !if ($newArg > "")
       !return $newArg
    !endif
  !endif
  !return $arg
!endfunction

' element specific (unused are hidden based on mask)
!unquoted function $toStereos($elementType, $tags)
  !if (%strlen($tags) == 0)
    !$stereos = '<<' + $elementType + '>>'
%set_variable_value("$" + $elementType + "Legend", %true())
    !return $stereos
  !endif
  !$stereos = ''
  !$mask = $resetMask()
  !$brPos = %strpos($tags, "+")
  !while ($brPos >= 0)
    !$tag = %substr($tags, 0, $brPos)
    !$stereos = $stereos + '<<' + $tag + '>>'
    !$mergedMask = $combineMaskWithTag($mask, $tag)
    !if ($mergedMask != $mask)
%set_variable_value("$" + $tag + "Legend", %true())
      !$mask = $mergedMask
    !endif
    !$tags = %substr($tags, $brPos+1)
    !$brPos = %strpos($tags, "+")
  !endwhile
  !if (%strlen($tags) > 0)
    !$stereos = $stereos + '<<' + $tags + '>>'
    !$mergedMask = $combineMaskWithTag($mask, $tags)
    !if ($mergedMask != $mask)
%set_variable_value("$" + $tags + "Legend", %true())
      !$mask = $mergedMask
    !endif
  !endif
  ' has to be last, otherwise PlantUML overwrites all tag specific skinparams
  !$stereos = $stereos + '<<' + $elementType + '>>'
  !$mergedMask = $combineMaskWithTag($mask, $elementType)
  !if ($mergedMask != $mask)
%set_variable_value("$" + $elementType + "Legend", %true())
    !$mask = $mergedMask
  !endif
  !return $stereos
!endfunction

' if $sprite/$techn is an empty argument, try to calculate it via the defined $tag
!unquoted function $toElementArg($arg, $tags, $varPostfix, $elementType)
  !if ($arg > "")
    !return $arg
  !endif

  !if (%strlen($tags) == 0)
    !$newArg = %get_variable_value("$" + $elementType + $varPostfix)
    !if ($newArg > "")
       !return $newArg
    !else
       !return $arg
    !endif
  !endif
  !$brPos = %strpos($tags, "+")
  !while ($brPos >= 0)
    !$tag = %substr($tags, 0, $brPos)
    !$newArg = %get_variable_value("$" + $tag + $varPostfix)
    !if ($newArg > "")
       !return $newArg
    !endif
    !$tags = %substr($tags, $brPos+1)
    !$brPos = %strpos($tags, "+")
  !endwhile
  !if (%strlen($tags) > 0)
    !$newArg = %get_variable_value("$" + $tags + $varPostfix)
    !if ($newArg > "")
       !return $newArg
    !endif
    !$newArg = %get_variable_value("$" + $elementType + $varPostfix)
    !if ($newArg > "")
       !return $newArg
    !endif
  !endif
  !return $arg
!endfunction

' if $value is empty try to load it via variable, optional can it store the calculated value
!function $restoreEmpty($elementType, $property, $value, $store)
  !$var = "$" + $elementType + "Restore" + $property
  !if ($value == "")
    !$value = %get_variable_value($var)
  !elseif ($store)
    %set_variable_value($var, $value)
  !endif
  !return $value
!endfunction

' clear the restore property
!function $clearRestore($elementType, $property)
  !$var = "$" + $elementType + "Restore" + $property
  %set_variable_value($var, "")
  !return ""
!endfunction

!function $elementTagSkinparams($element, $tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, $shape, $borderStyle, $borderThickness)
  !$elementSkin = "skinparam " + $element + "<<" + $tagStereo + ">> {" + $bl()
  !if ($fontColor != "")
    !if (%strpos($tagStereo, "boundary") < 0)
      !$elementSkin = $elementSkin + "    StereotypeFontColor " + $fontColor + $bl()
    !endif
    !$elementSkin = $elementSkin + "    FontColor " + $fontColor + $bl()
  !endif
  !if ($bgColor != "")
    !$elementSkin = $elementSkin + "    BackgroundColor " + $bgColor + $bl()
  !endif
  !if ($borderColor != "")
    !$elementSkin = $elementSkin + "    BorderColor " + $borderColor+ $bl()
  !endif
  !if ($shadowing == "true")
    !$elementSkin = $elementSkin + "    Shadowing<<" + $tagStereo + ">> " + "true" + $bl()
  !endif
  !if ($shadowing == "false")
    !$elementSkin = $elementSkin + "    Shadowing<<" + $tagStereo + ">> " + "false" + $bl()
  !endif
  ' only rectangle supports shape(d corners), define both skinparam that overlays are working
  !if ($shape != "" && $element == "rectangle")
    !if ($shape == $SHARP_CORNER)
      !$elementSkin = $elementSkin + "    RoundCorner " + "0" + $bl()
      !$elementSkin = $elementSkin + "    DiagonalCorner " + "0" + $bl()
    !elseif ($shape == $ROUNDED_BOX)
      !$elementSkin = $elementSkin + "    RoundCorner " + $ROUNDED_BOX_SIZE+ $bl()
      !$elementSkin = $elementSkin + "    DiagonalCorner " + "0" + $bl()
    !elseif ($shape == $EIGHT_SIDED)
      !$elementSkin = $elementSkin + "    RoundCorner " + "0" + $bl()
      !$elementSkin = $elementSkin + "    DiagonalCorner " + $EIGHT_SIDED_SIZE+ $bl()
    !endif
  !endif
  !if ($borderStyle != "")
    !$elementSkin = $elementSkin + "    BorderStyle " + $borderStyle + $bl()
  !endif
  !if ($borderThickness != "")
    !$elementSkin = $elementSkin + "    BorderThickness " + $borderThickness + $bl()
  !endif
  !$elementSkin = $elementSkin + "}" + $bl()
  !return $elementSkin
!endfunction

!unquoted procedure $defineSkinparams($tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, $shape, $borderStyle, $borderThickness)
  ' only rectangle supports shape(d corners)
  !$tagSkin = $elementTagSkinparams("rectangle", $tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, $shape, $borderStyle, $borderThickness)
  !$tagSkin = $tagSkin + $elementTagSkinparams("database", $tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, "", $borderStyle, $borderThickness)
  !$tagSkin = $tagSkin + $elementTagSkinparams("queue", $tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, "", $borderStyle, $borderThickness)
  ' plantuml.jar bug - actor have to be after person
  !$tagSkin = $tagSkin + $elementTagSkinparams("person", $tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, "", $borderStyle, $borderThickness)
  ' actor has style awesome, therefore $fontColor is ignored and text uses $bgColor too
  !$tagSkin = $tagSkin + $elementTagSkinparams("actor", $tagStereo, $bgColor, $bgColor, $borderColor, $shadowing, "", $borderStyle, $borderThickness)
  ' sequence requires participant
  !$tagSkin = $tagSkin + $elementTagSkinparams("participant", $tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, "", $borderStyle, $borderThickness)
  !$tagSkin = $tagSkin + $elementTagSkinparams("sequencebox", $tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, "", $borderStyle, $borderThickness)
  !if (%strpos($tagStereo, "boundary") >= 0 && $bgColor != "")
    !$tagSkin = $tagSkin + "skinparam package<<" + $tagStereo + ">>StereotypeFontColor " + $bgColor + $bl()
    !$tagSkin = $tagSkin + "skinparam rectangle<<" + $tagStereo + ">>StereotypeFontColor " + $bgColor + $bl()
  !endif
  !if %variable_exists("ENABLE_ALL_PLANT_ELEMENTS")
    !$tagSkin = $tagSkin + $elementTagSkinparams("agent", $tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, "", $borderStyle, $borderThickness)
    !$tagSkin = $tagSkin + $elementTagSkinparams("artifact", $tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, "", $borderStyle, $borderThickness)
    !$tagSkin = $tagSkin + $elementTagSkinparams("card", $tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, "", $borderStyle, $borderThickness)
    !$tagSkin = $tagSkin + $elementTagSkinparams("cloud", $tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, "", $borderStyle, $borderThickness)
    !$tagSkin = $tagSkin + $elementTagSkinparams("collections", $tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, "", $borderStyle, $borderThickness)
    !$tagSkin = $tagSkin + $elementTagSkinparams("file", $tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, "", $borderStyle, $borderThickness)
    !$tagSkin = $tagSkin + $elementTagSkinparams("folder", $tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, "", $borderStyle, $borderThickness)
    !$tagSkin = $tagSkin + $elementTagSkinparams("frame", $tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, "", $borderStyle, $borderThickness)
    !$tagSkin = $tagSkin + $elementTagSkinparams("hexagon", $tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, "", $borderStyle, $borderThickness)
    !$tagSkin = $tagSkin + $elementTagSkinparams("package", $tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, "", $borderStyle, $borderThickness)
    !$tagSkin = $tagSkin + $elementTagSkinparams("stack", $tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, "", $borderStyle, $borderThickness)
    !$tagSkin = $tagSkin + $elementTagSkinparams("storage", $tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, "", $borderStyle, $borderThickness)
    !$tagSkin = $tagSkin + $elementTagSkinparams("usecase", $tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, "", $borderStyle, $borderThickness)
    ' elements without background: font uses $bgColor
    !$tagSkin = $tagSkin + $elementTagSkinparams("boundary", $tagStereo, $bgColor, $bgColor, $borderColor, $shadowing, "", $borderStyle, $borderThickness)
    !$tagSkin = $tagSkin + $elementTagSkinparams("circle", $tagStereo, $bgColor, $bgColor, $borderColor, $shadowing, "", $borderStyle, $borderThickness)
    !$tagSkin = $tagSkin + $elementTagSkinparams("control", $tagStereo, $bgColor, $bgColor, $borderColor, $shadowing, "", $borderStyle, $borderThickness)
    !$tagSkin = $tagSkin + $elementTagSkinparams("entity", $tagStereo, $bgColor, $bgColor, $borderColor, $shadowing, "", $borderStyle, $borderThickness)
    !$tagSkin = $tagSkin + $elementTagSkinparams("interface", $tagStereo, $bgColor, $bgColor, $borderColor, $shadowing, "", $borderStyle, $borderThickness)
    ' label uses wrong font color? (should be $bgColor too)
    !$tagSkin = $tagSkin + $elementTagSkinparams("label", $tagStereo, $bgColor, $bgColor, $borderColor, $shadowing, "", $borderStyle, $borderThickness)
' label colors cannot be set via skinparam use additional style
    !$tagSkin = $tagSkin + "<style>" + $bl()
    ' componentDiagram {
    !$tagSkin = $tagSkin + "  label {" + $bl()
    !$tagSkin = $tagSkin + "    ." + $tagStereo + " {" + $bl()
    !$tagSkin = $tagSkin + "      StereotypeFontColor " + $bgColor + $bl()
    !$tagSkin = $tagSkin + "      Fontcolor " + $bgColor + $bl()
    !$tagSkin = $tagSkin + "      BackgroundColor " + $bgColor + $bl()
    !$tagSkin = $tagSkin + "      BorderColor " + $borderColor + $bl()
    !$tagSkin = $tagSkin + "    }" + $bl()
    !$tagSkin = $tagSkin + "  }" + $bl()
    ' }
    !$tagSkin = $tagSkin + "</style>" + $bl()
  !endif
$tagSkin
!endprocedure

' arrow colors cannot start with # (legend background has to start with #)
!function $colorWithoutHash($c)
  !if (%substr($c, 0, 1) == "#")
    !$c = %substr($c,1)
  !endif
  !return $c
!endfunction

!unquoted procedure $defineRelSkinparams($tagStereo, $textColor, $lineColor, $lineStyle, $lineThickness)
  !$elementSkin = "skinparam arrow<<" + $tagStereo + ">> {" + $bl()
  !if ($lineColor != "") || ($textColor != "") || ($lineStyle != "")
    !$elementSkin = $elementSkin + "    Color "
    !if ($lineColor != "")
      !$elementSkin = $elementSkin + $colorWithoutHash($lineColor)
    !endif
    !if ($textColor != "")
      !$elementSkin = $elementSkin + ";text:" + $colorWithoutHash($textColor)
    !endif
    !if ($lineStyle != "")
      !$elementSkin = $elementSkin + ";line." + $lineStyle
    !endif
    !$elementSkin = $elementSkin + $bl()
  !endif
  !if ($lineThickness != "")
    !$elementSkin = $elementSkin + "    thickness " + $lineThickness + $bl()
  !endif
  !$elementSkin = $elementSkin + "}" + $bl()
$elementSkin
!endprocedure

' %is_dark() requires PlantUML version >= 1.2021.6
!if (%function_exists("%is_dark"))
  !$PlantUMLSupportsDynamicLegendColor = %true()
!else
  !$PlantUMLSupportsDynamicLegendColor = %false()
  !log "dynamic undefined legend colors" requires PlantUML version >= 1.2021.6, therefore only static assigned colors are used
!endif

!unquoted function $contrastLegend($color)
  !if (%is_dark($color))
    !$value = $LEGEND_LIGHT_COLOR
  !else
    !$value = $LEGEND_DARK_COLOR
  !endif
  !return $value
!endfunction

!unquoted function $flatLegend($color)
  !if (%is_dark($color))
    !$value = $LEGEND_DARK_COLOR
  !else
    !$value = $LEGEND_LIGHT_COLOR
  !endif
  !return $value
!endfunction

' legend background has to start with #
!function $colorWithHash($c)
  !if (%substr($c, 0, 1) != "#")
    !$c = "#" + $c
  !endif
  !return $c
!endfunction

!function $addMaskFlag($mask, $attr)
  !if ($attr == "")
    !$mask = $mask + "0"
  !else
    !$mask = $mask + "1"
  !endif
  !return $mask
!endfunction

!function $orFlags($flag1, $flag2)
  !if ($flag1 == "0" && $flag2 == "0")
    !return "0"
  !endif
  !return "1"
!endfunction

!function $tagLegendMask($bgColor, $fontColor, $borderColor, $shadowing, $shape, $sprite, $borderStyle, $borderThickness)
  !$mask = ""
  !$mask = $addMaskFlag($mask, $bgColor)
  !$mask = $addMaskFlag($mask, $fontColor)
  !$mask = $addMaskFlag($mask, $borderColor)
  !$mask = $addMaskFlag($mask, $shadowing)
  !$mask = $addMaskFlag($mask, $shape)
  !$mask = $addMaskFlag($mask, $sprite)
  !$mask = $addMaskFlag($mask, $borderStyle)
  !$mask = $addMaskFlag($mask, $borderThickness)
  !return $mask
!endfunction

!function $resetMask()
  !return "00000000"
!endfunction

!function $combineMasks($mask1, $mask2)
  !$mask = ""
  !$mask = $mask + $orFlags(%substr($mask1, 0, 1), %substr($mask2, 0, 1))
  !$mask = $mask + $orFlags(%substr($mask1, 1, 1), %substr($mask2, 1, 1))
  !$mask = $mask + $orFlags(%substr($mask1, 2, 1), %substr($mask2, 2, 1))
  !$mask = $mask + $orFlags(%substr($mask1, 3, 1), %substr($mask2, 3, 1))
  !$mask = $mask + $orFlags(%substr($mask1, 4, 1), %substr($mask2, 4, 1))
  !$mask = $mask + $orFlags(%substr($mask1, 5, 1), %substr($mask2, 5, 1))
  !$mask = $mask + $orFlags(%substr($mask1, 6, 1), %substr($mask2, 6, 1))
  !$mask = $mask + $orFlags(%substr($mask1, 7, 1), %substr($mask2, 7, 1))
  !return $mask
!endfunction

!function $combineMaskWithTag($mask1, $tag)
  !$mask2 = %get_variable_value("$" + $tag+ "LegendMask")
  !if ($mask2 == "")
    ' !log combineMaskWithTag $mask1, $tag, ... only $mask1
    !return $mask1
  !endif

  ' !log combineMaskWithTag $mask1, $tag, $mask2 ... $combineMasks($mask1, $mask2)
  !return  $combineMasks($mask1, $mask2)
!endfunction

' element symbols typically 4 times too big in legend
!function $smallVersionSprite($sprite, $imageScale = $LEGEND_IMAGE_SIZE_FACTOR)
  ' ,scale= ... has to be first (...,color=black,scale=0.25... is invalid too)
  !if (%strpos($sprite, "=") < 0)
    !if (%substr($sprite, 0, 4) == "img:")
      !$smallSprite = $sprite + "{scale=" + $imageScale + "}"
    !else
      !$smallSprite = $sprite + ",scale=" + $imageScale
    !endif
  !else
    !$smallSprite = $sprite
  !endif
  !return $smallSprite
!endfunction

' format sprite that it can be used in diagram
!function $getSprite($sprite)
  ' if it starts with & it's a OpenIconic, details see https://useiconic.com/open/
  ' if it starts with img: it's an image, details see https://plantuml.com/creole
  !if (%substr($sprite, 0, 1) != "&" && %substr($sprite, 0, 4) != "img:")
    !$formatted = "<$" + $sprite + ">"
  !else
    !$formatted = "<" + $sprite + ">"
  !endif
  !return $formatted
!endfunction

!function $setTagLegendVariables($tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, $shape, $legendText, $legendSprite, $borderStyle, $borderThickness)
  !$bg = $bgColor
  !$fo = $fontColor
  !$bo = $borderColor

  !if ($fo == "")
    !if ($bg != "")
!if ($PlantUMLSupportsDynamicLegendColor)
      !$fo = $contrastLegend($bg)
!else
      !$fo = $LEGEND_DARK_COLOR
!endif
    !else
      !if ($bo == "")
        !$fo = $LEGEND_DARK_COLOR
        !$bg = $LEGEND_LIGHT_COLOR
      !else
!if ($PlantUMLSupportsDynamicLegendColor)
        !$fo = $flatLegend($bo)
        !$bg = $contrastLegend($bo)
!else
        !$fo = $LEGEND_DARK_COLOR
        !$bg = $LEGEND_LIGHT_COLOR
!endif
      !endif
    !endif
  !else
    !if ($bg == "")
!if ($PlantUMLSupportsDynamicLegendColor)
      !$bg = $contrastLegend($fo)
!else
      !$bg = $LEGEND_LIGHT_COLOR
!endif
    !endif
  !endif

  !if ($bo == "")
    !$bo = $bg
  !endif

  !$tagEntry = "|"
  !$tagDetails = "("
  !$tagEntry = $tagEntry + "<" + $colorWithHash($bg) +">"
  ' <U+25AF> ..white rectangle
  !$tagEntry = $tagEntry + "<color:"+$bo+"> <U+25AF></color> "
  !$tagEntry = $tagEntry + "<color:"+$fo+">"
  !if ($legendSprite != "")
    !$tagEntry = $tagEntry + $getSprite($legendSprite) + " "
  !endif

  !$isBoundary = 0
  !if ($legendText == "")
    !if (%strpos($tagStereo, "boundary") >= 0)
      !if ($tagStereo == "boundary")
        !$isBoundary = 1
        !$tagEntry = $LEGEND_BOUNDARY_PRE_PART + $tagEntry + $LEGEND_BOUNDARY_POST_PART + " "
      !else
        ' if contains/ends with _boundary remove _boundary and add "boundary (dashed)"
        !$pos = %strpos($tagStereo, "_boundary")
        !if ($pos > 0)
          !$isBoundary = 1
          !$tagEntry = $tagEntry + " " + $LEGEND_BOUNDARY_PRE_PART + %substr($tagStereo, 0 ,$pos) + $LEGEND_BOUNDARY_POST_PART + " "
        !endif
      !endif
    !endif
    !if ($isBoundary == 0)
      !$tagEntry = $tagEntry + " " + $tagStereo + " "
    !endif

    !if ($isBoundary == 1 && ($bgColor == "#00000000" || %lower($bgColor) == "transparent"))
       !$tagDetails = $tagDetails + $LEGEND_BOUNDARY_TRANSPARENT_INCL_COMA
    !endif
    !if ($shadowing == "true")
      !$tagDetails = $tagDetails + $LEGEND_SHADOW_TEXT + ", "
    !endif
    !if ($shadowing == "false")
      !$tagDetails = $tagDetails + $LEGEND_NO_SHADOW_TEXT + ", "
    !endif
    !if ($shape == $SHARP_CORNER && $shape != $DEFAULT_SHAPE)
      !$tagDetails = $tagDetails + $LEGEND_SHARP_CORNER + ", "
    !endif
    !if ($shape == $ROUNDED_BOX && $shape != $DEFAULT_SHAPE)
      !$tagDetails = $tagDetails + $LEGEND_ROUNDED_BOX + ", "
    !endif
    !if ($shape == $EIGHT_SIDED && $shape != $DEFAULT_SHAPE)
      !$tagDetails = $tagDetails + $LEGEND_EIGHT_SIDED + ", "
    !endif
    !if ($fontColor == "" && $bgColor == "")
      !$tagDetails = $tagDetails + $LEGEND_NO_FONT_BG_TEXT + ", "
    !else
      !if ($fontColor == "")
        !$tagDetails = $tagDetails + $LEGEND_NO_FONT_TEXT + ", "
      !endif
      !if ($bgColor == "")
        !$tagDetails = $tagDetails + $LEGEND_NO_BG_TEXT + ", "
      !endif
    !endif
    !if ($borderStyle != "")
      !if ($borderStyle == $DOTTED_LINE)
        !$tagDetails = $tagDetails + $LEGEND_DOTTED_LINE + ", "
      !elseif ($borderStyle == $DASHED_LINE)
        !if ($isBoundary == 1)
          !$tagDetails = $tagDetails + $LEGEND_BOUNDARY_DASHED_INCL_COMA
        !else
          !$tagDetails = $tagDetails + $LEGEND_DASHED_LINE + ", "
        !endif
      !elseif ($borderStyle == $BOLD_LINE)
        !$tagDetails = $tagDetails + $LEGEND_BOLD_LINE + ", "
      !elseif ($borderStyle == $SOLID_LINE)
        !$tagDetails = $tagDetails + $LEGEND_SOLID_LINE + ", "
      !else
        !$tagDetails = $tagDetails + $borderStyle + ", "
      !endif
    !endif
    !if ($borderThickness != "")
      !$tagDetails = $tagDetails + $LEGEND_THICKNESS + " " + $borderThickness + ", "
    !endif
    !if ($tagDetails=="(" || $tagDetails=="(, ")
      !$tagDetails = "</size>"
    !else
      !$tagDetails = %substr($tagDetails, 0, %strlen($tagDetails)-2)
      !$tagDetails = $tagDetails + ")</size>"
    !endif
  !else
    !$brPos = %strpos($legendText, "\n")
    !if ($brPos > 0)
      !$tagEntry = $tagEntry + %substr($legendText, 0, $brPos) + " "
      !$details = %substr($legendText, $brPos + 2)
      !if ($details=="")
        !$tagDetails = "</size>"
      !else
        !$tagDetails = $tagDetails + $details + ")</size>"
      !endif
    !else
      !$tagEntry = $tagEntry + " " + $legendText + " "
      !$tagDetails = "</size>"
    !endif
  !endif

  !$tagDetails = $tagDetails + "</color> "
  !$tagDetails = $tagDetails + "|"
%set_variable_value("$" + $tagStereo + "LegendEntry", $tagEntry)
%set_variable_value("$" + $tagStereo + "LegendDetails", $tagDetails)
  !return $tagEntry
!endfunction

!function $setTagRelLegendVariables($tagStereo, $textColor, $lineColor, $lineStyle, $legendText, $legendSprite, $lineThickness)
  !$tc = $textColor
  !$lc = $lineColor

  !if ($tc == "")
    !if ($PlantUMLSupportsDynamicLegendColor)
      !$tc = $flatLegend($ARROW_FONT_COLOR)
    !else
      !$tc = $LEGEND_DARK_COLOR
    !endif
  !endif
  !if ($lc == "")
    !if ($PlantUMLSupportsDynamicLegendColor)
      !$lc = $flatLegend($ARROW_COLOR)
    !else
      !$lc = $LEGEND_DARK_COLOR
    !endif
  !endif

  !$tagEntry = "|"
  !$tagDetails = "("
  ' <U+2500> ..white line
  !$tagEntry = $tagEntry + "<color:"+$lc+"> <U+2500></color> "
  !$tagEntry = $tagEntry + "<color:"+$tc+">"
  !if ($legendSprite != "")
    !$tagEntry = $tagEntry + $getSprite($legendSprite) + " "
  !endif
  !if ($legendText == "")
    !$tagEntry = $tagEntry + " " + $tagStereo + " "
    !if ($textColor == "")
      !$tagDetails = $tagDetails + $LEGEND_NO_FONT_TEXT + ", "
    !endif
    !if ($lineColor == "")
      !$tagDetails = $tagDetails + $LEGEND_NO_LINE_TEXT + ", "
    !endif
    !if ($lineStyle != "")
      !if ($lineStyle == $DOTTED_LINE)
        !$tagDetails = $tagDetails + $LEGEND_DOTTED_LINE + ", "
      !elseif ($lineStyle == $DASHED_LINE)
        !$tagDetails = $tagDetails + $LEGEND_DASHED_LINE + ", "
      !elseif ($lineStyle == $BOLD_LINE)
        !$tagDetails = $tagDetails + $LEGEND_BOLD_LINE + ", "
      !else
        !$tagDetails = $tagDetails + $lineStyle + ", "
      !endif
    !endif
    !if ($lineThickness != "")
      !$tagDetails = $tagDetails + $LEGEND_THICKNESS + " " + $lineThickness + ", "
    !endif
    !if ($tagDetails=="(")
      !$tagDetails = "</size>"
    !else
      !$tagDetails = %substr($tagDetails, 0, %strlen($tagDetails)-2)
      !$tagDetails = $tagDetails + ")</size>"
    !endif
  !else
    !$brPos = %strpos($legendText, "\n")
    !if ($brPos > 0)
      !$tagEntry = $tagEntry + " " + %substr($legendText, 0, $brPos) + " "
      !$details = %substr($legendText, $brPos + 2)
      !if ($details=="")
        !$tagDetails = "</size>"
      !else
        !$tagDetails = $tagDetails + $details + ")</size>"
      !endif
    !else
      !$tagEntry = $tagEntry + " " + $legendText + " "
      !$tagDetails = "</size>"
    !endif
  !endif

  !$tagDetails = $tagDetails + "</color> "
  !$tagDetails = $tagDetails + "|"
%set_variable_value("$" + $tagStereo + "_LineLegendEntry", $tagEntry)
%set_variable_value("$" + $tagStereo + "_LineLegendDetails", $tagDetails)
  !return $tagEntry
!endfunction

!unquoted procedure $addTagToLegend($tagStereo, $bgColor="", $fontColor="", $borderColor="", $shadowing="", $shape="", $legendText="", $legendSprite="", $borderStyle="", $borderThickness="")
'' if a combined element tag is defined (e.g. "v1.0&v1.1") then it is typically a merged color,
'' like a new $fontColor="#fdae61" therefore it should be added to the legend
'' and the & combined tags will be not removed
'  !if (%strpos($tagStereo, "&") < 0)
  !$dummyAlreadyVariables = $setTagLegendVariables($tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, $shape, $legendText, $legendSprite, $borderStyle, $borderThickness)
  !$tagCustomLegend = $tagCustomLegend + $tagStereo + "\n"
  !$tagMask = $tagLegendMask( $bgColor, $fontColor, $borderColor, $shadowing, $shape, $sprite, $borderStyle, $borderThickness)
%set_variable_value("$" + $tagStereo + "LegendMask", $tagMask)
'  !endif
!endprocedure

!unquoted procedure $addRelTagToLegend($tagStereo, $textColor="", $lineColor="", $lineStyle="", $legendText="", $legendSprite="", $lineThickness="")
'' Arrows have a bug with stereotype/skinparams and cannot combine text colors of one stereotype
'' and the line color of another stereotype. Therefore the text color of one tag and the line color
'' of another tag have to be combined via a "workaround" tag ("v1.0&v1.1").
'' This workaround tag could be theoretically removed in the legend but after that there would
'' be an inconsistency between the element tags and the rel tags and therefore
'' & combined workaround tags are not removed too (and in unlikely cases the color itself could be changed)
'  !if (%strpos($tagStereo, "&") < 0)
     !$dummyAlreadyVariables = $setTagRelLegendVariables($tagStereo, $textColor, $lineColor, $lineStyle, $legendText, $legendSprite, $lineThickness)
     !$tagCustomLegend = $tagCustomLegend + $tagStereo + "_Line\n"
'  !endif
!endprocedure

!procedure $showActiveLegendEntries($allDefined)
  !$brPos = %strpos($allDefined, "\n")
  !while ($brPos >= 0)
    !$tagStereo = %substr($allDefined, 0, $brPos)
    !$allDefined = %substr($allDefined, $brPos+2)
    !$brPos = %strpos($allDefined, "\n")
    !if (%variable_exists("$" + $tagStereo + "Legend"))
      ' </size> is part of legendDetails
      !$part1 = %get_variable_value("$" + $tagStereo + "LegendEntry")
      !$partSize = "<size:" + $LEGEND_DETAILS_SIZE + ">"
      !$part2 = %get_variable_value("$" + $tagStereo + "LegendDetails")
      !$line = $part1 + $partSize + $part2
$line
    !endif
  !endwhile
  !if (%strlen($allDefined) > 0)
    !$tagStereo = $allDefined
    !if (%variable_exists("$" + $tagStereo + "Legend"))
      ' </size> is part of legendDetails
      !$part1 = %get_variable_value("$" + $tagStereo + "LegendEntry")
      !$partSize = "<size:" + $LEGEND_DETAILS_SIZE + ">"
      !$part2 = %get_variable_value("$" + $tagStereo + "LegendDetails")
      !$line = $part1 + $partSize + $part2
$line
    !endif
  !endif
!endprocedure

' normal rectangle
!function SharpCornerShape()
!return $SHARP_CORNER
!endfunction

!function RoundedBoxShape()
!return $ROUNDED_BOX
!endfunction

!function EightSidedShape()
!return $EIGHT_SIDED
!endfunction

!function DottedLine()
!return $DOTTED_LINE
!endfunction

!function DashedLine()
!return $DASHED_LINE
!endfunction

!function BoldLine()
!return $BOLD_LINE
!endfunction

!function SolidLine()
!return $SOLID_LINE
!endfunction

' used by new defined tags
!unquoted procedure AddElementTag($tagStereo, $bgColor="", $fontColor="", $borderColor="", $shadowing="", $shape="", $sprite="", $techn="", $legendText="", $legendSprite="", $borderStyle="", $borderThickness="")

!if (NEW_C4_STYLE == 1)
  !$swap=$bgColor
  !$bgColor=$fontColor
  !$fontColor=$swap
!endif

$defineSkinparams($tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, $shape, $borderStyle, $borderThickness)
  !if ($sprite!="")
%set_variable_value("$" + $tagStereo + "ElementTagSprite", $sprite)
    !if ($legendSprite == "")
      !$legendSprite = $smallVersionSprite($sprite)
    !endif
  !endif
  !if ($techn != "")
%set_variable_value("$" + $tagStereo + "ElementTagTechn", $techn)
  !endif
$addTagToLegend($tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, $shape, $legendText, $legendSprite, $borderStyle, $borderThickness)
!endprocedure

!unquoted procedure $addElementTagInclReuse($elementName, $tagStereo, $bgColor="", $fontColor="", $borderColor="", $shadowing="", $shape="", $sprite="", $techn="", $legendText="", $legendSprite="", $borderStyle="", $borderThickness="")

'stored tags are already swapped (swap before comparing)
!if (NEW_C4_STYLE == 1)
  !$swap=$bgColor
  !$bgColor=$fontColor
  !$fontColor=$swap
!endif

  !$bgColor=$restoreEmpty($elementName, "bgColor", $bgColor, %false())
  !$fontColor=$restoreEmpty($elementName, "fontColor", $fontColor, %false())
  !$borderColor=$restoreEmpty($elementName, "borderColor", $borderColor, %false())
  !$shadowing=$restoreEmpty($elementName, "shadowing", $shadowing, %false())
  !$shape=$restoreEmpty($elementName, "shape", $shape, %false())
  !$sprite=$restoreEmpty($elementName, "sprite", $sprite, %false())
  !$techn=$restoreEmpty($elementName, "techn", $techn, %false())
  ' new style should has its own legend text
  ' !$legendText=$restoreEmpty($elementName, "legendText", $legendText, %false())
  !$legendSprite=$restoreEmpty($elementName, "legendSprite", $legendSprite, %false())
  !$borderStyle=$restoreEmpty($elementName, "borderStyle", $borderStyle, %false())
  !$borderThickness=$restoreEmpty($elementName, "borderThickness", $borderThickness, %false())

' swap back
!if (NEW_C4_STYLE == 1)
  !$swap=$bgColor
  !$bgColor=$fontColor
  !$fontColor=$swap
!endif

  AddElementTag($tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, $shape, $sprite, $techn, $legendText, $legendSprite, $borderStyle, $borderThickness)
!endprocedure

' used by new defined rel tags
!unquoted procedure AddRelTag($tagStereo, $textColor="", $lineColor="", $lineStyle="", $sprite="", $techn="", $legendText="", $legendSprite="", $lineThickness="")
$defineRelSkinparams($tagStereo, $textColor, $lineColor, $lineStyle, $lineThickness)
  !if ($sprite != "")
%set_variable_value("$" + $tagStereo + "RelTagSprite", $sprite)
    !if ($legendSprite == "")
      ' relation symbols typically 1:1 no additional scale required
      !$legendSprite = $sprite
    !endif
  !endif
  !if ($techn != "")
%set_variable_value("$" + $tagStereo + "RelTagTechn", $techn)
  !endif
$addRelTagToLegend($tagStereo, $textColor, $lineColor, $lineStyle, $legendText, $legendSprite, $lineThickness)
!endprocedure

' update the style of existing elements like person, ...
!unquoted procedure UpdateElementStyle($elementName, $bgColor="", $fontColor="", $borderColor="", $shadowing="", $shape="", $sprite="", $techn="", $legendText="", $legendSprite="", $borderStyle="", $borderThickness="")

!if (NEW_C4_STYLE == 1)
  !$swap=$bgColor
  !$bgColor=$fontColor
  !$fontColor=$swap
!endif

!$bgColor=$restoreEmpty($elementName, "bgColor", $bgColor, %true())
!$fontColor=$restoreEmpty($elementName, "fontColor", $fontColor, %true())
!$borderColor=$restoreEmpty($elementName, "borderColor", $borderColor, %true())
!$shadowing=$restoreEmpty($elementName, "shadowing", $shadowing, %true())
!$shape=$restoreEmpty($elementName, "shape", $shape, %true())
!$sprite=$restoreEmpty($elementName, "sprite", $sprite, %true())
!$techn=$restoreEmpty($elementName, "techn", $techn, %true())
!$legendText=$restoreEmpty($elementName, "legendText", $legendText, %true())
!$legendSprite=$restoreEmpty($elementName, "legendSprite", $legendSprite, %true())
!$borderStyle=$restoreEmpty($elementName, "borderStyle", $borderStyle, %true())
!$borderThickness=$restoreEmpty($elementName, "borderThickness", $borderThickness, %true())

$defineSkinparams($elementName, $bgColor, $fontColor, $borderColor, $shadowing, $shape, $borderStyle, $borderThickness)
  !if ($sprite != "")
%set_variable_value("$" + $elementName + "ElementTagSprite", $sprite)
    !if ($legendSprite == "")
      !$legendSprite = $smallVersionSprite($sprite)
    !endif
  !endif
  !if ($techn != "")
%set_variable_value("$" + $elementName + "ElementTagTechn", $techn)
  !endif
  !$dummyAlreadyVariables = $setTagLegendVariables($elementName, $bgColor, $fontColor, $borderColor, $shadowing, $shape, $legendText, $legendSprite, $borderStyle, $borderThickness)
  ' default tags sets at least bgColor and fontColor
  !$tagMask = $tagLegendMask("CHANGED", "CHANGED", $borderColor, $shadowing, $shape, $sprite, $borderStyle, $borderThickness)
%set_variable_value("$" + $elementName + "LegendMask", $tagMask)
!endprocedure

/' @deprecated in favor of UpdateElementStyle '/
!unquoted procedure UpdateSkinparamsAndLegendEntry($elementName, $bgColor="", $fontColor="", $borderColor="", $shadowing="")
UpdateElementStyle($elementName, $bgColor, $fontColor, $borderColor, $shadowing)
!endprocedure

' update the style of default relation, it has to set both properties (combined statement not working)
!unquoted procedure UpdateRelStyle($textColor, $lineColor)
  !$elementSkin = "skinparam arrow {" + $bl()
  !$elementSkin = $elementSkin + "    Color " + $lineColor + $bl()
  !$elementSkin = $elementSkin + "    FontColor " + $textColor + $bl()
  !$elementSkin = $elementSkin + "}" + $bl()
$elementSkin
!endprocedure

!unquoted procedure UpdateLegendTitle($newTitle)
  !$LEGEND_TITLE_TEXT = $newTitle
!endprocedure

' tags/stereotypes have to be delimited with \n
!unquoted procedure SetDefaultLegendEntries($tagStereoEntries)
  !$tagDefaultLegend = $tagStereoEntries
!endprocedure

' Links
' ##################################

!function $getLink($link)
  !if ($link != "")
    !return "[[" + $link + "]]"
  !else
    !return ""
  !endif
!endfunction

' Line breaks
' ##################################

!unquoted function $breakText($text, $usedNewLine, $widthStr="-1")
!$width = %intval($widthStr)
!$multiLine = ""
!if (%strpos($text, "\n") >= 0)
  !while (%strpos($text, "\n") >= 0)
    !$brPos = %strpos($text, "\n")
    !if ($brPos > 0)
      !$multiLine = $multiLine + %substr($text, 0, $brPos) + $usedNewLine
    !else
      ' <U+00A0> non breaking change that newLine breaks with formats can be used with \n\n
      !$multiLine = $multiLine + "<U+00A0>" + $usedNewLine
    !endif
    !$text = %substr($text, $brPos+2)
    !if (%strlen($text) == 0)
      !$text = "<U+00A0>"
    !endif
  !endwhile
!else
  !while ($width>0 && %strlen($text) > $width)
    !$brPos = $width
    !while ($brPos > 0 && %substr($text, $brPos, 1) != ' ')
      !$brPos = $brPos - 1
    !endwhile

    !if ($brPos < 1)
      !$brPos = %strpos($text, " ")
    !else
    !endif

    !if ($brPos > 0)
      !$multiLine = $multiLine + %substr($text, 0, $brPos) + $usedNewLine
      !$text = %substr($text, $brPos + 1)
    !else
      !$multiLine = $multiLine+ $text
      !$text = ""
    !endif
  !endwhile
!endif
!if (%strlen($text) > 0)
  !$multiLine = $multiLine + $text
!endif
!return $multiLine
!endfunction

!unquoted function $breakLabel($text)
!$usedNewLine = "\n== "
!$multiLine = $breakText($text, $usedNewLine)
!return $multiLine
!endfunction

!unquoted function $breakDescr($text, $widthStr)
  !$usedNewLine = "\n"
  !return $breakText($text, $usedNewLine, $widthStr)
!endfunction

' $breakTechn() supports //...//; $breakNode() in C4_Deployment supports no //....//
!unquoted function $breakTechn($text, $widthStr)
  !$usedNewLine = '</size>//\n//<size:'+$TECHN_FONT_SIZE+'>'
  !return $breakText($text, $usedNewLine, $widthStr)
!endfunction

' Element base layout
' ##################################

!function $getElementBase($label, $techn, $descr, $sprite)
  !$element = ""
  !if ($sprite != "")
    !$element = $element + $getSprite($sprite)
    !if ($label != "")
      !$element = $element + '\n'
    !endif
  !endif
  !if ($label != "")
    !$element = $element + '== ' + $breakLabel($label)
  !else
    !$element = $element + '<size:0>.</size>'
  !endif
  !if ($techn != "")
    !$element = $element + '\n//<size:' + $TECHN_FONT_SIZE + '>[' + $breakTechn($techn, '-1') + ']</size>//'
  !endif
  !if ($descr != "")
    !$element = $element + '\n\n' + $descr
  !endif
  !return $element
!endfunction

!function $getElementLine($umlShape, $elementType, $alias, $label, $techn, $descr, $sprite, $tags, $link)
  !$sprite=$toElementArg($sprite, $tags, "ElementTagSprite", $elementType)
  !$techn=$toElementArg($techn, $tags, "ElementTagTechn", $elementType)
  !$baseProp = $getElementBase($label, $techn, $descr, $sprite) + $getProps()
  !$stereo = $toStereos($elementType,$tags)
  !$calcLink = $getLink($link)

  !$line = $umlShape + " " + %chr(34) + $baseProp + %chr(34) +" " + $stereo + " as " + $alias + " " + $calcLink
  !return $line
!endfunction

' Element properties
' ##################################

' collect all defined properties as table rows
!global $propTable = ""
!global $propTableCaption = ""
!global $propColCaption = "="

!global $isFirstProp = 1
!global $firstPropCol = 1
!global $lastPropCol = 1

!function $fillMissing($col, $colNext)
  !if ($col == "" && $colNext != "")
    !return " "
  !endif
  !return $col
!endfunction

!function $updatePropColumns($colIdx)
  !if ($isFirstProp == 1 && $colIdx > $firstPropCol)
    !$firstPropCol = $colIdx
  !endif
  !if ($isFirstProp == 0 && $colIdx > $lastPropCol)
    !$lastPropCol = $colIdx
  !endif
  !return ""
!endfunction

' add missing header columns, if a following row has more columns
' (fixed in PlantUML v1.2025.1beta9; only required in older versions)
!function $fixHeaderColumns()
  ' the number of displayed columns considers only the first row
  ' if another row has more columns the first has to be filled with missing columns
  !if ($lastPropCol > $firstPropCol)
    !$delta = $lastPropCol - $firstPropCol
    !$delta = $delta * 2
    !$fix = %substr(" | | | |", 0, $delta)

    ' basically the line break \n should be the split
    ' but \n is not encoded (anymore?) therefore split only via
    ' \ and remove the last obsolete  \ (changed order with add
    ' \ at the beginning is not working).
    ' "\n" would split \ and n ==> n would be an unwanted line break
    !$lines = %splitstr($propTable, "\")
    ' !$lines = %splitstr_regex($propTable, "(?=[\x000A])")
    !$first = 1
    !$newTab = ""
    !foreach $item in $lines
      !if ($first == 1)
        !$item = $item + $fix
        !$first = 0
      !endif
      !$newTab = $newTab + $item + "\"
    !endfor

    !$fixLen = %strlen($newTab) - 1
    !$newTab = %substr($newTab, 0, $fixLen)

    !$propTable = $newTab
  !endif

  !$isFirstProp = 1
  !$firstPropCol = 1
  !$lastPropCol = 1

  !return ""
!endfunction

!unquoted function SetPropertyHeader($col1Name, $col2Name = "", $col3Name = "", $col4Name = "")
  !$col3Name = $fillMissing($col3Name, $col4Name)
  !$col2Name = $fillMissing($col2Name, $col3Name)
  !$col1Name = $fillMissing($col1Name, $col2Name)

  !$propColCaption = ""
  !$propTableCaption = "|= " + $col1Name + " |"
  !if ($col2Name != "")
    !$propTableCaption = $propTableCaption + "= " + $col2Name + " |"
    $updatePropColumns(2)
  !endif
  !if ($col3Name != "")
    !$propTableCaption = $propTableCaption + "= " + $col3Name + " |"
    $updatePropColumns(3)
  !endif
  !if ($col4Name != "")
    !$propTableCaption = $propTableCaption + "= " + $col4Name + " |"
    $updatePropColumns(4)
  !endif

  !$isFirstProp = 0
  !return ""
!endfunction

!unquoted function WithoutPropertyHeader()
  !$propTableCaption = ""
  !$propColCaption = "="

  !$isFirstProp = 1
  !$firstPropCol = 1
  !$lastPropCol = 1

  !return ""
!endfunction

!unquoted function AddProperty($col1, $col2 = "", $col3 = "", $col4 = "")
  !$col3 = $fillMissing($col3, $col4)
  !$col2 = $fillMissing($col2, $col3)
  !$col1 = $fillMissing($col1, $col2)

  !if ($propTable == "")
    !if ($propTableCaption != "")
      !$propTable = $propTableCaption + "\n"
    !endif
  !else
    !$propTable = $propTable + "\n"
  !endif

  !$propTable = $propTable + "| " + $col1 + " |"
  !if ($col2 != "")
    !$propTable = $propTable + $propColCaption + " " + $col2 + " |"
    $updatePropColumns(2)
  !endif
  !if ($col3 != "")
    !$propTable = $propTable + " " + $col3 + " |"
    $updatePropColumns(3)
  !endif
  !if ($col4 != "")
    !$propTable = $propTable + " " + $col4 + " |"
    $updatePropColumns(4)
  !endif

  !$isFirstProp = 0
  !return ""
!endfunction

!unquoted function $getProps($alignedNL = "\n")
  $fixHeaderColumns()

  !if ($propTable != "")
    !$retTable = $alignedNL + $propTable
    !$propTable = ""
    !return $retTable
  !endif
  !return ""
!endfunction

!unquoted function $getProps_L()
  !return $getProps("\l")
!endfunction

!unquoted function $getProps_R()
  !return $getProps("\r")
!endfunction

SetPropertyHeader("Property","Value")

' Layout
' ##################################

!function $getLegendDetailsSize($detailsFormat)
  !if $detailsFormat == $LEGEND_DETAILS_NONE
    !$size = 0
  !elseif $detailsFormat == $LEGEND_DETAILS_SMALL
    !$size = $LEGEND_DETAILS_SMALL_SIZE
  !else
    !$size = $LEGEND_DETAILS_NORMAL_SIZE
  !endif
  !return $size
!endfunction

!procedure $getHideStereotype($hideStereotype)
!if ($hideStereotype == "true")
hide stereotype
!endif
!endprocedure

!procedure $getLegendTable($detailsFormat)
!global $LEGEND_DETAILS_SIZE = $getLegendDetailsSize($detailsFormat)
<$colorWithHash(transparent),$colorWithHash(transparent)>|<color:$LEGEND_TITLE_COLOR>**$LEGEND_TITLE_TEXT **</color> |
$showActiveLegendEntries($tagDefaultLegend)
$showActiveLegendEntries($tagCustomLegend)
!endprocedure

!procedure $getLegendArea($areaAlias, $hideStereotype, $details)
$getHideStereotype($hideStereotype)
rectangle $areaAlias<<legendArea>> [
$getLegendTable($details)
]
!endprocedure

!procedure HIDE_STEREOTYPE()
hide stereotype
!endprocedure

!unquoted procedure SET_SKETCH_STYLE($bgColor="_dont_change_", $fontColor="_dont_change_", $warningColor="_dont_change_", $fontName="_dont_change_", $footerWarning="_dont_change_", $footerText="_dont_change_")
!if $bgColor != "_dont_change_"
  !global $SKETCH_BG_COLOR = $bgColor
!endif
!if $fontColor != "_dont_change_"
  !global $SKETCH_FONT_COLOR = $fontColor
!endif
!if $warningColor != "_dont_change_"
  !global $SKETCH_WARNING_COLOR = $warningColor
!endif
!if $fontName != "_dont_change_"
  !global $SKETCH_FONT_NAME = $fontName
!endif
!if $footerWarning != "_dont_change_"
  !global $SKETCH_FOOTER_WARNING = $footerWarning
!endif
!if $footerText != "_dont_change_"
  !global $SKETCH_FOOTER_TEXT = $footerText
!endif
!endprocedure

!procedure LAYOUT_AS_SKETCH()
!$counter=0
!foreach $versionPart in %splitstr(%version(), ".")
  !$counter=$counter+1

  !if ($counter == 2)
    !$year=$versionPart
  !endif

  !if ($counter == 3)
    !$minor=$versionPart
  !endif
!endfor

!if ($year < 2025) || ($year == 2025 && $minor == 0)
  skinparam handwritten true
!else
  !option handwritten true
!endif

!if $SKETCH_BG_COLOR > ""
  skinparam backgroundColor $SKETCH_BG_COLOR
!endif
!if $SKETCH_FONT_COLOR > ""
  skinparam footer {
    FontColor $SKETCH_FONT_COLOR
  }
  !if $ARROW_COLOR == "#666666"
    !global $ARROW_COLOR = $SKETCH_FONT_COLOR
    !global $ARROW_FONT_COLOR = $SKETCH_FONT_COLOR
    skinparam arrow {
      Color $ARROW_COLOR
      FontColor $ARROW_FONT_COLOR
    }
  !endif
  !if $BOUNDARY_COLOR == "#444444"
    !global $BOUNDARY_COLOR = $SKETCH_FONT_COLOR
    skinparam rectangle<<boundary>> {
      FontColor $BOUNDARY_COLOR
      BorderColor $BOUNDARY_COLOR
    }
  !endif
!endif
!if $SKETCH_FONT_NAMES > ""
  skinparam defaultFontName $SKETCH_FONT_NAME
!endif
!if $SKETCH_FOOTER_WARNING > "" || $SKETCH_FOOTER_TEXT > ""
  !$line = "footer <font color=" + $SKETCH_WARNING_COLOR + ">"+ $SKETCH_FOOTER_WARNING + "</font> " + $SKETCH_FOOTER_TEXT
  $line
!endif
!endprocedure

!global $fix_direction=%false()

!function $down($start,$end)
!if ($fix_direction)
!return $start+"RIGHT"+$end
!else
!return $start+"DOWN"+$end
!endif
!endfunction

!function $up($start,$end)
!if ($fix_direction)
!return $start+"LEFT"+$end
!else
!return $start+"UP"+$end
!endif
!endfunction

!function $left($start,$end)
!if ($fix_direction)
!return $start+"UP"+$end
!else
!return $start+"LEFT"+$end
!endif
!endfunction

!function $right($start,$end)
!if ($fix_direction)
!return $start+"DOWN"+$end
!else
!return $start+"RIGHT"+$end
!endif
!endfunction

!procedure LAYOUT_TOP_DOWN()
!global $fix_direction=%false()
top to bottom direction
!endprocedure

!procedure LAYOUT_LEFT_RIGHT()
!global $fix_direction = %false()
left to right direction
!endprocedure

!procedure LAYOUT_LANDSCAPE()
!global $fix_direction = %true()
left to right direction
!endprocedure

' legend details can displayed as Normal(), Small(), None()
!function None()
!return $LEGEND_DETAILS_NONE
!endfunction

!function Normal()
!return $LEGEND_DETAILS_NORMAL
!endfunction

!function Small()
!return $LEGEND_DETAILS_SMALL
!endfunction

' has to be last call in diagram
!unquoted procedure SHOW_LEGEND($hideStereotype="true", $details=Small())
$getHideStereotype($hideStereotype)
legend right
$getLegendTable($details)
endlegend
!endprocedure

/' @deprecated in favor of SHOW_LEGEND '/
!unquoted procedure SHOW_DYNAMIC_LEGEND($hideStereotype="true")
SHOW_LEGEND($hideStereotype)
!endprocedure

' legend is reserved and cannot be uses as alias of SHOW_FLOATING_LEGEND() therefore
' LEGEND() is introduced. It returns the default name of the floating alias "floating_legend_alias"
' and can be used in the Lay_Distance() calls
!function LEGEND()
!return "floating_legend_alias"
!endfunction

' enables that legend can be located in drawing area of the diagram. It has to be last call in diagram followed by Lay_Distance()
!unquoted procedure SHOW_FLOATING_LEGEND($alias=LEGEND(), $hideStereotype="true", $details=Small())
$getLegendArea($alias, $hideStereotype, $details)
!endprocedure

' Boundaries
' ##################################

!unquoted procedure UpdateBoundaryStyle($elementName="", $bgColor="", $fontColor="", $borderColor="", $shadowing="", $shape="", $type="", $legendText="", $borderStyle="", $borderThickness="", $sprite="", $legendSprite="")

!if (NEW_C4_STYLE == 1)
  !$swap=$bgColor
  !$bgColor=$fontColor
  !$fontColor=$swap
!endif

  !if ($elementName != "")
    !$elementBoundary = $elementName + '_boundary'
    UpdateElementStyle($elementBoundary, $bgColor, $fontColor, $borderColor, $shadowing, $shape, $sprite, $type, $legendText, $legendSprite, $borderStyle, $borderThickness)
  !else
    UpdateElementStyle("boundary", $bgColor, $fontColor, $borderColor, $shadowing, $shape, $sprite, $type, $legendText, $legendSprite, $borderStyle, $borderThickness)
    ' simulate color inheritance
    UpdateBoundaryStyle("enterprise", $bgColor, $fontColor, $borderColor, $shadowing, $shape, "Enterprise", "", $borderStyle, $borderThickness, $sprite, $legendSprite)
    UpdateBoundaryStyle("system", $bgColor, $fontColor, $borderColor, $shadowing, $shape, "System", "", $borderStyle, $borderThickness, $sprite, $legendSprite)
    UpdateBoundaryStyle("container", $bgColor, $fontColor, $borderColor, $shadowing, $shape, "Container", "", $borderStyle, $borderThickness, $sprite, $legendSprite)
  !endif
!endprocedure

!unquoted procedure AddBoundaryTag($tagStereo, $bgColor="", $fontColor="", $borderColor="", $shadowing="", $shape="", $type="", $legendText="", $borderStyle="", $borderThickness="", $sprite="", $legendSprite="")
    !$tagBoundary = $tagStereo + '_boundary'
    AddElementTag($tagBoundary, $bgColor, $fontColor, $borderColor, $shadowing, $shape, $sprite, $type, $legendText, $legendSprite, $borderStyle, $borderThickness)
!endprocedure

' add _boundary to all tags that short tag version can be used
!unquoted function $addBoundaryPostfix($tags)
  !if (%strlen($tags) == 0)
    !return ''
  !endif
  !$boundaryTags = ''
  !$brPos = %strpos($tags, "+")
  !while ($brPos >= 0)
    !$tag = %substr($tags, 0, $brPos)
    !$boundaryTags = $boundaryTags + $tag + '_boundary+'
    !$tags = %substr($tags, $brPos+1)
    !$brPos = %strpos($tags, "+")
  !endwhile
  !if (%strlen($tags) > 0)
    !$boundaryTags = $boundaryTags + $tags + '_boundary'
  !endif
  !return $boundaryTags
!endfunction

!function $getBoundary($label, $type, $descr, $sprite)
  !$line = '== '
  !if ($sprite != "")
    ' add sprite in label line that it is more compact
    !$line = $line + $getSprite($smallVersionSprite($sprite, $BOUNDARY_IMAGE_SIZE_FACTOR)) + ' '
  !endif
  !$line = $line + $breakLabel($label)
  !if ($type != "")
    !$line = $line + '\n<size:' + $TECHN_FONT_SIZE + '>[' + $type + ']</size>'
  !endif
  !if ($descr != "")
    !$line = $line + '\n\n' + $breakDescr($descr, $BOUNDARY_DESCR_MAX_CHAR_WIDTH)
  !endif
  !return $line
!endfunction

!unquoted procedure Boundary($alias, $label, $type="", $tags="", $link="", $descr = "")
!$boundaryTags = $addBoundaryPostfix($tags)
' boundary $type reuses $techn definition of $boundaryTags
!$type=$toElementArg($type, $boundaryTags, "ElementTagTechn", "boundary")
!$sprite=$toElementArg("", $boundaryTags, "ElementTagSprite", "boundary")
rectangle "$getBoundary($label, $type, $descr, $sprite)" $toStereos("boundary", $boundaryTags) as $alias $getLink($link)
!endprocedure

' Boundary Styling
UpdateBoundaryStyle("", $bgColor=$BOUNDARY_BG_COLOR, $fontColor=$BOUNDARY_COLOR, $borderColor=$BOUNDARY_COLOR, $borderStyle=DashedLine(), $legendText="$BOUNDARY_LEGEND_TEXT")

' Index
' ##################################

' Dynamic/Sequence diagram supports (automatically) numbered interactions:
'     preferred function calls
'     (Uppercase) LastIndex(): return the last used index (function which can be used as argument)
'     (Uppercase) Index($offset=1): returns current index and calculates next index (function which can be used as argument)
'     (Uppercase) SetIndex($new_index): returns new set index and calculates next index (function which can be used as argument)

'     old procedures calls
'     (lowercase) increment($offset=1): increase current index (procedure which has no direct output)
'     (lowercase) setIndex($new_index): set the new index (procedure which has no direct output)

!$lastIndex = 0
!$index = 1

!procedure increment($offset=1)
  !$lastIndex = $index
  !$index = $index + $offset
!endprocedure

!procedure setIndex($new_index)
  !$lastIndex = $index
  !$index = $new_index
!endprocedure

!function Index($offset=1)
  !$lastIndex = $index
  !$index = $lastIndex + $offset
  !return $lastIndex
!endfunction

!function LastIndex()
  !return $lastIndex
!endfunction

!function SetIndex($new_index, $offset=1)
  !$lastIndex = $new_index
  !$index = $new_index + $offset
  !return $lastIndex
!endfunction

!unquoted function $getPrefix($index)
  !if ($index == "")
    !$pre = Index() + ": "
  !else
    !$pre = $index + ": "
  !endif
  !return $pre
!endfunction

' Relationship
' ##################################

!function $getRel($direction, $alias1, $alias2, $label, $techn, $descr, $sprite, $tags, $link)
  !$sprite = $toRelArg($sprite, $tags, "RelTagSprite")
  !$techn = $toRelArg($techn, $tags, "RelTagTechn")
  !$rel = $alias1 + ' ' + $direction + ' ' + $alias2
  !if ($tags != "")
    !$rel = $rel + ' ' + $toStereos($tags)
  !endif
  !$rel = $rel + ' : '
  !if ($link != "")
    !$rel = $rel + '**[[' + $link + ' '
  !endif
  !if ($sprite != "")
    !$rel = $rel + $getSprite($sprite)
    !if ($label != "")
      !$rel = $rel + ' '
    !endif
  !endif
  !if ($link != "")
    !$usedNewLine = ']]**\n**[[' + $link + ' '
    ' if sprite and label is empty than the link url is shown (otherwise link cannot be activated at all)
    !$rel = $rel + $breakText($label, $usedNewLine) + ']]**'
  !else
    !if ($label != "")
      !$usedNewLine = '**\n**'
      !$rel = $rel + '**' + $breakText($label, $usedNewLine) + '**'
    !else
      !$rel = $rel + '<size:0>.</size>'
    !endif
  !endif
  !if ($techn != "")
    ' line break is not deterministic, calculate it
    !$rel = $rel + '\n//<size:' + $TECHN_FONT_SIZE + '>[' + $breakTechn($techn, $REL_TECHN_MAX_CHAR_WIDTH) + ']</size>//'
  !endif
  !if ($descr != "")
    ' line break is not deterministic, calculate it
    !$rel = $rel + '\n\n' + $breakDescr($descr, $REL_DESCR_MAX_CHAR_WIDTH)
  !endif
  !$prop = $getProps()
  !if ($prop != "")
    ' reuse table
    !$rel = $rel + $prop
  !endif
  !return $rel
!endfunction

!unquoted procedure Rel_($alias1, $alias2, $label, $direction)
$getRel($direction, $alias1, $alias2, $label, "", "", "", "", "")
!endprocedure
!unquoted procedure Rel_($alias1, $alias2, $label, $techn, $direction)
$getRel($direction, $alias1, $alias2, $label, $techn, "", "", "", "")
!endprocedure

!unquoted procedure Rel($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="")
$getRel("-->>", $from, $to, $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure

!unquoted procedure BiRel($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="")
$getRel("<<-->>", $from, $to, $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure

!unquoted procedure Rel_Back($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="")
$getRel("<<--", $from, $to, $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure

!unquoted procedure Rel_Neighbor($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="")
$getRel("->>", $from, $to, $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure

!unquoted procedure BiRel_Neighbor($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="")
$getRel("<<->>", $from, $to, $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure

!unquoted procedure Rel_Back_Neighbor($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="")
$getRel("<<-", $from, $to, $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure

!unquoted procedure Rel_D($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="")
$getRel($down("-","->>"), $from, $to, $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure
!unquoted procedure Rel_Down($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="")
$getRel($down("-","->>"), $from, $to, $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure

!unquoted procedure BiRel_D($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="")
$getRel($down("<<-","->>"), $from, $to, $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure
!unquoted procedure BiRel_Down($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="")
$getRel($down("<<-","->>"), $from, $to, $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure

!unquoted procedure Rel_U($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="")
$getRel($up("-","->>"), $from, $to, $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure
!unquoted procedure Rel_Up($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="")
$getRel($up("-","->>"), $from, $to, $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure

!unquoted procedure BiRel_U($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="")
$getRel($up("<<-","->>"), $from, $to, $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure
!unquoted procedure BiRel_Up($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="")
$getRel($up("<<-","->>"), $from, $to, $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure

!unquoted procedure Rel_L($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="")
$getRel($left("-","->>"), $from, $to, $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure
!unquoted procedure Rel_Left($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="")
$getRel($left("-","->>"), $from, $to, $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure

!unquoted procedure BiRel_L($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="")
$getRel($left("<<-","->>"), $from, $to, $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure
!unquoted procedure BiRel_Left($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="")
$getRel($left("<<-","->>"), $from, $to, $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure

!unquoted procedure Rel_R($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="")
$getRel($right("-","->>"), $from, $to, $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure
!unquoted procedure Rel_Right($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="")
$getRel($right("-","->>"), $from, $to, $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure

!unquoted procedure BiRel_R($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="")
$getRel($right("<<-","->>"), $from, $to, $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure
!unquoted procedure BiRel_Right($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="")
$getRel($right("<<-","->>"), $from, $to, $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure

' Layout Helpers
' ##################################

!function $getHiddenLine($distance)
  !return '-[hidden]' + %substr('------------', 0, %intval($distance) + 1)
!endfunction

!function $l_down($start,$end)
  !if (NO_LAY_ROTATE == 0)
    !return $down($start,$end)
  !else
    !return $start+"DOWN"+$end
  !endif
!endfunction

!function $l_up($start,$end)
  !if (NO_LAY_ROTATE == 0)
    !return $up($start,$end)
  !else
    !return $start+"UP"+$end
  !endif
!endfunction

!function $l_left($start,$end)
  !if (NO_LAY_ROTATE == 0)
    !return $left($start,$end)
  !else
    !return $start+"LEFT"+$end
  !endif
!endfunction

!function $l_right($start,$end)
  !if (NO_LAY_ROTATE == 0)
    !return $right($start,$end)
  !else
    !return $start+"RIGHT"+$end
  !endif
!endfunction

!unquoted procedure Lay_D($from, $to)
$from $l_down("-[hidden]","-") $to
!endprocedure
!unquoted procedure Lay_Down($from, $to)
$from $l_down("-[hidden]","-") $to
!endprocedure

!unquoted procedure Lay_U($from, $to)
$from $l_up("-[hidden]","-") $to
!endprocedure
!unquoted procedure Lay_Up($from, $to)
$from $l_up("-[hidden]","-") $to
!endprocedure

!unquoted procedure Lay_R($from, $to)
$from $l_right("-[hidden]","-") $to
!endprocedure
!unquoted procedure Lay_Right($from, $to)
$from $l_right("-[hidden]","-") $to
!endprocedure

!unquoted procedure Lay_L($from, $to)
$from $l_left("-[hidden]","-") $to
!endprocedure
!unquoted procedure Lay_Left($from, $to)
$from $l_left("-[hidden]","-") $to
!endprocedure

' PlantUML bug: lines which does "not match" with the orientation/direction of the diagram
' use the same length therefore the method offers no direction at all.
' If a direction is required the Lay_...() methods can be used
!unquoted procedure Lay_Distance($from, $to, $distance="0")
$from $getHiddenLine($distance) $to
!endprocedure


================================================
FILE: C4_Component.puml
================================================
' convert it with additional command line argument -DRELATIVE_INCLUDE="relative/absolute" to use locally
!if %variable_exists("RELATIVE_INCLUDE")
  !include ./C4_Container.puml
!else
  !include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Container.puml
!endif

' Scope: A single container.
' Primary elements: Components within the container in scope.
' Supporting elements: Containers (within the software system in scope) plus people and software systems directly connected to the components.
' Intended audience: Software architects and developers.

' Colors
' ##################################

!$COMPONENT_FONT_COLOR ?= "#000000"
!$COMPONENT_BG_COLOR ?= "#85BBF0"
!$COMPONENT_BORDER_COLOR ?= "#78A8D8"

!$EXTERNAL_COMPONENT_LEGEND_TEXT ?= "external component"
!$EXTERNAL_COMPONENT_FONT_COLOR ?= $COMPONENT_FONT_COLOR
!$EXTERNAL_COMPONENT_BG_COLOR ?= "#CCCCCC"
!$EXTERNAL_COMPONENT_BORDER_COLOR ?= "#BFBFBF"

' New C4 style automatically swaps font and background colors, but with those settings the component/node background would be black.
' Therefore the colors should be displayed unchanged (the init colors have to be swapped too).
!$NEW_C4_USE_ORIGINAL_COMPONENT_COLORS ?= 1
!$componentColorsSwappedAlready ?= 0
!if (NEW_C4_STYLE == 1 && $NEW_C4_USE_ORIGINAL_COMPONENT_COLORS == 1 && $componentColorsSwappedAlready == 0)
  !$swap=$COMPONENT_BG_COLOR
  !$COMPONENT_BG_COLOR=$COMPONENT_FONT_COLOR
  !$COMPONENT_FONT_COLOR=$swap
  !$swap=$EXTERNAL_COMPONENT_BG_COLOR
  !$EXTERNAL_COMPONENT_BG_COLOR=$EXTERNAL_COMPONENT_FONT_COLOR
  !$EXTERNAL_COMPONENT_FONT_COLOR=$swap
  ' don't swap them again below if C4_... included again
  !$componentColorsSwappedAlready=1
!endif

' Labels
' ##################################

!$COMPONENT_LEGEND_TEXT ?= "component"
!$EXTERNAL_COMPONENT_LEGEND_TEXT ?= "external component"

' Styling
' ##################################

UpdateElementStyle("component", $COMPONENT_BG_COLOR, $COMPONENT_FONT_COLOR, $COMPONENT_BORDER_COLOR, $shape=$DEFAULT_SHAPE, $legendText="$COMPONENT_LEGEND_TEXT")
UpdateElementStyle("external_component", $EXTERNAL_COMPONENT_BG_COLOR, $EXTERNAL_COMPONENT_FONT_COLOR, $EXTERNAL_COMPONENT_BORDER_COLOR, $shape=$DEFAULT_SHAPE, $legendText="$EXTERNAL_COMPONENT_LEGEND_TEXT")

' shortcuts with default colors
!unquoted procedure AddComponentTag($tagStereo, $bgColor="", $fontColor="", $borderColor="", $shadowing="", $shape="", $sprite="", $techn="", $legendText="", $legendSprite="", $borderStyle="", $borderThickness="")
  $addElementTagInclReuse("component", $tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, $shape, $sprite, $techn, $legendText, $legendSprite, $borderStyle, $borderThickness)
!endprocedure
!unquoted procedure AddExternalComponentTag($tagStereo, $bgColor="", $fontColor="", $borderColor="", $shadowing="", $shape="", $sprite="", $techn="", $legendText="", $legendSprite="", $borderStyle="", $borderThickness="")
  $addElementTagInclReuse("external_component", $tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, $shape, $sprite, $techn, $legendText, $legendSprite, $borderStyle, $borderThickness)
!endprocedure

' Layout
' ##################################

SetDefaultLegendEntries("person\nsystem\ncontainer\ncomponent\nexternal_person\nexternal_system\nexternal_container\nexternal_component\nenterprise_boundary\nsystem_boundary\ncontainer_boundary\nboundary")

!procedure LAYOUT_WITH_LEGEND()
hide stereotype
legend right
|<color:$LEGEND_TITLE_COLOR>**Legend**</color> |
|<$PERSON_BG_COLOR> person |
|<$SYSTEM_BG_COLOR> system |
|<$CONTAINER_BG_COLOR> container |
|<$COMPONENT_BG_COLOR> component |
|<$EXTERNAL_PERSON_BG_COLOR> external person |
|<$EXTERNAL_SYSTEM_BG_COLOR> external system |
|<$EXTERNAL_CONTAINER_BG_COLOR> external container |
|<$EXTERNAL_COMPONENT_BG_COLOR> external component |
endlegend
!endprocedure

' Elements
' ##################################

!unquoted procedure Component($alias, $label, $techn="", $descr="", $sprite="", $tags="", $link="", $baseShape="rectangle")
  $getElementLine($baseShape, "component", $alias, $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure

!unquoted procedure ComponentDb($alias, $label, $techn="", $descr="", $sprite="", $tags="", $link="")
  $getElementLine("database", "component", $alias, $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure

!unquoted procedure ComponentQueue($alias, $label, $techn="", $descr="", $sprite="", $tags="", $link="")
  $getElementLine("queue", "component", $alias, $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure

!unquoted procedure Component_Ext($alias, $label, $techn="", $descr="", $sprite="", $tags="", $link="", $baseShape="rectangle")
  $getElementLine($baseShape, "external_component", $alias, $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure

!unquoted procedure ComponentDb_Ext($alias, $label, $techn="", $descr="", $sprite="", $tags="", $link="")
  $getElementLine("database", "external_component", $alias, $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure

!unquoted procedure ComponentQueue_Ext($alias, $label, $techn="", $descr="", $sprite="", $tags="", $link="")
  $getElementLine("queue", "external_component", $alias, $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure


================================================
FILE: C4_Container.puml
================================================
' convert it with additional command line argument -DRELATIVE_INCLUDE="relative/absolute" to use locally
!if %variable_exists("RELATIVE_INCLUDE")
  !include ./C4_Context.puml
!else
  !include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Context.puml
!endif

' Scope: A single software system.
' Primary elements: Containers within the software system in scope.
' Supporting elements: People and software systems directly connected to the containers.
' Intended audience: Technical people inside and outside of the software development team; including software architects, developers and operations/support staff.

' Colors
' ##################################

!$CONTAINER_FONT_COLOR ?= $ELEMENT_FONT_COLOR
!$CONTAINER_BG_COLOR ?= "#438DD5"
!$CONTAINER_BORDER_COLOR ?= "#3C7FC0"

!$CONTAINER_BOUNDARY_COLOR ?= $BOUNDARY_COLOR
!$CONTAINER_BOUNDARY_BG_COLOR ?= $BOUNDARY_BG_COLOR
!$CONTAINER_BOUNDARY_BORDER_STYLE ?= $BOUNDARY_BORDER_STYLE

!$EXTERNAL_CONTAINER_FONT_COLOR ?= $ELEMENT_FONT_COLOR
!$EXTERNAL_CONTAINER_BG_COLOR ?= "#B3B3B3"
!$EXTERNAL_CONTAINER_BORDER_COLOR ?= "#A6A6A6"

' Labels
' ##################################

!$CONTAINER_LEGEND_TEXT ?= "container"
!$CONTAINER_BOUNDARY_TYPE ?= "container"
!$CONTAINER_BOUNDARY_LEGEND_TEXT ?= "container boundary"
!$EXTERNAL_CONTAINER_LEGEND_TEXT ?= "external container"

' Styling
' ##################################
UpdateElementStyle("container", $CONTAINER_BG_COLOR, $CONTAINER_FONT_COLOR, $CONTAINER_BORDER_COLOR, $shape=$DEFAULT_SHAPE, $legendText="$CONTAINER_LEGEND_TEXT")
UpdateElementStyle("external_container", $EXTERNAL_CONTAINER_BG_COLOR, $EXTERNAL_CONTAINER_FONT_COLOR, $EXTERNAL_CONTAINER_BORDER_COLOR, $shape=$DEFAULT_SHAPE, $legendText="$EXTERNAL_CONTAINER_LEGEND_TEXT")

UpdateBoundaryStyle("container", $bgColor=$CONTAINER_BOUNDARY_BG_COLOR, $fontColor=$CONTAINER_BOUNDARY_COLOR, $borderColor=$CONTAINER_BOUNDARY_COLOR, $type="$CONTAINER_BOUNDARY_TYPE", $shape=$DEFAULT_SHAPE, $legendText="$CONTAINER_BOUNDARY_LEGEND_TEXT")

' shortcuts with default colors
!unquoted procedure AddContainerTag($tagStereo, $bgColor="", $fontColor="", $borderColor="", $shadowing="", $shape="", $sprite="", $techn="", $legendText="", $legendSprite="", $borderStyle="", $borderThickness="")
  $addElementTagInclReuse("container", $tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, $shape, $sprite, $techn, $legendText, $legendSprite, $borderStyle, $borderThickness)
!endprocedure
!unquoted procedure AddExternalContainerTag($tagStereo, $bgColor="", $fontColor="", $borderColor="", $shadowing="", $shape="", $sprite="", $techn="", $legendText="", $legendSprite="", $borderStyle="", $borderThickness="")
  $addElementTagInclReuse("external_container", $tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, $shape, $sprite, $techn, $legendText, $legendSprite, $borderStyle, $borderThickness)
!endprocedure

!unquoted procedure UpdateContainerBoundaryStyle($bgColor=$CONTAINER_BOUNDARY_BG_COLOR, $fontColor=$CONTAINER_BOUNDARY_COLOR, $borderColor=$CONTAINER_BOUNDARY_COLOR, $shadowing="", $shape="", $type="Container", $legendText="", $borderStyle="", $borderThickness="", $sprite="", $legendSprite="")
  UpdateBoundaryStyle("container", $bgColor, $fontColor, $borderColor, $shadowing, $shape, $type, $legendText, $borderStyle, $borderThickness, $sprite, $legendSprite)
!endprocedure

' Layout
' ##################################

SetDefaultLegendEntries("person\nsystem\ncontainer\nexternal_person\nexternal_system\nexternal_container\nenterprise_boundary\nsystem_boundary\ncontainer_boundary\nboundary")

!procedure LAYOUT_WITH_LEGEND()
hide stereotype
legend right
|<color:$LEGEND_TITLE_COLOR>**Legend**</color> |
|<$PERSON_BG_COLOR> person |
|<$SYSTEM_BG_COLOR> system |
|<$CONTAINER_BG_COLOR> container |
|<$EXTERNAL_PERSON_BG_COLOR> external person |
|<$EXTERNAL_SYSTEM_BG_COLOR> external system |
|<$EXTERNAL_CONTAINER_BG_COLOR> external container |
endlegend
!endprocedure

' Elements
' ##################################

!unquoted procedure Container($alias, $label, $techn="", $descr="", $sprite="", $tags="", $link="", $baseShape="rectangle")
  $getElementLine($baseShape , "container", $alias, $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure

!unquoted procedure ContainerDb($alias, $label, $techn="", $descr="", $sprite="", $tags="", $link="")
  $getElementLine("database", "container", $alias, $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure

!unquoted procedure ContainerQueue($alias, $label, $techn="", $descr="", $sprite="", $tags="", $link="")
  $getElementLine("queue", "container", $alias, $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure

!unquoted procedure Container_Ext($alias, $label, $techn="", $descr="", $sprite="", $tags="", $link="", $baseShape="rectangle")
  $getElementLine($baseShape , "external_container", $alias, $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure

!unquoted procedure ContainerDb_Ext($alias, $label, $techn="", $descr="", $sprite="", $tags="", $link="")
  $getElementLine("database", "external_container", $alias, $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure

!unquoted procedure ContainerQueue_Ext($alias, $label, $techn="", $descr="", $sprite="", $tags="", $link="")
  $getElementLine("queue", "external_container", $alias, $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure

' Boundaries
' ##################################

!unquoted procedure Container_Boundary($alias, $label, $tags="", $link="", $descr = "")
  !if ($tags != "")
    !$allTags = $tags + '+container'
  !else
    !$allTags = 'container'
  !endif
  ' $type defined via $tag style
  Boundary($alias, $label, "", $allTags, $link, $descr)
!endprocedure


================================================
FILE: C4_Context.puml
================================================
' convert it with additional command line argument -DRELATIVE_INCLUDE="relative/absolute" to use locally
!if %variable_exists("RELATIVE_INCLUDE")
  !include ./C4.puml
!else
  !include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4.puml
!endif

' Scope: A single software system.
' Primary elements: The software system in scope.
' Supporting elements: People and software systems directly connected to the software system in scope.
' Intended audience: Everybody, both technical and non-technical people, inside and outside of the software development team.

' Colors
' ##################################

!$PERSON_FONT_COLOR ?= $ELEMENT_FONT_COLOR
!$PERSON_BG_COLOR ?= "#08427B"
!$PERSON_BORDER_COLOR ?= "#073B6F"

!$EXTERNAL_PERSON_FONT_COLOR ?= $ELEMENT_FONT_COLOR
!$EXTERNAL_PERSON_BG_COLOR ?= "#686868"
!$EXTERNAL_PERSON_BORDER_COLOR ?= "#8A8A8A"

!$SYSTEM_FONT_COLOR ?= $ELEMENT_FONT_COLOR
!$SYSTEM_BG_COLOR ?= "#1168BD"
!$SYSTEM_BORDER_COLOR ?= "#3C7FC0"

!$SYSTEM_BOUNDARY_COLOR ?= $BOUNDARY_COLOR
!$SYSTEM_BOUNDARY_BG_COLOR ?= $BOUNDARY_BG_COLOR
!$SYSTEM_BOUNDARY_BORDER_STYLE ?= $BOUNDARY_BORDER_STYLE

!$EXTERNAL_SYSTEM_FONT_COLOR ?= $ELEMENT_FONT_COLOR
!$EXTERNAL_SYSTEM_BG_COLOR ?= "#999999"
!$EXTERNAL_SYSTEM_BORDER_COLOR ?= "#8A8A8A"

!$ENTERPRISE_BOUNDARY_COLOR ?= $BOUNDARY_COLOR
!$ENTERPRISE_BOUNDARY_BG_COLOR ?= $BOUNDARY_BG_COLOR
!$ENTERPRISE_BOUNDARY_BORDER_STYLE ?= $BOUNDARY_BORDER_STYLE

' Labels
' ##################################

!$PERSON_LEGEND_TEXT ?= "person"
!$EXTERNAL_PERSON_LEGEND_TEXT ?= "external person"

!$SYSTEM_LEGEND_TEXT ?= "system"
!$SYSTEM_BOUNDARY_TYPE ?= "system"
!$SYSTEM_BOUNDARY_LEGEND_TEXT ?= "system boundary"
!$EXTERNAL_SYSTEM_LEGEND_TEXT ?= "external system"

!$ENTERPRISE_BOUNDARY_TYPE ?= "enterprise"
!$ENTERPRISE_BOUNDARY_LEGEND_TEXT ?= "enterprise boundary"

' Styling
' ##################################

UpdateElementStyle("person", $PERSON_BG_COLOR, $PERSON_FONT_COLOR, $PERSON_BORDER_COLOR, $shape=$DEFAULT_SHAPE, $legendText="$PERSON_LEGEND_TEXT")
UpdateElementStyle("external_person", $EXTERNAL_PERSON_BG_COLOR, $EXTERNAL_PERSON_FONT_COLOR, $EXTERNAL_PERSON_BORDER_COLOR, $shape=$DEFAULT_SHAPE, $legendText="$EXTERNAL_PERSON_LEGEND_TEXT")
UpdateElementStyle("system", $SYSTEM_BG_COLOR, $SYSTEM_FONT_COLOR, $SYSTEM_BORDER_COLOR, $shape=$DEFAULT_SHAPE, $legendText="$SYSTEM_LEGEND_TEXT")
UpdateElementStyle("external_system", $EXTERNAL_SYSTEM_BG_COLOR, $EXTERNAL_SYSTEM_FONT_COLOR, $EXTERNAL_SYSTEM_BORDER_COLOR, $shape=$DEFAULT_SHAPE, $legendText="$EXTERNAL_SYSTEM_LEGEND_TEXT")

UpdateBoundaryStyle("system", $bgColor=$SYSTEM_BOUNDARY_BG_COLOR, $fontColor=$SYSTEM_BOUNDARY_COLOR, $borderColor=$SYSTEM_BOUNDARY_COLOR, $type="$SYSTEM_BOUNDARY_TYPE", $shape=$DEFAULT_SHAPE, $legendText="$SYSTEM_BOUNDARY_LEGEND_TEXT")
UpdateBoundaryStyle("enterprise", $bgColor=$ENTERPRISE_BOUNDARY_BG_COLOR, $fontColor=$ENTERPRISE_BOUNDARY_COLOR, $borderColor=$ENTERPRISE_BOUNDARY_COLOR, $type="$ENTERPRISE_BOUNDARY_TYPE", $shape=$DEFAULT_SHAPE, $legendText="$ENTERPRISE_BOUNDARY_LEGEND_TEXT")

' shortcuts with default colors
!unquoted procedure AddPersonTag($tagStereo, $bgColor="", $fontColor="", $borderColor="", $shadowing="", $shape="", $sprite="", $legendText="", $legendSprite="", $type="", $borderStyle="", $borderThickness="")
  $addElementTagInclReuse("person", $tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, $shape, $sprite, $type, $legendText, $legendSprite, $borderStyle, $borderThickness)
!endprocedure
!unquoted procedure AddExternalPersonTag($tagStereo, $bgColor="", $fontColor="", $borderColor="", $shadowing="", $shape="", $sprite="", $legendText="", $legendSprite="", $type="", $borderStyle="", $borderThickness="")
  $addElementTagInclReuse("external_person", $tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, $shape, $sprite, $type, $legendText, $legendSprite, $borderStyle, $borderThickness)
!endprocedure
!unquoted procedure AddSystemTag($tagStereo, $bgColor="", $fontColor="", $borderColor="", $shadowing="", $shape="", $sprite="", $legendText="", $legendSprite="", $type="", $borderStyle="", $borderThickness="")
  $addElementTagInclReuse("system", $tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, $shape, $sprite, $type, $legendText, $legendSprite, $borderStyle, $borderThickness)
!endprocedure
!unquoted procedure AddExternalSystemTag($tagStereo, $bgColor="", $fontColor="", $borderColor="", $shadowing="", $shape="", $sprite="", $legendText="", $legendSprite="", $type="", $borderStyle="", $borderThickness="")
  $addElementTagInclReuse("external_system", $tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, $shape, $sprite, $type, $legendText, $legendSprite, $borderStyle, $borderThickness)
!endprocedure

!unquoted procedure UpdateEnterpriseBoundaryStyle($bgColor=$ENTERPRISE_BOUNDARY_BG_COLOR, $fontColor=$ENTERPRISE_BOUNDARY_COLOR, $borderColor=$ENTERPRISE_BOUNDARY_COLOR, $shadowing="", $shape="", $type="Enterprise", $legendText="", $borderStyle="", $borderThickness="", $sprite="", $legendSprite="")
  UpdateBoundaryStyle("enterprise", $bgColor, $fontColor, $borderColor, $shadowing, $shape, $type, $legendText, $borderStyle, $borderThickness, $sprite, $legendSprite)
!endprocedure
!unquoted procedure UpdateSystemBoundaryStyle($bgColor=$SYSTEM_BOUNDARY_BG_COLOR, $fontColor=$SYSTEM_BOUNDARY_COLOR, $borderColor=$SYSTEM_BOUNDARY_COLOR, $shadowing="", $shape="", $type="System", $legendText="", $borderStyle="", $borderThickness="", $sprite="", $legendSprite="")
  UpdateBoundaryStyle("system", $bgColor, $fontColor, $borderColor, $shadowing, $shape, $type, $legendText, $borderStyle, $borderThickness, $sprite, $legendSprite)
!endprocedure

' Sprites
' ##################################

sprite $person [48x48/16] {
000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000
0000000000000000000049BCCA7200000000000000000000
0000000000000000006EFFFFFFFFB3000000000000000000
00000000000000001CFFFFFFFFFFFF700000000000000000
0000000000000001EFFFFFFFFFFFFFF80000000000000000
000000000000000CFFFFFFFFFFFFFFFF6000000000000000
000000000000007FFFFFFFFFFFFFFFFFF100000000000000
00000000000001FFFFFFFFFFFFFFFFFFF900000000000000
00000000000006FFFFFFFFFFFFFFFFFFFF00000000000000
0000000000000BFFFFFFFFFFFFFFFFFFFF40000000000000
0000000000000EFFFFFFFFFFFFFFFFFFFF70000000000000
0000000000000FFFFFFFFFFFFFFFFFFFFF80000000000000
0000000000000FFFFFFFFFFFFFFFFFFFFF80000000000000
0000000000000DFFFFFFFFFFFFFFFFFFFF60000000000000
0000000000000AFFFFFFFFFFFFFFFFFFFF40000000000000
00000000000006FFFFFFFFFFFFFFFFFFFE00000000000000
00000000000000EFFFFFFFFFFFFFFFFFF800000000000000
000000000000007FFFFFFFFFFFFFFFFFF100000000000000
000000000000000BFFFFFFFFFFFFFFFF5000000000000000
0000000000000001DFFFFFFFFFFFFFF70000000000000000
00000000000000000BFFFFFFFFFFFF500000000000000000
0000000000000000005DFFFFFFFFA1000000000000000000
0000000000000000000037ABB96100000000000000000000
000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000
000000000000025788300000000005886410000000000000
000000000007DFFFFFFD9643347BFFFFFFFB400000000000
0000000004EFFFFFFFFFFFFFFFFFFFFFFFFFFB1000000000
000000007FFFFFFFFFFFFFFFFFFFFFFFFFFFFFD200000000
00000006FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE10000000
0000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB0000000
000000BFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5000000
000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD000000
000009FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF200000
00000DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF600000
00000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF800000
00001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA00000
00001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB00000
00001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB00000
00001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB00000
00001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA00000
00000EFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF700000
000006FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE100000
0000008FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD3000000
000000014555555555555555555555555555555300000000
000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000
}

sprite $person2 [48x48/16] {
0000000000000000000049BCCA7200000000000000000000
0000000000000000006EFFFFFFFFB3000000000000000000
00000000000000001CFFFFFFFFFFFF700000000000000000
0000000000000001EFFFFFFFFFFFFFF80000000000000000
000000000000000CFFFFFFFFFFFFFFFF6000000000000000
000000000000007FFFFFFFFFFFFFFFFFF100000000000000
00000000000001FFFFFFFFFFFFFFFFFFF900000000000000
00000000000006FFFFFFFFFFFFFFFFFFFF00000000000000
0000000000000BFFFFFFFFFFFFFFFFFFFF40000000000000
0000000000000EFFFFFFFFFFFFFFFFFFFF70000000000000
0000000000000FFFFFFFFFFFFFFFFFFFFF80000000000000
0000000000000FFFFFFFFFFFFFFFFFFFFF80000000000000
0000000000000DFFFFFFFFFFFFFFFFFFFF60000000000000
0000000000000AFFFFFFFFFFFFFFFFFFFF40000000000000
00000000000006FFFFFFFFFFFFFFFFFFFE00000000000000
00000000000000EFFFFFFFFFFFFFFFFFF800000000000000
000000000000007FFFFFFFFFFFFFFFFFF100000000000000
000000000000000BFFFFFFFFFFFFFFFF5000000000000000
0000000000000001DFFFFFFFFFFFFFF70000000000000000
00000000000000000BFFFFFFFFFFFF500000000000000000
0000000000000000005DFFFFFFFFA1000000000000000000
0000000000000000000037ABB96100000000000000000000
000000000002578888300000000005888864100000000000
0000000007DFFFFFFFFD9643347BFFFFFFFFFB4000000000
00000004EFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB10000000
0000007FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD2000000
000006FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE100000
00003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB00000
0000BFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF50000
0003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD0000
0009FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2000
000DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6000
000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000
001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB000
001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB000
001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB000
001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA000
000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000
000DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6000
0009FFFFFFFF8FFFFFFFFFFFFFFFFFFFFFF8FFFFFFFF2000
0003FFFFFFFF8FFFFFFFFFFFFFFFFFFFFFF8FFFFFFFD0000
0000BFFFFFFF8FFFFFFFFFFFFFFFFFFFFFF8FFFFFFF50000
00003FFFFFFF8FFFFFFFFFFFFFFFFFFFFFF8FFFFFFB00000
000006FFFFFF8FFFFFFFFFFFFFFFFFFFFFF8FFFFFE100000
0000007FFFFF8FFFFFFFFFFFFFFFFFFFFFF8FFFFD2000000
00000004EFFF8FFFFFFFFFFFFFFFFFFFFFF8FFFB10000000
0000000007DF8FFFFFFFFFFFFFFFFFFFFFF8FB4000000000
000000000002578888888888888888888864100000000000
}

sprite $robot [48x48/16] {
000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000
000000000005BFFFFFFFFFFFFFFFFFFFFFE9100000000000
0000000000AFFFFFFFFFFFFFFFFFFFFFFFFFE30000000000
0000000007FFFFFFFFFFFFFFFFFFFFFFFFFFFE1000000000
000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000000000
000000004FFFFFFFFFFFFFFFFFFFFFFFFFFFFFC000000000
000000005FFFFFFFFFFFFFFFFFFFFFFFFFFFFFD000000000
000000005FFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000
000000005FFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000
000699405FFFFFFC427FFFFFFFFFC427FFFFFFE009982000
008FFF705FFFFFE10006FFFFFFFE00007FFFFFE00FFFF100
00CFFF705FFFFFA00001FFFFFFF900002FFFFFE00FFFF500
00DFFF705FFFFFB00002FFFFFFFA00003FFFFFE00FFFF500
00DFFF705FFFFFF4000AFFFFFFFF3000BFFFFFE00FFFF500
00DFFF705FFFFFFFA8DFFFFFFFFFFA8DFFFFFFE00FFFF500
00DFFF705FFFFFFFFFFFFFFFFFFFFFFFFFFFFFE00FFFF500
00DFFF705FFFFFFFFFFFFFFFFFFFFFFFFFFFFFE00FFFF500
00DFFF705FFFFFFFFFFFFFFFFFFFFFFFFFFFFFE00FFFF500
00DFFF705FFFFFFFFFFFFFFFFFFFFFFFFFFFFFE00FFFF500
00DFFF705FFFFFFFFFFFFFFFFFFFFFFFFFFFFFE00FFFF500
00CFFF705FFFFFF87777777777777777CFFFFFE00FFFF500
008FFF705FFFFFF100000000000000009FFFFFE00FFFF100
000699405FFFFFF76666666666666666CFFFFFE009982000
000000005FFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000
000000005FFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000
000000004FFFFFFFFFFFFFFFFFFFFFFFFFFFFFC000000000
000000000EFFFFFFFFFFFFFFFFFFFFFFFFFFFF7000000000
0000000005FFFFFFFFFFFFFFFFFFFFFFFFFFFD0000000000
00000000004CFFFFFFFFFFFFFFFFFFFFFFFF910000000000
000000000000011111111111111111111110000000000000
000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000
}

sprite $robot2 [48x48/16] {
000000000000000088888888888888880000000000000000
000000000000000AFFFFFFFFFFFFFFFFA000000000000000
00000000000000CFFFFFFFFFFFFFFFFFFC00000000000000
00000000000004EFFFFFFFFFFFFFFFFFFE40000000000000
0000000000000AFFFFFFFFFFFFFFFFFFFFA0000000000000
00000000000008FFFFFFFFFFFFFFFFFFFF80000000000000
00000000000008FFFFFFFFFFFFFFFFFFFF80000000000000
00000000000008FFFFFFFFFFFFFFFFFFFF80000000000000
00000000000888FFFFFFFFFFFFFFFFFFFF88800000000000
00000000008FF8FFFFFFFFFFFFFFFFFFFF8FF80000000000
00000000008FF8FFFFFFFFFFFFFFFFFFFF8FF80000000000
00000000008FF8FFFFFFFFFFFFFFFFFFFF8FF80000000000
00000000008FF8FFFFFFFFFFFFFFFFFFFF8FF80000000000
00000000008FF8FFFFFFFFFFFFFFFFFFFF8FF80000000000
00000000008FF8FFFFFFFFFFFFFFFFFFFF8FF80000000000
00000000000888FFFFFFFFFFFFFFFFFFFF88800000000000
00000000000008FFFFFFFFFFFFFFFFFFFF80000000000000
00000000000008FFFFFFFFFFFFFFFFFFFF80000000000000
00000000000008FFFFFFFFFFFFFFFFFFFF80000000000000
00000000000008FFFFFFFFFFFFFFFFFFFF80000000000000
00000000000008FFFFFFFFFFFFFFFFFFFF80000000000000
00000000000004CFFFFFFFFFFFFFFFFFFC40000000000000
000000488888848CFFFFFFFFFFFFFFFFC848888884000000
00000CFFFFFFFFC888888888888888888CFFFFFFFFC00000
00008FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80000
0000CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0000
0008FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000
0008FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000
0008FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000
0008FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000
0008FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000
0008FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000
0008FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000
0008FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000
0008FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000
0008FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000
0008FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000
0008FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000
0008FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000
0008FFFFFFFF8FFFFFFFFFFFFFFFFFFFFFF8FFFFFFFF8000
0008FFFFFFFF8FFFFFFFFFFFFFFFFFFFFFF8FFFFFFFF8000
0008FFFFFFFF8FFFFFFFFFFFFFFFFFFFFFF8FFFFFFFF8000
0008FFFFFFFF8FFFFFFFFFFFFFFFFFFFFFF8FFFFFFFF8000
0000CFFFFFFF8FFFFFFFFFFFFFFFFFFFFFF8FFFFFFFC0000
00008FFFFFFF8FFFFFFFFFFFFFFFFFFFFFF8FFFFFFF80000
00000CFFFFFF8FFFFFFFFFFFFFFFFFFFFFF8FFFFFFC00000
000000488887578888888888888888888864688884000000
000000000000000000000000000000000000000000000000
}

' Layout
' ##################################

SetDefaultLegendEntries("person\nsystem\nexternal_person\nexternal_system\nenterprise_boundary\nsystem_boundary\nboundary")

!procedure LAYOUT_WITH_LEGEND()
hide stereotype
legend right
|<color:$LEGEND_TITLE_COLOR>**Legend**</color> |
|<$PERSON_BG_COLOR> person |
|<$SYSTEM_BG_COLOR> system|
|<$EXTERNAL_PERSON_BG_COLOR> external person |
|<$EXTERNAL_SYSTEM_BG_COLOR> external system |
endlegend
!endprocedure

!global $defaultPersonSprite = "person"
!$dummy = $restoreEmpty("person", "sprite", $defaultPersonSprite, %true())
UpdateElementStyle("person")
' workaround of plantuml.jar bug - person overwrites external_person setting
!$dummy = $restoreEmpty("external_person", "sprite", $defaultPersonSprite, %true())
UpdateElementStyle("external_person")
!global $portraitPerson = "false"

!procedure $clearPersonRestore()
  !$dummy = $clearRestore("person", "sprite")
  !$dummy = $clearRestore("person", "legendSprite")
  %set_variable_value("$" + "person" + "ElementTagSprite", "")
  UpdateElementStyle("person")
  ' workaround of plantuml.jar bug - person overwrites external_person setting
  !$dummy = $clearRestore("external_person", "sprite")
  !$dummy = $clearRestore("external_person", "legendSprite")
  %set_variable_value("$" + "external_person" + "ElementTagSprite", "")
  UpdateElementStyle("external_person")
!endprocedure

!procedure HIDE_PERSON_SPRITE()
  !$defaultPersonSprite = ""
  !$portraitPerson = "false"
  $clearPersonRestore()
!endprocedure

!unquoted procedure SHOW_PERSON_SPRITE($sprite="")
  !if ($sprite == "")
    !$defaultPersonSprite = "person"
  !else
    !$defaultPersonSprite = $sprite
  !endif
  !$dummy = $restoreEmpty("person", "sprite", $defaultPersonSprite, %true())
  UpdateElementStyle("person")
  ' workaround of plantuml.jar bug - person overwrites external_person setting
  !$dummy = $restoreEmpty("external_person", "sprite", $defaultPersonSprite, %true())
  UpdateElementStyle("external_person")
  !$portraitPerson = "false"
!endprocedure

!unquoted procedure SHOW_PERSON_PORTRAIT()
  !$defaultPersonSprite = ""
  !$portraitPerson = "portrait"
  $clearPersonRestore()
!endprocedure

!unquoted procedure SHOW_PERSON_OUTLINE()
  !$defaultPersonSprite = ""
  !$portraitPerson = "outline"
  $clearPersonRestore()
!endprocedure

' Elements
' ##################################

!function $getPerson($label, $type, $descr, $sprite)
  !if ($sprite == "") && ($defaultPersonSprite != "")
    !$sprite = $defaultPersonSprite
  !endif
  !return $getElementBase($label, $type, $descr, $sprite)
!endfunction

!unquoted procedure Person($alias, $label, $descr="", $sprite="", $tags="", $link="", $type="")
!$sprite=$toElementArg($sprite, $tags, "ElementTagSprite", "person")
' $type reuses $techn definition of $tags
!$type=$toElementArg($type, $tags, "ElementTagTechn", "person")
!if ($portraitPerson == "portrait") && ($sprite == "")
actor "$getPerson($label, $type, $descr, $sprite)$getProps()" $toStereos("person", $tags) as $alias $getLink($link)
!elseif ($portraitPerson == "outline") && ($sprite == "")
person "$getPerson($label, $type, $descr, $sprite)$getProps()" $toStereos("person", $tags) as $alias $getLink($link)
!else
rectangle "$getPerson($label, $type, $descr, $sprite)$getProps()" $toStereos("person", $tags) as $alias $getLink($link)
!endif
!endprocedure

!unquoted procedure Person_Ext($alias, $label, $descr="", $sprite="", $tags="", $link="", $type="")
!$sprite=$toElementArg($sprite, $tags, "ElementTagSprite", "external_person")
' $type reuses $techn definition of $tags
!$type=$toElementArg($type, $tags, "ElementTagTechn", "external_person")
!if ($portraitPerson == "portrait") && ($sprite == "")
actor "$getPerson($label, $type, $descr, $sprite)$getProps()" $toStereos("external_person", $tags) as $alias $getLink($link)
!elseif ($portraitPerson == "outline") && ($sprite == "")
person "$getPerson($label, $type, $descr, $sprite)$getProps()" $toStereos("external_person", $tags) as $alias $getLink($link)
!else
rectangle "$getPerson($label, $type, $descr, $sprite)$getProps()" $toStereos("external_person", $tags) as $alias $getLink($link)
!endif
!endprocedure

!unquoted procedure System($alias, $label, $descr="", $sprite="", $tags="", $link="", $type="", $baseShape="rectangle")
  ' $type reuses $techn definition of $tags
  $getElementLine($baseShape, "system", $alias, $label, $type, $descr, $sprite, $tags, $link)
!endprocedure

!unquoted procedure SystemDb($alias, $label, $descr="", $sprite="", $tags="", $link="", $type="")
  ' $type reuses $techn definition of $tags
  $getElementLine("database", "system", $alias, $label, $type, $descr, $sprite, $tags, $link)
!endprocedure

!unquoted procedure SystemQueue($alias, $label, $descr="", $sprite="", $tags="", $link="", $type="")
  ' $type reuses $techn definition of $tags
  $getElementLine("queue", "system", $alias, $label, $type, $descr, $sprite, $tags, $link)
!endprocedure

!unquoted procedure System_Ext($alias, $label, $descr="", $sprite="", $tags="", $link="", $type="", $baseShape="rectangle")
  ' $type reuses $techn definition of $tags
  $getElementLine($baseShape , "external_system", $alias, $label, $type, $descr, $sprite, $tags, $link)
!endprocedure

!unquoted procedure SystemDb_Ext($alias, $label, $descr="", $sprite="", $tags="", $link="", $type="")
  ' $type reuses $techn definition of $tags
  $getElementLine("database", "external_system", $alias, $label, $type, $descr, $sprite, $tags, $link)
!endprocedure

!unquoted procedure SystemQueue_Ext($alias, $label, $descr="", $sprite="", $tags="", $link="", $type="")
  ' $type reuses $techn definition of $tags
  $getElementLine("queue", "external_system", $alias, $label, $type, $descr, $sprite, $tags, $link)
!endprocedure

' Boundaries
' ##################################

!unquoted procedure Enterprise_Boundary($alias, $label, $tags="", $link="", $descr = "")
  !if ($tags != "")
    !$allTags = $tags + '+enterprise'
  !else
    !$allTags = 'enterprise'
  !endif
  ' $type defined via $tag style
  Boundary($alias, $label, "", $allTags, $link, $descr)
!endprocedure

!unquoted procedure System_Boundary($alias, $label, $tags="", $link="", $descr = "")
  !if ($tags != "")
    !$allTags = $tags + '+system'
  !else
    !$allTags = 'system'
  !endif
  ' $type defined via $tag style
  Boundary($alias, $label, "", $allTags, $link, $descr)
!endprocedure


================================================
FILE: C4_Deployment.puml
================================================
' convert it with additional command line argument -DRELATIVE_INCLUDE="relative/absolute" to use locally
!if %variable_exists("RELATIVE_INCLUDE")
  !include ./C4_Container.puml
!else
  !include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Container.puml
!endif

' Colors
' ##################################

!$NODE_FONT_COLOR ?= "#000000"
!$NODE_BG_COLOR ?= "#FFFFFF"
!$NODE_BORDER_COLOR ?= "#A2A2A2"

' New C4 style automatically swaps font and background colors, but with those settings the component/node background would be black.
' Therefore the colors should be displayed unchanged (the init colors have to be swapped too).
!$NEW_C4_USE_ORIGINAL_NODE_COLORS ?= 1
!$nodeColorsSwappedAlready ?= 0
!if (NEW_C4_STYLE == 1 && $NEW_C4_USE_ORIGINAL_NODE_COLORS == 1 && $nodeColorsSwappedAlready == 0)
  !$swap=$NODE_BG_COLOR
  !$NODE_BG_COLOR=$NODE_FONT_COLOR
  !$NODE_FONT_COLOR=$swap
  ' don't swap them again below if C4_... included again
  !$nodeColorsSwappedAlready=1
!endif

' Labels
' ##################################

!$NODE_LEGEND_TEXT ?= "node"

' Styling
' ##################################

' PlantUML supports no automatic line breaks of "PlantUML containers" (C4 Deployment_Node is a "PlantUML container")
' therefore (Deployment_)Node() implements an automatic line break based on spaces (like in all other objects).
' If a $type contains \n then these are used (and no automatic space based line breaks are done)
' $NODE_TYPE_MAX_CHAR_WIDTH defines the automatic line break position
!$NODE_TYPE_MAX_CHAR_WIDTH ?= 35
!$NODE_DESCR_MAX_CHAR_WIDTH ?= 32

UpdateElementStyle("node", $bgColor=$NODE_BG_COLOR, $fontColor=$NODE_FONT_COLOR, $borderColor=$NODE_BORDER_COLOR, $shape=$DEFAULT_SHAPE, $legendText="$NODE_LEGEND_TEXT")
skinparam rectangle<<node>> {
    FontStyle normal
}

' shortcuts with default colors
' node specific: $techn is only used in old scripts, new scripts uses $type ($techn has to remain, it could be called via named argument)
!unquoted procedure AddNodeTag($tagStereo, $bgColor="", $fontColor="", $borderColor="", $shadowing="", $shape="", $sprite="", $type="", $legendText="", $legendSprite="", $techn="", $borderStyle="", $borderThickness="")
  !$type=$type+$techn
  $addElementTagInclReuse("node", $tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, $shape, $sprite, $type, $legendText, $legendSprite, $borderStyle, $borderThickness)
!endprocedure

' Layout
' ##################################

' comment if node should not be added to legend. No calculated legend extension required
SetDefaultLegendEntries("person\nsystem\ncontainer\nexternal_person\nexternal_system\nexternal_container\nnode\nenterprise_boundary\nsystem_boundary\ncontainer_boundary\nboundary")

' Line breaks
' ##################################

' $breakTechn() in C4 supports //...//; $breakNode() in C4_Deployment supports no //....//
!unquoted function $breakNode($text, $widthStr)
  !$usedNewLine = '</size>\n<size:'+$TECHN_FONT_SIZE+'>'
  !return $breakText($text, $usedNewLine, $widthStr)
!endfunction

' Elements
' ##################################

!function $getNode($label, $type, $descr, $sprite)
  !$nodeText = ""
  !if ($sprite != "")
    !$nodeText = $nodeText + $getSprite($sprite) + '\n'
  !endif
  !$nodeText = $nodeText + '== ' + $breakText($label, "\n== ")
  !if ($type != "")
    !$nodeText = $nodeText + '\n<size:' + $TECHN_FONT_SIZE + '>[' + $breakNode($type, $NODE_TYPE_MAX_CHAR_WIDTH) + ']</size>'
  !endif
  !if ($descr != "")
    !$nodeText = $nodeText + '\n\n' + $breakDescr($descr, $NODE_DESCR_MAX_CHAR_WIDTH)
  !endif
  !return $nodeText 
!endfunction

!function $getNode_L($label, $type, $descr, $sprite)
  !$nodeText = ""
  !if ($sprite != "")
    !$nodeText = $nodeText + $getSprite($sprite) + '\l'
  !endif
  !$nodeText = $nodeText + '== ' + $breakText($label, "\l== ")
  !if ($type != "")
    !$nodeText = $nodeText + '\l<size:' + $TECHN_FONT_SIZE + '>[' + $breakNode($type, $NODE_TYPE_MAX_CHAR_WIDTH) + ']</size>'
  !endif
  !if ($descr != "")
    !$nodeText = $nodeText + '\l\l' + $breakDescr($descr, $NODE_DESCR_MAX_CHAR_WIDTH)
  !endif
  !return $nodeText 
!endfunction

!function $getNode_R($label, $type, $descr, $sprite)
  !$nodeText = ""
  !if ($sprite != "")
    !$nodeText = $nodeText + $getSprite($sprite) + '\r'
  !endif
  !$nodeText = $nodeText + '== ' + $breakText($label, "\r== ")
  !if ($type != "")
    !$nodeText = $nodeText + '\r<size:' + $TECHN_FONT_SIZE + '>[' + $breakNode($type, $NODE_TYPE_MAX_CHAR_WIDTH) + ']</size>'
  !endif
  !if ($descr != "")
    !$nodeText = $nodeText + '\r\r' + $breakDescr($descr, $NODE_DESCR_MAX_CHAR_WIDTH)
  !endif
  !return $nodeText 
!endfunction

!unquoted procedure Deployment_Node($alias, $label, $type="", $descr="", $sprite="", $tags="", $link="")
!$sprite=$toElementArg($sprite, $tags, "ElementTagSprite", "node")
' nodes $type reuses $techn definition of $tags
!$type=$toElementArg($type, $tags, "ElementTagTechn", "node")
rectangle "$getNode($label, $type, $descr, $sprite)$getProps()" $toStereos("node",$tags) as $alias $getLink($link)
!endprocedure

!unquoted procedure Deployment_Node_L($alias, $label, $type="", $descr="", $sprite="", $tags="", $link="")
!$sprite=$toElementArg($sprite, $tags, "ElementTagSprite", "node")
' nodes $type reuses $techn definition of $tags
!$type=$toElementArg($type, $tags, "ElementTagTechn", "node")
rectangle "$getNode_L($label, $type, $descr, $sprite)$getProps_L()" $toStereos("node",$tags) as $alias $getLink($link)
!endprocedure

!unquoted procedure Deployment_Node_R($alias, $label, $type="", $descr="", $sprite="", $tags="", $link="")
!$sprite=$toElementArg($sprite, $tags, "ElementTagSprite", "node")
' nodes $type reuses $techn definition of $tags
!$type=$toElementArg($type, $tags, "ElementTagTechn", "node")
rectangle "$getNode_R($label, $type, $descr, $sprite)$getProps_R()" $toStereos("node",$tags) as $alias $getLink($link)
!endprocedure

!unquoted procedure Node($alias, $label, $type="", $descr="", $sprite="", $tags="", $link="")
!$sprite=$toElementArg($sprite, $tags, "ElementTagSprite", "node")
' nodes $type reuses $techn definition of $tags
!$type=$toElementArg($type, $tags, "ElementTagTechn", "node")
rectangle "$getNode($label, $type, $descr, $sprite)$getProps()" $toStereos("node",$tags) as $alias $getLink($link)
!endprocedure

!unquoted procedure Node_L($alias, $label, $type="", $descr="", $sprite="", $tags="", $link="")
!$sprite=$toElementArg($sprite, $tags, "ElementTagSprite", "node")
' nodes $type reuses $techn definition of $tags
!$type=$toElementArg($type, $tags, "ElementTagTechn", "node")
rectangle "$getNode_L($label, $type, $descr, $sprite)$getProps_L()" $toStereos("node",$tags) as $alias $getLink($link)
!endprocedure

!unquoted procedure Node_R($alias, $label, $type="", $descr="", $sprite="", $tags="", $link="")
!$sprite=$toElementArg($sprite, $tags, "ElementTagSprite", "node")
' nodes $type reuses $techn definition of $tags
!$type=$toElementArg($type, $tags, "ElementTagTechn", "node")
rectangle "$getNode_R($label, $type, $descr, $sprite)$getProps_R()" $toStereos("node",$tags) as $alias $getLink($link)
!endprocedure


================================================
FILE: C4_Dynamic.puml
================================================
' convert it with additional command line argument -DRELATIVE_INCLUDE="relative/absolute" to use locally
!if %variable_exists("RELATIVE_INCLUDE")
  !include ./C4_Component.puml
!else
  !include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Component.puml
!endif

' Scope: Interactions in an enterprise, software system or container.
' Primary and supporting elements: Depends on the diagram scope -
'     enterprise - people and software systems Related to the enterprise in scope
'     software system - see system context or container diagrams,
'     container - see component diagram.
' Intended audience: Technical and non-technical people, inside and outside of the software development team.

' Dynamic diagram introduces (automatically) numbered interactions:
'     (lowercase) increment($offset=1): increase current index (procedure which has no direct output)
'     (lowercase) setIndex($new_index): set the new index (procedure which has no direct output)
'
'     (Uppercase) LastIndex(): return the last used index (function which can be used as argument)
'     (Uppercase) Index($offset=1): returns current index and calculates next index (function which can be used as argument)
'     (Uppercase) SetIndex($new_index): returns new set index and calculates next index (function which can be used as argument)

' Relationship override
' ##################################

' Relationship
' ##################################

!unquoted procedure Rel_($e_index, $alias1, $alias2, $label, $direction)
$getRel($direction, $alias1, $alias2, $e_index + ": " + $label, "", "", "", "", "")
!endprocedure
!unquoted procedure Rel_($e_index, $alias1, $alias2, $label, $techn, $direction)
$getRel($direction, $alias1, $alias2, $e_index + ": " + $label, $techn, "", "", "", "")
!endprocedure

' all RelIndex... calls are outdated, Rel(..., $index=...) calls should be used !!!!

' first Rel() supports the $index argument too; second Rel() overwrites C4.puml definition
!unquoted procedure Rel($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="", $index="")
!$pre = $getPrefix($index)
$getRel("-->>", $from, $to, $pre + $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure
!unquoted procedure Rel($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="")
Rel($from, $to, $label, $techn, $descr, $sprite, $tags, $link, "")
!endprocedure
!unquoted procedure RelIndex($e_index, $from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="")
$getRel("-->>", $from, $to, $e_index + ": " + $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure

!unquoted procedure Rel_Back($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="", $index="")
!$pre = $getPrefix($index)
$getRel("<<--", $from, $to, $pre + $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure
!unquoted procedure Rel_Back($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="")
Rel_Back($from, $to, $label, $techn, $descr, $sprite, $tags, $link, "")
!endprocedure
!unquoted procedure RelIndex_Back($e_index, $from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="")
$getRel("<<--", $from, $to, $e_index + ": " + $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure

!unquoted procedure Rel_Neighbor($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="", $index="")
!$pre = $getPrefix($index)
$getRel("->>", $from, $to, $pre + $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure
!unquoted procedure Rel_Neighbor($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="")
Rel_Neighbor($from, $to, $label, $techn, $descr, $sprite, $tags, $link, "")
!endprocedure
!unquoted procedure RelIndex_Neighbor($e_index, $from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="")
$getRel("->>", $from, $to, $e_index + ": " + $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure

!unquoted procedure Rel_Back_Neighbor($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="", $index="")
!$pre = $getPrefix($index)
$getRel("<<-", $from, $to, $pre + $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure
!unquoted procedure Rel_Back_Neighbor($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="")
Rel_Back_Neighbor($from, $to, $label, $techn, $descr, $sprite, $tags, $link, "")
!endprocedure
!unquoted procedure RelIndex_Back_Neighbor($e_index, $from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="")
$getRel("<<-", $from, $to, $e_index + ": " + $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure

!unquoted procedure Rel_D($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="", $index="")
!$pre = $getPrefix($index)
$getRel($down("-","->>"), $from, $to, $pre + $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure
!unquoted procedure Rel_D($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="")
Rel_D($from, $to, $label, $techn, $descr, $sprite, $tags, $link, "")
!endprocedure
!unquoted procedure Rel_Down($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="", $index="")
!$pre = $getPrefix($index)
$getRel($down("-","->>"), $from, $to, $pre + $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure
!unquoted procedure Rel_Down($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="")
Rel_Down($from, $to, $label, $techn, $descr, $sprite, $tags, $link, "")
!endprocedure
!unquoted procedure RelIndex_D($e_index, $from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="")
$getRel($down("-","->>"), $from, $to, $e_index + ": " + $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure
!unquoted procedure RelIndex_Down($e_index, $from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="")
$getRel($down("-","->>"), $from, $to, $e_index + ": " + $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure

!unquoted procedure Rel_U($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="", $index="")
!$pre = $getPrefix($index)
$getRel($up("-","->>"), $from, $to, $pre + $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure
!unquoted procedure Rel_U($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="")
Rel_U($from, $to, $label, $techn, $descr, $sprite, $tags, $link=, "")
!endprocedure
!unquoted procedure Rel_Up($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="", $index="")
!$pre = $getPrefix($index)
$getRel($up("-","->>"), $from, $to, $pre + $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure
!unquoted procedure Rel_Up($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="")
Rel_Up($from, $to, $label, $techn, $descr, $sprite, $tags, $link, "")
!endprocedure
!unquoted procedure RelIndex_U($e_index, $from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="")
$getRel($up("-","->>"), $from, $to, $e_index + ": " + $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure
!unquoted procedure RelIndex_Up($e_index, $from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="")
$getRel($up("-","->>"), $from, $to, $e_index + ": " + $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure

!unquoted procedure Rel_L($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="", $index="")
!$pre = $getPrefix($index)
$getRel($left("-","->>"), $from, $to, $pre + $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure
!unquoted procedure Rel_L($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="")
Rel_L($from, $to, $label, $techn, $descr, $sprite, $tags, $link, "")
!endprocedure
!unquoted procedure Rel_Left($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="", $index="")
!$pre = $getPrefix($index)
$getRel($left("-","->>"), $from, $to, $pre + $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure
!unquoted procedure Rel_Left($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="")
Rel_Left($from, $to, $label, $techn, $descr, $sprite, $tags, $link, "")
!endprocedure
!unquoted procedure RelIndex_L($e_index, $from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="")
$getRel($left("-","->>"), $from, $to, $e_index + ": " + $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure
!unquoted procedure RelIndex_Left($e_index, $from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="")
$getRel($left("-","->>"), $from, $to, $e_index + ": " + $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure

!unquoted procedure Rel_R($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="", $index="")
!$pre = $getPrefix($index)
$getRel($right("-","->>"), $from, $to, $pre + $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure
!unquoted procedure Rel_R($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="")
Rel_R($from, $to, $label, $techn, $descr, $sprite, $tags, $link, "")
!endprocedure
!unquoted procedure Rel_Right($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="", $index="")
!$pre = $getPrefix($index)
$getRel($right("-","->>"), $from, $to, $pre + $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure
!unquoted procedure Rel_Right($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="")
Rel_Right($from, $to, $label, $techn, $descr, $sprite, $tags, $link, "")
!endprocedure
!unquoted procedure RelIndex_R($e_index, $from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="")
$getRel($right("-","->>"), $from, $to, $e_index + ": " + $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure
!unquoted procedure RelIndex_Right($e_index, $from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="")
$getRel($right("-","->>"), $from, $to, $e_index + ": " + $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure


================================================
FILE: C4_Sequence.puml
================================================
' convert it with additional command line argument -DRELATIVE_INCLUDE="relative/absolute" to use locally
!if %variable_exists("RELATIVE_INCLUDE")
  !include ./C4_Component.puml
!else
  !include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Component.puml
!endif

' Scope: Interactions in an enterprise, software system or container.
' Primary and supporting elements: Depends on the diagram scope -
'     enterprise - people and software systems Related to the enterprise in scope
'     software system - see system context or container diagrams,
'     container - see component diagram.
' Intended audience: Technical and non-technical people, inside and outside of the software development team.

' Sequence diagram introduces (automatically) numbered interactions:
'     (lowercase) increment($offset=1): increase current index (procedure which has no direct output)
'     (lowercase) setIndex($new_index): set the new index (procedure which has no direct output)
'
'     (Uppercase) LastIndex(): return the last used index (function which can be used as argument)
'     (Uppercase) Index($offset=1): returns current index and calculates next index (function which can be used as argument)
'     (Uppercase) SetIndex($new_index): returns new set index and calculates next index (function which can be used as argument)

' enables multi-level boxes
!pragma teoz true


' Legend redefinition
' ##################################
' sequence has no dashed boxes
!if ($LEGEND_DASHED_BOUNDARY == "dashed")
  !$LEGEND_DASHED_BOUNDARY = ""
!endif
!if ($LEGEND_DASHED_TRANSPARENT_BOUNDARY == "dashed")
  !$LEGEND_DASHED_TRANSPARENT_BOUNDARY = ""
!endif
UpdateBoundaryStyle("", $bgColor=$BOUNDARY_BG_COLOR, $fontColor=$BOUNDARY_COLOR, $borderColor=$BOUNDARY_COLOR, $shape=$DEFAULT_SHAPE)
UpdateBoundaryStyle("enterprise", $bgColor=$ENTERPRISE_BOUNDARY_BG_COLOR, $fontColor=$ENTERPRISE_BOUNDARY_COLOR, $borderColor=$ENTERPRISE_BOUNDARY_COLOR, $type="$ENTERPRISE_BOUNDARY_TYPE", $shape=$DEFAULT_SHAPE)
UpdateBoundaryStyle("system", $bgColor=$SYSTEM_BOUNDARY_BG_COLOR, $fontColor=$SYSTEM_BOUNDARY_COLOR, $borderColor=$SYSTEM_BOUNDARY_COLOR, $type="$SYSTEM_BOUNDARY_TYPE", $shape=$DEFAULT_SHAPE)
UpdateBoundaryStyle("container", $bgColor=$CONTAINER_BOUNDARY_BG_COLOR, $fontColor=$CONTAINER_BOUNDARY_COLOR, $borderColor=$CONTAINER_BOUNDARY_COLOR, $type="$CONTAINER_BOUNDARY_TYPE", $shape=$DEFAULT_SHAPE)

' Styling and Layout
' ##################################

!global $display_element_description = %false()

' typically the element/participant descriptions are not displayed in a sequence diagram, but it can be activated with this call
!unquoted procedure SHOW_ELEMENT_DESCRIPTIONS($show="true")
!if ($show == "true")
  !global $display_element_description = %true()
!else
  !global $display_element_description = %false()
!endif
!endprocedure

' typically the foot boxes descriptions are not displayed in a sequence diagram, but it can be activated with this call
!unquoted procedure SHOW_FOOT_BOXES($show="true")
!if ($show == "true")
  show footbox
!else
  hide footbox
!endif
!endprocedure

!global $show_index = %false()
' All relation specific (default) ordinary index numbers can be shown with this call
!unquoted procedure SHOW_INDEX($show="true")
!if ($show == "true")
  !global $show_index = %true()
!else
  !global $show_index = %false()
!endif
!endprocedure

' ======= if no theme is defined hide foot box and activate C4_blue styles
!if (%variable_exists("$THEME"))
!else
' $BOUNDARY_BG_COLOR... have to be defined in theme itself that it can be used in styles,...
' (no default values which are defined in C4.puml) 
' If skinparams and styles are defined with concrete values no variables are required 
!$BOUNDARY_BG_COLOR ?= "transparent"
!$BOUNDARY_COLOR ?= "#444444"
!$ARROW_COLOR ?= "#666666"

' replace transparent with concrete background that it can be used as font color too
!if ($BOUNDARY_BG_COLOR == "transparent")
  !$SEQUENCE_BG_COLOR = white
!else
  !$SEQUENCE_BG_COLOR = $BOUNDARY_BG_COLOR
!endif

' "C4 styled" default is no foot boxes
hide footbox
' "C4 styled" default is that lifeline is arrow color
skinparam SequenceLifelineBorderColor $ARROW_COLOR

skinparam SequenceGroupBodyBackgroundColor $SEQUENCE_BG_COLOR
skinparam SequenceGroupFontColor $BOUNDARY_COLOR
skinparam SequenceGroupBackgroundColor $BOUNDARY_COLOR
skinparam SequenceGroupHeaderFontColor $SEQUENCE_BG_COLOR
skinparam SequenceGroupBorderColor $BOUNDARY_COLOR

skinparam SequenceReferenceBackgroundColor $SEQUENCE_BG_COLOR
skinparam SequenceReferenceFontColor $BOUNDARY_COLOR
skinparam SequenceReferenceHeaderBackgroundColor $BOUNDARY_COLOR
' VIA STYLE
' skinparam SequenceReferenceHeaderFontColor $SEQUENCE_BG_COLOR
<style>
referenceHeader {
  fontcolor $SEQUENCE_BG_COLOR
}
</style>
skinparam SequenceReferenceBorderColor $BOUNDARY_COLOR

skinparam SequenceDividerBackgroundColor $SEQUENCE_BG_COLOR
skinparam SequenceDividerFontColor $BOUNDARY_COLOR
skinparam SequenceDividerBorderColor $BOUNDARY_COLOR

' VIA STYLE
' skinparam SequenceDelayFontColor green
<style>
sequenceDiagram {
  delay {
    FontColor $BOUNDARY_COLOR
  }
}
</style>
!endif
' ======= if no theme is defined hide foot box and activate C4_blue styles

!if (ROUNDED_STYLE == 1)
skinparam participantRoundCorner $ROUNDED_BOX_SIZE
!endif

' Elements redefinition
' ##################################

' all elements have to be displayed as participant
' participants requires ` %newline()` instead of `\n`

!unquoted function $breakWithNewline($text, $lineEnd, $lineStart, $widthStr="-1")
!$width = %intval($widthStr)
!$multiLine = ""
!if (%strpos($text, "\n") >= 0)
  !while (%strpos($text, "\n") >= 0)
    !$brPos = %strpos($text, "\n")
    !if ($brPos > 0)
      !$multiLine = $multiLine + %substr($text, 0, $brPos) + $lineEnd + %newline() + $lineStart
    !else
      ' <U+00A0> non breaking change that newLine breaks with formats can be used with \n\n 
      !$multiLine = $multiLine + "<U+00A0>" + $lineEnd + %newline() + $lineStart
    !endif
    !$text = %substr($text, $brPos+2)
    !if (%strlen($text) == 0)
      !$text = "<U+00A0>"
    !endif
  !endwhile
!else
  !while ($width>0 && %strlen($text) > $width)
    !$brPos = $width
    !while ($brPos > 0 && %substr($text, $brPos, 1) != ' ')
      !$brPos = $brPos - 1
    !endwhile

    !if ($brPos < 1)
      !$brPos = %strpos($text, " ")
    !else
    !endif

    !if ($brPos > 0)
      !$multiLine = $multiLine + %substr($text, 0, $brPos) + $lineEnd + %newline() + $lineStart
      !$text = %substr($text, $brPos + 1)
    !else
      !$multiLine = $multiLine+ $text
      !$text = ""
    !endif
  !endwhile
!endif
!if (%strlen($text) > 0)
  !$multiLine = $multiLine + $text
!endif
!return $multiLine
!endfunction

!unquoted function $breakNewLineLabel($text)
!$multiLine = $breakWithNewline($text, "", "==")
!return $multiLine
!endfunction

!unquoted function $breakNewLineDescr($text)
  !return $breakWithNewline($text, "", "", $REL_DESCR_MAX_CHAR_WIDTH)
!endfunction

!unquoted function $breakNewLineTechn($text)
  !$lineStart = "</size>//"
  !$lineEnd = '//<size:'+$TECHN_FONT_SIZE+'>'
  !return $breakWithNewline($text, $lineStart, $lineEnd, $REL_TECHN_MAX_CHAR_WIDTH)
!endfunction

' properties are not displayed in sequence diagram (size would be too big)
' $breakLabel() not required by participant
!procedure $getParticipant($elementType, $alias, $label, $techn, $descr, $sprite, $tags, $link)
  !$sprite=$toElementArg($sprite, $tags, "ElementTagSprite", $elementType)
  !$techn=$toElementArg($techn, $tags, "ElementTagTechn", $elementType)
  !$stereo = $toStereos($elementType,$tags)
  !$calcLabel = "== " + $breakNewLineLabel($label)
  !$calcTech = "//<size:" + $TECHN_FONT_SIZE + ">[" + $breakNewLineTechn($techn) + "]</size>//"
  !$calcDescr = $breakNewLineDescr($descr)
  !$calcLink = $getLink($link)
  !$props = $getProps()

participant $alias $stereo $calcLink [
!if ($sprite != "")
$getSprite($sprite)
!endif
!if ($label != "")
$calcLabel
!endif
!if ($techn != "")
$calcTech
!endif
!if ($display_element_description == %true() && $descr != "")

$calcDescr
!endif
]
!endprocedure


!unquoted procedure Person($alias, $label, $descr="", $sprite="", $tags="", $link="", $type="")
  ' $type reuses $techn definition of $tags
  $getParticipant("person", $alias, $label, $type, $descr, $sprite, $tags, $link)
!endprocedure

!unquoted procedure Person_Ext($alias, $label, $descr="", $sprite="", $tags="", $link="", $type="")
  ' $type reuses $techn definition of $tags
  $getParticipant("external_person", $alias, $label, $type, $descr, $sprite, $tags, $link)
!endprocedure

!unquoted procedure System($alias, $label, $descr="", $sprite="", $tags="", $link="", $type="", $baseShape="rectangle")
  ' $type reuses $techn definition of $tags
  $getParticipant("system", $alias, $label, $type, $descr, $sprite, $tags, $link)
!endprocedure

!unquoted procedure SystemDb($alias, $label, $descr="", $sprite="", $tags="", $link="", $type="")
  ' $type reuses $techn definition of $tags
  $getParticipant("system", $alias, $label, $type, $descr, $sprite, $tags, $link)
!endprocedure

!unquoted procedure SystemQueue($alias, $label, $descr="", $sprite="", $tags="", $link="", $type="")
  ' $type reuses $techn definition of $tags
  $getParticipant("system", $alias, $label, $type, $descr, $sprite, $tags, $link)
!endprocedure

!unquoted procedure System_Ext($alias, $label, $descr="", $sprite="", $tags="", $link="", $type="", $baseShape="rectangle")
  ' $type reuses $techn definition of $tags
  $getParticipant("external_system", $alias, $label, $type, $descr, $sprite, $tags, $link)
!endprocedure

!unquoted procedure SystemDb_Ext($alias, $label, $descr="", $sprite="", $tags="", $link="", $type="")
  ' $type reuses $techn definition of $tags
  $getParticipant("external_system", $alias, $label, $type, $descr, $sprite, $tags, $link)
!endprocedure

!unquoted procedure SystemQueue_Ext($alias, $label, $descr="", $sprite="", $tags="", $link="", $type="")
  ' $type reuses $techn definition of $tags
  $getParticipant("external_system", $alias, $label, $type, $descr, $sprite, $tags, $link)
!endprocedure



!unquoted procedure Container($alias, $label, $techn="", $descr="", $sprite="", $tags="", $link="", $baseShape="rectangle")
  $getParticipant("container", $alias, $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure

!unquoted procedure ContainerDb($alias, $label, $techn="", $descr="", $sprite="", $tags="", $link="")
  $getParticipant("container", $alias, $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure

!unquoted procedure ContainerQueue($alias, $label, $techn="", $descr="", $sprite="", $tags="", $link="")
  $getParticipant("container", $alias, $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure

!unquoted procedure Container_Ext($alias, $label, $techn="", $descr="", $sprite="", $tags="", $link="", $baseShape="rectangle")
  $getParticipant("external_container", $alias, $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure

!unquoted procedure ContainerDb_Ext($alias, $label, $techn="", $descr="", $sprite="", $tags="", $link="")
  $getParticipant("external_container", $alias, $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure

!unquoted procedure ContainerQueue_Ext($alias, $label, $techn="", $descr="", $sprite="", $tags="", $link="")
  $getParticipant("external_container", $alias, $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure



!unquoted procedure Component($alias, $label, $techn="", $descr="", $sprite="", $tags="", $link="", $baseShape="rectangle")
  $getParticipant("component", $alias, $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure

!unquoted procedure ComponentDb($alias, $label, $techn="", $descr="", $sprite="", $tags="", $link="")
  $getParticipant("component", $alias, $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure

!unquoted procedure ComponentQueue($alias, $label, $techn="", $descr="", $sprite="", $tags="", $link="")
  $getParticipant("component", $alias, $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure

!unquoted procedure Component_Ext($alias, $label, $techn="", $descr="", $sprite="", $tags="", $link="", $baseShape="rectangle")
  $getParticipant("external_component", $alias, $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure

!unquoted procedure ComponentDb_Ext($alias, $label, $techn="", $descr="", $sprite="", $tags="", $link="")
  $getParticipant("external_component", $alias, $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure

!unquoted procedure ComponentQueue_Ext($alias, $label, $techn="", $descr="", $sprite="", $tags="", $link="")
  $getParticipant("external_component", $alias, $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure


' Boundary redefinition
' ##################################

' all boundaries have to be displayed as box and 
' !!! important changes: without { at the end; and boundary ends with Boundary_End() instead of }

' alias ignored
' $breakLabel() not required by participant

!unquoted procedure Boundary($alias, $label, $type="", $tags="", $link="", $descr = "")
!$boundaryTags = $addBoundaryPostfix($tags)
' boundary $type reuses $techn definition of $boundaryTags
!$type=$toElementArg($type, $boundaryTags, "ElementTagTechn", "boundary")
!$sprite=$toElementArg("", $boundaryTags, "ElementTagSprite", "boundary")
!$labelSprite=""
!if ($sprite != "")
  ' add sprite in label line that it is more compact
  !$labelSprite = $getSprite($smallVersionSprite($sprite, $BOUNDARY_IMAGE_SIZE_FACTOR)) + ' '
!endif
!if ($link != "")
  !$usedNewLine = ']]\n== [[' + $link + ' '
  !$labelType = '== [[' + $link + ' ' + $labelSprite + $breakText($label, $usedNewLine) + ']]'
!else
  !$usedNewLine = '\n== '
  !$labelType = $labelSprite + $breakText($label, $usedNewLine)
!endif
!if (type != "")
    !$labelType = $labelType + '\n<size:' + $TECHN_FONT_SIZE + '>[' + $type + ']</size>'
!endif
!if ($display_element_description == %true() && $descr != "")
    !$labelType = $labelType + '\n\n' + $breakDescr($descr, $BOUNDARY_DESCR_MAX_CHAR_WIDTH)
!endif
box "$labelType" $toStereos("boundary", $boundaryTags)
!endprocedure

!procedure Boundary_End()
end box
!endprocedure

!unquoted procedure Enterprise_Boundary($alias, $label, $tags="", $link="", $descr = "")
  !if ($tags != "")
    !$allTags = $tags + '+enterprise'
  !else
    !$allTags = 'enterprise'
  !endif
  ' $type defined via $tag style
  Boundary($alias, $label, "", $allTags, $link, $descr)
!endprocedure

!unquoted procedure System_Boundary($alias, $label, $tags="", $link="", $descr = "")
  !if ($tags != "")
    !$allTags = $tags + '+system'
  !else
    !$allTags = 'system'
  !endif
  ' $type defined via $tag style
  Boundary($alias, $label, "", $allTags, $link, $descr)
!endprocedure

!unquoted procedure Container_Boundary($alias, $label, $tags="", $link="", $descr = "")
  !if ($tags != "")
    !$allTags = $tags + '+container'
  !else
    !$allTags = 'container'
  !endif
  ' $type defined via $tag style
  Boundary($alias, $label, "", $allTags, $link, $descr)
!endprocedure

' Relationship (redefinition)
' ##################################

' only Rel is supported in sequence diagram

' first Rel() supports the $index and $rel argument too; second Rel() overwrites C4.puml definition
' don't add empty lines in procedure otherwise & calls are not working anymore '& a -> b: call' are not working anymore
!unquoted procedure Rel($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="", $index="", $rel="")
  !if ($show_index == %true())
    !$pre = $getPrefix($index)
  !else
    !$pre = ""
  !endif
  !if ($rel == "")
    !$rel = "->"
  !endif
$getRel($rel, $from, $to, $pre + $label, $techn, $descr, $sprite, $tags, $link)
!endprocedure
!unquoted procedure Rel($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="")
Rel($from, $to, $label, $techn, $descr, $sprite, $tags, $link, "", "")
!endprocedure


================================================
FILE: LICENSE
================================================
MIT License

(c) 2018-2020 Ricardo Niepel, 2021-2026 Ricardo Niepel, kirchsth and contributors

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: LayoutOptions.md
================================================
# Layout Options

C4-PlantUML comes with some layout options.

- [📄 C4-PlantUML](README.md#c4-plantuml)
- [📄 Layout Options](#layout-options)
  - [Layout Guidance and Practices](#layout-guidance-and-practices)
    - [Overall Guidance](#overall-guidance)
    - [Layout Practices](#layout-practices)
  - [LAYOUT_TOP_DOWN() or LAYOUT_LEFT_RIGHT() or LAYOUT_LANDSCAPE()](#layout_top_down-or-layout_left_right-or-layout_landscape)
  - [LAYOUT_WITH_LEGEND() or SHOW_LEGEND(?hideStereotype, ?details)](#layout_with_legend-or-show_legendhidestereotype-details)
  - [SHOW_FLOATING_LEGEND(?alias, ?hideStereotype, ?details) and LEGEND()](#show_floating_legendalias-hidestereotype-details-and-legend)
  - [LAYOUT_AS_SKETCH() and SET_SKETCH_STYLE(?bgColor, ?fontColor, ?warningColor, ?fontName, ?footerWarning, ?footerText)](#layout_as_sketch-and-set_sketch_stylebgcolor-fontcolor-warningcolor-fontname-footerwarning-footertext)
  - [HIDE_STEREOTYPE()](#hide_stereotype)
  - [HIDE_PERSON_SPRITE(), SHOW_PERSON_SPRITE(?sprite), SHOW_PERSON_PORTRAIT() and SHOW_PERSON_OUTLINE()](#hide_person_sprite-show_person_spritesprite-show_person_portrait-and-show_person_outline)
    - [Using HIDE_PERSON_SPRITE()](#using-hide_person_sprite)
    - [Using SHOW_PERSON_SPRITE()](#using-show_person_sprite)
    - [Using SHOW_PERSON_SPRITE(sprite)](#using-show_person_spritesprite)
    - [Using SHOW_PERSON_PORTRAIT()](#using-show_person_portrait)
    - [Using SHOW_PERSON_OUTLINE()](#using-show_person_outline)
  - [(C4 styled) Sequence diagram specific layout options](#c4-styled-sequence-diagram-specific-layout-options)
    - [SHOW_ELEMENT_DESCRIPTIONS(?show)](#show_element_descriptionsshow)
    - [SHOW_FOOT_BOXES(?show)](#show_foot_boxesshow)
    - [SHOW_INDEX(?show)](#show_indexshow)
  - [Optional support of additional PlantUML elements](#optional-support-of-additional-plantuml-elements)
    - [List of supported PlantUML elements](#list-of-supported-plantuml-elements)
- [📄 Themes (different styles and languages)](Themes.md#themes)
- samples
  - [📄 C4 Model Diagrams](samples/C4CoreDiagrams.md#c4-model-diagrams)

## Layout Guidance and Practices

PlantUML uses [Graphviz](https://www.graphviz.org/) for its graph visualization. Thus the rendering itself is done automatically for you - that it one of the biggest advantages of using PlantUML.

...and also sometimes one of the biggest disadvantages, if the rendering is not what the user intended.

### Overall Guidance

1. Be minimal in the use of all directed relations - introduce the fewest possible directed `Rel_` and `Lay_` statements that achieve the desired layout. One way to do this is to immediately remove any of these you experiment with when they don't actually affect the layout at all. And of course you will remove the ones that affect it the layout in a negative way.
2. With dynamic rendering tools (e.g. VS Code plugin) do NOT trust the first rendering as it is shifty when adding code because you do not know exactly when it grabs the current unsaved code. Wait for a bit or close and reopen preview panel.

### Layout Practices

These are intended to correlate to the layout engine’s algorithm, but have (as of this writing) been determined by trial and error - not a code review.

Please read through all practices before starting.

1. Create all components, containers and boundaries first - in order top to bottom or left to right.
2. Use `Rel` (directionless) to create initial relationships.
3. If layout is not as desired, modify **some** Rel statements to contain direction `Rel_{direction}` to force shape layouts.
4. If the layout is not as desired, sparingly add `Lay_{direction}` to force any layouts that `Rel_{direction}` does not correct.
5. For both `Lay_{direction}` and `Rel_{direction}` statements used above:
   1. Exhaust attempts to get a working layout with `Rel_{direction}` before adding `Lay_{direction}`
   2. Try to introduce the fewest possible directed statements (of either type) that result in the desired layout.
   3. Immediately back out any directed statements that do not change the layout at all.
   4. Order inner objects first when it creates the desired result (enclosing objects tend to follow suit when child objects are ordered).
   5. When ordering multiple objects, only specify one relationship and, if possible in the same direction. For example if you want entity1 => entity2 => entity3, then `Rel_R(entity1,entity2)` and `Rel_R(entity2, entity3)` is the minimum possible statements and they all specify the same direction.
   6. Try NOT to apply directed statements to both inner elements and enclosing elements to force relationships that aren't working out.
   7. Make all orderings at the same nesting level whenever possible.
   8. Do NOT create duplicated, opposite direction statements in an attempt to force or ensure relationships as it does not affect the results. For instance if you have `Lay_R(entity1,entity2)` which is not working as desired and then also add the opposing one as `Lay_L(entity2,entity1)` - it does not help with forcing layouts to be as you want them. It might help to use `Lay_L` **instead of** `Lay_R`, but not both together.
6. Do not create an "All enclosing" boundary - the code for processing relationships seems to struggle with relationships inside this. Additionally, `SHOW_FLOATING_LEGEND()` will not display inside the All enclosing boundary.
7. Legend statements must come after at least one usage of each of the elements you want the legend to contain.

## LAYOUT_TOP_DOWN() or LAYOUT_LEFT_RIGHT() or LAYOUT_LANDSCAPE()

With the two macros `LAYOUT_TOP_DOWN()` and `LAYOUT_LEFT_RIGHT()` it is possible to easily change the flow visualization of the diagram. `LAYOUT_TOP_DOWN()` is the default.

```plantuml
@startuml LAYOUT_TOP_DOWN Sample
!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Container.puml

/' Not needed because this is the default '/
LAYOUT_TOP_DOWN()

Person(admin, "Administrator")
System_Boundary(c1, 'Sample') {
    Container(web_app, "Web Application", "C#, ASP.NET Core 2.1 MVC", "Allows users to compare multiple Twitter timelines")
}
System(twitter, "Twitter")

Rel(admin, web_app, "Uses", "HTTPS")
Rel(web_app, twitter, "Gets tweets from", "HTTPS")
@enduml
```

[![LAYOUT_TOP_DOWN Sample - open link](https://www.plantuml.com/plantuml/svg/JL1DZzCm4BtxLmpba5Jg9bh4YTE6WE2msqOaPSKfSk8fjUGFovuegX3_dR4eBRayPTx7FCzJ8XbfiKQyqMusYq8u4uNqeQwZNAkVcixBj2ICitU4ZghPspeOwRBd8P4oUghRzmzT7XrVdcih4s7aqTYoGsg7iGevNzG5x3s1GrIeOC9PSYxGMIVGYH51uKakXg2enNFput0Snk7GZPyEh_joAqI7CNbNIcMrsy6coQWJHKa-RhQYl_1YEtxqYrCoNihvSGT5BsqmM6pXbm3-RfNA5QTHXi0vhpX14uBHioJjtOP7xTiTD7rNxJyFR8a8xwf7UFdUPgunngy9yacuj0U-Hv0iiLnSyouOLas44KXRDBmhSRVVrtOb_IKvvofZKdn3y-zLzrh7n4x6hyFGzQpAeXjxZ_c2bFWNCvoYj_zRdj0fllmV "LAYOUT_TOP_DOWN Sample")](https://www.plantuml.com/plantuml/uml/JL1DZzCm4BtxLmpba5Jg9bh4YTE6WE2msqOaPSKfSk8fjUGFovuegX3_dR4eBRayPTx7FCzJ8XbfiKQyqMusYq8u4uNqeQwZNAkVcixBj2ICitU4ZghPspeOwRBd8P4oUghRzmzT7XrVdcih4s7aqTYoGsg7iGevNzG5x3s1GrIeOC9PSYxGMIVGYH51uKakXg2enNFput0Snk7GZPyEh_joAqI7CNbNIcMrsy6coQWJHKa-RhQYl_1YEtxqYrCoNihvSGT5BsqmM6pXbm3-RfNA5QTHXi0vhpX14uBHioJjtOP7xTiTD7rNxJyFR8a8xwf7UFdUPgunngy9yacuj0U-Hv0iiLnSyouOLas44KXRDBmhSRVVrtOb_IKvvofZKdn3y-zLzrh7n4x6hyFGzQpAeXjxZ_c2bFWNCvoYj_zRdj0fllmV)

`LAYOUT_LEFT_RIGHT()` rotates the flow visualization to _from Left to Right_ and directed relations like `Rel_Left()`, `Rel_Right()`, `Rel_Up()` and `Rel_Down()` are rotated too.

```plantuml
@startuml LAYOUT_LEFT_RIGHT Sample
!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Container.puml

LAYOUT_LEFT_RIGHT()

Person(admin, "Administrator")
System_Boundary(c1, 'Sample') {
    Container(web_app, "Web Application", "C#, ASP.NET Core 2.1 MVC", "Allows users to compare multiple Twitter timelines")
}
System(twitter, "Twitter")

Rel(admin, web_app, "Uses", "HTTPS")
Rel(web_app, twitter, "Gets tweets from", "HTTPS")
@enduml
```

[![LAYOUT_LEFT_RIGHT Sample - open link](https://www.plantuml.com/plantuml/svg/JKzDR-8m4BtdLtZP0q62HApsj2Uo4OLAq4OajEefSf9fiUGFonuhGbN_UySgXVeoyhoFUM_baJiCIYQ_XEvb682T4At_b-UEZzcRm5FeWWVN6usWCUkDogtaEceM7WSfkhpykwZYwhZVvOfx14UhjYG55nfSPgD_iYjU7ezDkrlVDUtXVhDjqbcILE2yqIaVbD1pDYdY51uTH-CciwG-avjg_vkW1-xEQR-SisdDbPKmdR7tXD6xtab7w5fkBVayGySAQwNeEGgZ9xGgVyzPKLVPmxeXYGFs9rko_LCPiK9ACteMUtg6Xb59oucYWaH1jrWp2gHQ38K2IRSyPVkuf4ln2oIV2ut0v03okzKjrj6JakQsQQgQM95qHMys6q1FNuZZrHdrKx82FGGbFm40 "LAYOUT_LEFT_RIGHT Sample")](https://www.plantuml.com/plantuml/uml/JKzDR-8m4BtdLtZP0q62HApsj2Uo4OLAq4OajEefSf9fiUGFonuhGbN_UySgXVeoyhoFUM_baJiCIYQ_XEvb682T4At_b-UEZzcRm5FeWWVN6usWCUkDogtaEceM7WSfkhpykwZYwhZVvOfx14UhjYG55nfSPgD_iYjU7ezDkrlVDUtXVhDjqbcILE2yqIaVbD1pDYdY51uTH-CciwG-avjg_vkW1-xEQR-SisdDbPKmdR7tXD6xtab7w5fkBVayGySAQwNeEGgZ9xGgVyzPKLVPmxeXYGFs9rko_LCPiK9ACteMUtg6Xb59oucYWaH1jrWp2gHQ38K2IRSyPVkuf4ln2oIV2ut0v03okzKjrj6JakQsQQgQM95qHMys6q1FNuZZrHdrKx82FGGbFm40)

`LAYOUT_LANDSCAPE()` rotates the default flow visualization to _from Left to Right_ like `LAYOUT_LEFT_RIGHT()` additional **directed relations** like Rel_Left(), Rel_Right(), Rel_Up() and Rel_Down() **are not rotated** anymore.

```plantuml
@startuml LAYOUT_LANDSCAPE Sample
!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Container.puml

LAYOUT_LANDSCAPE()

Person(admin, "Administrator")
System_Boundary(c1, 'Sample') {
    Container(web_app, "Web Application", "C#, ASP.NET Core 2.1 MVC", "Allows users to compare multiple Twitter timelines")
}
System(twitter, "Twitter")

Rel(admin, web_app, "Uses", "HTTPS")
Rel(web_app, twitter, "Gets tweets from", "HTTPS")

System(S,"S")
System(SU,"S Up")
System(SD,"S Down")
System(SL,"S Left")
System(SR,"S Right")

Rel_Up(S, SU, "Up")
Rel_Down(S, SD, "Down")
Rel_Left(S, SL, "Left")
Rel_Right(S, SR, "Right")

SHOW_LEGEND()

@enduml
```

[![LAYOUT_LANDSCAPE Sample - open link](https://www.plantuml.com/plantuml/svg/NOzFRvj04CNlV8gjUmYM75kfUkef5ApaG1nae55FQ0sJUANzizeTXAAgtxqpCNQiSa7lDxFllRcFA0EEHeio-_tSDbsPxOewpwgjgANn6f8lolPw740S4NtyiTa4EQtV51x7mnWXzCuYM5ptpcoybfQzRYCEMXqs-VVRYb7xL6wCZ0Y1K9VJ2waiXBMdtIJvFpXT9aa58JgRoi4eknABZFygOf3emcAPrEzaPhgVRhI33EzfVxSIDwU-Dqln9n7qNMBI2GwTz9vyNk0WCk-rwYKgPnU4ygyhaTNLUhTjw4a0yMrz9vv-vJpBj7PJ57nc5EW4tUWbhPXHew8iqKmA4O90PK1JLgHkV-TsAPw6v3ElqJ3PWpvVzLchZH0vxx5fgfgsUEao_RHv08maWN-lmPdh9-VGUhLWULOjIT7wAr8mATnahrZ9h8HNl69xPdlrTiIvTjTwSXTrouNPaHaRVT22A8kPiza7Bucpc3aRdWPx6bpiwyVdbwxSFcntHKho7kmm6lqF "LAYOUT_LANDSCAPE Sample")](https://www.plantuml.com/plantuml/uml/NOzFRvj04CNlV8gjUmYM75kfUkef5ApaG1nae55FQ0sJUANzizeTXAAgtxqpCNQiSa7lDxFllRcFA0EEHeio-_tSDbsPxOewpwgjgANn6f8lolPw740S4NtyiTa4EQtV51x7mnWXzCuYM5ptpcoybfQzRYCEMXqs-VVRYb7xL6wCZ0Y1K9VJ2waiXBMdtIJvFpXT9aa58JgRoi4eknABZFygOf3emcAPrEzaPhgVRhI33EzfVxSIDwU-Dqln9n7qNMBI2GwTz9vyNk0WCk-rwYKgPnU4ygyhaTNLUhTjw4a0yMrz9vv-vJpBj7PJ57nc5EW4tUWbhPXHew8iqKmA4O90PK1JLgHkV-TsAPw6v3ElqJ3PWpvVzLchZH0vxx5fgfgsUEao_RHv08maWN-lmPdh9-VGUhLWULOjIT7wAr8mATnahrZ9h8HNl69xPdlrTiIvTjTwSXTrouNPaHaRVT22A8kPiza7Bucpc3aRdWPx6bpiwyVdbwxSFcntHKho7kmm6lqF)

## LAYOUT_WITH_LEGEND() or SHOW_LEGEND(?hideStereotype, ?details)

Colors can help to add additional information or simply to make the diagram more aesthetically pleasing.
It can also help to save some space.

All of that is the reason, C4-PlantUML uses colors and prefer also to enable a layout without `<<stereotypes>>` and with a legend.
This can be enabled with `LAYOUT_WITH_LEGEND()`.

```plantuml
@startuml LAYOUT_WITH_LEGEND Sample
!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Container.puml

LAYOUT_WITH_LEGEND()

Person(admin, "Administrator")
System_Boundary(c1, 'Sample') {
    Container(web_app, "Web Application", "C#, ASP.NET Core 2.1 MVC", "Allows users to compare multiple Twitter timelines")
}
System(twitter, "Twitter")

Rel(admin, web_app, "Uses", "HTTPS")
Rel(web_app, twitter, "Gets tweets from", "HTTPS")
@enduml
```

[![LAYOUT_WITH_LEGEND Sample - open link](https://www.plantuml.com/plantuml/svg/JKzDJy904BttLwnue2JG1kF94xKIC05iR95uQhRjA9linsPtceOO_xkp4S6zJ7RUnxotyCWTzaf6DqAtik-07H6jVqXJnuVaK-2nRteFhZKQGMFI6fLQoNNGp3nsKZHfVZyhmcg_tQIAUmH7gped1HSQN6A3VxJ9tb_sLNruhjRrPhbQxfxYIHGLuBpHCU-Kq5Csoi8K7Xr7uqQJg3oHdQeVJQyxxavnEv-oSScLbJ2UiA-8qRiSYWTeQcuj-HoWOPcrKhGSXT4ZMkMtKvQLHR9RLYHnmEwIETk-vG7Bf3I3Pw6ePsWOjRISAAgNAEWMgmQ1r8kXK23fbgTit-TqCVw2v7Ec4HYzWlnhTOsrzwIakQwgeYHM85tGgyqAqDCNmlXmHlsNB41tlP8_ "LAYOUT_WITH_LEGEND Sample")](https://www.plantuml.com/plantuml/uml/JKzDJy904BttLwnue2JG1kF94xKIC05iR95uQhRjA9linsPtceOO_xkp4S6zJ7RUnxotyCWTzaf6DqAtik-07H6jVqXJnuVaK-2nRteFhZKQGMFI6fLQoNNGp3nsKZHfVZyhmcg_tQIAUmH7gped1HSQN6A3VxJ9tb_sLNruhjRrPhbQxfxYIHGLuBpHCU-Kq5Csoi8K7Xr7uqQJg3oHdQeVJQyxxavnEv-oSScLbJ2UiA-8qRiSYWTeQcuj-HoWOPcrKhGSXT4ZMkMtKvQLHR9RLYHnmEwIETk-vG7Bf3I3Pw6ePsWOjRISAAgNAEWMgmQ1r8kXK23fbgTit-TqCVw2v7Ec4HYzWlnhTOsrzwIakQwgeYHM85tGgyqAqDCNmlXmHlsNB41tlP8_)

Instead of a static legend (activated with `LAYOUT_WITH_LEGEND()`) a calculated legend can be activated with `SHOW_LEGEND(?hideStereotype, ?details)`.

The calculated legend has following differences:

- only relevant elements are listed
- custom tags/styles are supported
- stereotypes can remain visible (with `SHOW_LEGEND(false)`)
- details can be displayed in different sizes via the `$details` argument
  - `$details = Small()` .. default; details are displayed with a smaller size compared to the legend labels
  - `$details = Normal()` .. details and labels are displayed with same size
  - `$details = None()` .. only the labels are displayed
  - if `$legendText` contains `\n` then the text before is the label and the text behind the details
- **`SHOW_LEGEND()` has to be last call in the diagram**

```plantuml
@startuml SHOW_LEGEND Sample
!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Container.puml

Person(admin, "Administrator")
System_Boundary(c1, 'Sample') {
    Container(web_app, "Web Application", "C#, ASP.NET Core 2.1 MVC", "Allows users to compare multiple Twitter timelines")
}
System(twitter, "Twitter")

Rel(admin, web_app, "Uses", "HTTPS")
Rel(web_app, twitter, "Gets tweets from", "HTTPS")

SHOW_LEGEND()
@enduml
```

[![SHOW_LEGEND Sample - open link](https://www.plantuml.com/plantuml/svg/JL5Dgzf05DtFhxYr2oDeWgMhhfgceWkreObr6IR9RHsOZs7cXY3b_VTtWpurcqlEn-4Svdia6MWm6ghThtEptsmtnvzGIUCrYa_ATdhe4Iv4FdxBiY37z9-Yoz0E4KFdBA6bj7CcyrhQAMOLgTUgpOglgtA2JeTzPcGa30mr1JkaiXXIpreXIWpHsKJsHjabpFBfgaX1aWkpXQYkR3JD3pVONePhqgsNCBzrco_Wlm3-7f79Y6qZlUUSCxQGUwzL9qavEsEe-Bo4l2hJuwPcIq3uagxXyAUOk5nhDqQO9aKW1xp7IvQOGPFo6g4U5H4686LGAukHkxtTsoLq8pddBcDI_4RziUfPwnJPoNTNrsN5gadqO9ynMwJ8lpYTly6PLujuUQLa8Tu1 "SHOW_LEGEND Sample")](https://www.plantuml.com/plantuml/uml/JL5Dgzf05DtFhxYr2oDeWgMhhfgceWkreObr6IR9RHsOZs7cXY3b_VTtWpurcqlEn-4Svdia6MWm6ghThtEptsmtnvzGIUCrYa_ATdhe4Iv4FdxBiY37z9-Yoz0E4KFdBA6bj7CcyrhQAMOLgTUgpOglgtA2JeTzPcGa30mr1JkaiXXIpreXIWpHsKJsHjabpFBfgaX1aWkpXQYkR3JD3pVONePhqgsNCBzrco_Wlm3-7f79Y6qZlUUSCxQGUwzL9qavEsEe-Bo4l2hJuwPcIq3uagxXyAUOk5nhDqQO9aKW1xp7IvQOGPFo6g4U5H4686LGAukHkxtTsoLq8pddBcDI_4RziUfPwnJPoNTNrsN5gadqO9ynMwJ8lpYTly6PLujuUQLa8Tu1)

Legend labels and details can be defined via `\n` in `$legendTest` arguments too.

```plantuml
@startuml
' convert it with additional command line argument -DRELATIVE_INCLUDE="./.." to use locally
!if %variable_exists("RELATIVE_INCLUDE")
  !include %get_variable_value("RELATIVE_INCLUDE")/C4_Container.puml
!else
  !include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Container.puml
!endif
' $legendText with \n defines the label and details of the legend entry ("backend container" is label, "eight sided shape" is details)
AddElementTag("backendContainer", $fontColor=$ELEMENT_FONT_COLOR, $bgColor="#335DA5", $shape=EightSidedShape(), $legendText="backend container\neight sided shape")
' $legendText without \n defines only a label
AddRelTag("async", $textColor=$ARROW_FONT_COLOR, $lineColor=$ARROW_COLOR, $lineStyle=DashedLine(), $legendText="async call")
' if no $legendText defined, $tag is automatically the label and all additional displayed properties are the details
AddRelTag("sync/async", $textColor=$ARROW_FONT_COLOR, $lineColor=$ARROW_COLOR, $lineStyle=DottedLine())

System_Boundary(c1, "Internet Banking") {
    Container(mobile_app, "Mobile App", "C#, Xamarin", "Provides a limited subset of the Internet banking functionality to customers via their mobile device")
    Container(backend_api, "API Application", "Java, Docker Container", "Provides Internet banking functionality via API", $tags="backendContainer")
}
System_Ext(banking_system, "Mainframe Banking System", "Stores all of the core banking information about customers, accounts, transactions, etc.")

Rel(mobile_app, backend_api, "Uses", "async, JSON/HTTPS", $tags="async")
Rel_Neighbor(backend_api, banking_system, "Uses", "sync/async, XML/HTTPS", $tags="sync/async")

SHOW_LEGEND()
@enduml
```

[![SHOW_LEGEND Sample, $legendText defines legend details - open link](https://www.plantuml.com/plantuml/svg/hLDHRo8t47xdLqpsSYf815HTxwKIqd6mxUL480BIzk4aDBiUm3gllR9dIL3L_zxnRjWIagelvG7js9xFt_VDvq-1qNDLcCuFa3jx8C-W6Hurxm6LqgoTHIDRHO5MWT6M0FskAiWot4oNwMoyllqjtTpE9xE7QJfA1iF1805sK0K2ut8qvjYvqbjuVq2lCJEqeISTE7IJo-Qarm6uqZOtbI9uloFUj3q7D1MzrJAS_BIPECiepFoWZ4gko0GwXzepb-7duT3Zus0dogfCoFbSkaJ5GBGDIuCskd0JM1cT2UZDVLnwk9iD2mnC_irxhD9RCUxQq4w-r_JqxDmt2ugsSZ80xmaCPcGW-gT804m0jsqswZuG5lu8tIJ3_7kiyzCz2UZGTFSX8RtRCmIjI47OOqbnzuJOWyvOgTHG7CmQTorOopdfm_LMYeappe-kqrbwbyxNcryMyZTPp1PBsSzspMxoxiE7ZzFnnzXJNpLAu-MhUFSgrjrU_rprw3NrR_OrtzuRNhcAp-rorXm16ysrf2MPMWw6eyqZ7wQdauRnShdulIKVi_cl6oi-6XfDCUn9pQI-5D3WGunhJLC2QzqBiWr35HdWBZgF5Ri2MTSHlvYpB9q_7wL3QV182ahlIlAiIVeznQxJ1C_5HYx3_qUnOpufxdKwgsDWAZQVNMKL-cCt_r62TMkPl2M6psY_QxjBUl1d1-JNPgTRk4mRsc1Polcxke1nMIPIJTxruGyiq6iRotll3bf5UM1qeJaElye2yIdpxMrPSnjiAvitFcaM7ntaLH1doGSuQ8mzsaD301GTT2v1kg3td3xXfuN2-FusaZCwnuWRMNt50_PXwkIO_wVldEz_yOfK13XfvXz6hzwNaFhhv6_wnDrdc4sebw9jScphiQ2Jpz2SZYnMx7pqp9YJJRaij5IaqVcYLWEOnIVJUjG7p7CP9yiNUxG1Qz9I4US38TMHKBsOtqkp7W85OT3almzVLulvyCjwVRzgbJPRWYH0ctbyrPcxiFn2Qujw5cG9oTti4laivp6SNoJ2i_JNT3xjzZgV1BqgpDy0 "SHOW_LEGEND Sample, $legendText defines legend details")](https://www.plantuml.com/plantuml/uml/hLDHRo8t47xdLqpsSYf815HTxwKIqd6mxUL480BIzk4aDBiUm3gllR9dIL3L_zxnRjWIagelvG7js9xFt_VDvq-1qNDLcCuFa3jx8C-W6Hurxm6LqgoTHIDRHO5MWT6M0FskAiWot4oNwMoyllqjtTpE9xE7QJfA1iF1805sK0K2ut8qvjYvqbjuVq2lCJEqeISTE7IJo-Qarm6uqZOtbI9uloFUj3q7D1MzrJAS_BIPECiepFoWZ4gko0GwXzepb-7duT3Zus0dogfCoFbSkaJ5GBGDIuCskd0JM1cT2UZDVLnwk9iD2mnC_irxhD9RCUxQq4w-r_JqxDmt2ugsSZ80xmaCPcGW-gT804m0jsqswZuG5lu8tIJ3_7kiyzCz2UZGTFSX8RtRCmIjI47OOqbnzuJOWyvOgTHG7CmQTorOopdfm_LMYeappe-kqrbwbyxNcryMyZTPp1PBsSzspMxoxiE7ZzFnnzXJNpLAu-MhUFSgrjrU_rprw3NrR_OrtzuRNhcAp-rorXm16ysrf2MPMWw6eyqZ7wQdauRnShdulIKVi_cl6oi-6XfDCUn9pQI-5D3WGunhJLC2QzqBiWr35HdWBZgF5Ri2MTSHlvYpB9q_7wL3QV182ahlIlAiIVeznQxJ1C_5HYx3_qUnOpufxdKwgsDWAZQVNMKL-cCt_r62TMkPl2M6psY_QxjBUl1d1-JNPgTRk4mRsc1Polcxke1nMIPIJTxruGyiq6iRotll3bf5UM1qeJaElye2yIdpxMrPSnjiAvitFcaM7ntaLH1doGSuQ8mzsaD301GTT2v1kg3td3xXfuN2-FusaZCwnuWRMNt50_PXwkIO_wVldEz_yOfK13XfvXz6hzwNaFhhv6_wnDrdc4sebw9jScphiQ2Jpz2SZYnMx7pqp9YJJRaij5IaqVcYLWEOnIVJUjG7p7CP9yiNUxG1Qz9I4US38TMHKBsOtqkp7W85OT3almzVLulvyCjwVRzgbJPRWYH0ctbyrPcxiFn2Qujw5cG9oTti4laivp6SNoJ2i_JNT3xjzZgV1BqgpDy0)

Legend details can be deactivated via `SHOW_LEGEND($details=None())`

```plantuml
@startuml
' convert it with additional command line argument -DRELATIVE_INCLUDE="./.." to use locally
!if %variable_exists("RELATIVE_INCLUDE")
  !include %get_variable_value("RELATIVE_INCLUDE")/C4_Container.puml
!else
  !include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Container.puml
!endif
' $legendText with \n defines the label and details of the legend entry ("backend container" is label, "eight sided shape" is details)
AddElementTag("backendContainer", $fontColor=$ELEMENT_FONT_COLOR, $bgColor="#335DA5", $shape=EightSidedShape(), $legendText="backend container\neight sided shape")
' $legendText without \n defines only a label
AddRelTag("async", $textColor=$ARROW_FONT_COLOR, $lineColor=$ARROW_COLOR, $lineStyle=DashedLine(), $legendText="async call")
' if no $legendText defined, $tag is automatically the label and all additional displayed properties are the details
AddRelTag("sync/async", $textColor=$ARROW_FONT_COLOR, $lineColor=$ARROW_COLOR, $lineStyle=DottedLine())

System_Boundary(c1, "Internet Banking") {
    Container(mobile_app, "Mobile App", "C#, Xamarin", "Provides a limited subset of the Internet banking functionality to customers via their mobile device")
    Container(backend_api, "API Application", "Java, Docker Container", "Provides Internet banking functionality via API", $tags="backendContainer")
}
System_Ext(banking_system, "Mainframe Banking System", "Stores all of the core banking information about customers, accounts, transactions, etc.")

Rel(mobile_app, backend_api, "Uses", "async, JSON/HTTPS", $tags="async")
Rel_Neighbor(backend_api, banking_system, "Uses", "sync/async, XML/HTTPS", $tags="sync/async")

SHOW_LEGEND($details=None())
@enduml
```

[![SHOW_LEGEND Sample, hide details with $details=None() - open link](https://www.plantuml.com/plantuml/svg/hLDHZ-8s47xdLypczbIGM51rlPTAICT1sjiJ2ojWspwS54tY8LZds97ji8kg_lUE4nAbx5PzAGyeu_7CVDytdpyvZzPNXHhy09dH9x8Uf8TdwO-0GaWlZKR5gQ90BK19JO0shmhI7kwMstWrtz__5YVtwyNgQHdFeibqCed06wWSWJ8PAdKUtCW3l3-XbPWgIkX5Ek-6qNLnD1e0t4YTgKeGlC_99rtD2LL5RvLC5pyb2wCzCZ8xAGEJ6rAE-gsEtfVkv-dKulCaPsPLok1inbNCOiBSfgL27LIuSruecNBJkyTmzFImcXReFDatvsWX3opShQASl_VqSb7kcmP11xxfm1y95AQa8EWdY1ie1-PG9vewO1Js3CCenUnxYBDsJWJIDTLZY4ZcHmzE2XBWZbXIoBOTHp2O2n4h2elPOzurwp17OxWzSB0mojZPRRoA7-BrFlbbmt-BpMgpvNoQDzdetOSF7vVpZw6c7ZMBm_1TcBqByN0qxhEUlOR-JR_6Etf3ArFvlbn6gpDWmxccj2LLqq5trbd0u-cbvJ3VRZU_T-23D_yriVDdHRCbkYE95SVN19h-4EnQmsJ3QjE_SK4e0WBCW_9OULEWbxN5h_RCH_td8wHZXvr9G6bDITPBudfBeQhTO9zim3BzVnWRxrl6ey5WTtQUYkIpgRH0UnvcFxAnxhKdgyd3PzJVfSwZ4Vmv0FvrtXaM9fMA4YnBllzG1p0louYZnRin_845Mgb3-6ZDIOf03vGif0_Bhr97llLyDorjfi6XqbcZa_Iieu6iSgmiMGSdYQ56McW0WA2JpDX8L_0kxcDyaY7C7-y3E2Kp37q3ggzumZ4i3L-pqDNryVuNhW256qVD_jtirVjYK7-r-iOlVdXfaxZwACZ6rmuM2sfrXkPsGB7pnWRDb6fbolYWWyA5nXOr6y0qF9bEep5Wbl4wFNzvYzfX3PeZyjc4GGtOLFtzmJ_5UdBa647Z_p5ytMtMqo_x_UEkOzgak1CtIjRXLQVcIl9hhctNdf7P90-hgyvzdmTpVc4Bh-9Vu_LoU7jv5RErQQpxYOTLXVeR "SHOW_LEGEND Sample, hide details with $details=None()")](https://www.plantuml.com/plantuml/uml/hLDHZ-8s47xdLypczbIGM51rlPTAICT1sjiJ2ojWspwS54tY8LZds97ji8kg_lUE4nAbx5PzAGyeu_7CVDytdpyvZzPNXHhy09dH9x8Uf8TdwO-0GaWlZKR5gQ90BK19JO0shmhI7kwMstWrtz__5YVtwyNgQHdFeibqCed06wWSWJ8PAdKUtCW3l3-XbPWgIkX5Ek-6qNLnD1e0t4YTgKeGlC_99rtD2LL5RvLC5pyb2wCzCZ8xAGEJ6rAE-gsEtfVkv-dKulCaPsPLok1inbNCOiBSfgL27LIuSruecNBJkyTmzFImcXReFDatvsWX3opShQASl_VqSb7kcmP11xxfm1y95AQa8EWdY1ie1-PG9vewO1Js3CCenUnxYBDsJWJIDTLZY4ZcHmzE2XBWZbXIoBOTHp2O2n4h2elPOzurwp17OxWzSB0mojZPRRoA7-BrFlbbmt-BpMgpvNoQDzdetOSF7vVpZw6c7ZMBm_1TcBqByN0qxhEUlOR-JR_6Etf3ArFvlbn6gpDWmxccj2LLqq5trbd0u-cbvJ3VRZU_T-23D_yriVDdHRCbkYE95SVN19h-4EnQmsJ3QjE_SK4e0WBCW_9OULEWbxN5h_RCH_td8wHZXvr9G6bDITPBudfBeQhTO9zim3BzVnWRxrl6ey5WTtQUYkIpgRH0UnvcFxAnxhKdgyd3PzJVfSwZ4Vmv0FvrtXaM9fMA4YnBllzG1p0louYZnRin_845Mgb3-6ZDIOf03vGif0_Bhr97llLyDorjfi6XqbcZa_Iieu6iSgmiMGSdYQ56McW0WA2JpDX8L_0kxcDyaY7C7-y3E2Kp37q3ggzumZ4i3L-pqDNryVuNhW256qVD_jtirVjYK7-r-iOlVdXfaxZwACZ6rmuM2sfrXkPsGB7pnWRDb6fbolYWWyA5nXOr6y0qF9bEep5Wbl4wFNzvYzfX3PeZyjc4GGtOLFtzmJ_5UdBa647Z_p5ytMtMqo_x_UEkOzgak1CtIjRXLQVcIl9hhctNdf7P90-hgyvzdmTpVc4Bh-9Vu_LoU7jv5RErQQpxYOTLXVeR)

## SHOW_FLOATING_LEGEND(?alias, ?hideStereotype, ?details) and LEGEND()

`LAYOUT_WITH_LEGEND()` and SHOW_LEGEND(?hideStereotype)` adds the legend at the bottom right of the picture like below and additional whitespace is created.

```plantuml
@startuml Layout With Whitespace Sample
!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Container.puml

Person(a, "Person A")
Container(b, "Container B", "techn")
System(c, "System C")
Container(d, "Container D", "techn")
Container_Ext(e, "Ext. Container E", "techn")

Rel_R(a, b, "calls")
Rel_D(b, c, "uses")
Rel_D(c, d, "uses")
Rel_R(d, e, "updates")

SHOW_LEGEND()
@enduml
```

[![Layout With Whitespace Sample - open link](https://www.plantuml.com/plantuml/svg/LSwnReCm40RWtK_XCZbI0qkdJca1jGDjew2A4HdxL91iOzbdjNdx7eb4meJlk_y_SOWe0oPhU2FFSqBUJJZoRfmGefSAU2kjDy0U9gTCqi17H1-VYoB8t_o7icb84OAQ7OB3NCssy4QwvU8-eZRJK9HF--D2tnzDOML424HzIGqvEGYvfonZHmXnTa8-ykpwv2_PZgqfCT1YdVXhHYE26Xs5sZCTjK8HNP-yt5JrfbhTLrVkwpyKG1lwvloMhk_Jx0IcFot_E90gQKmaNR0I98emHRWPWTuObGbWCQybNfYrxrzTtzHlzMSbTkm0JYTh_W40 "Layout With Whitespace Sample")](https://www.plantuml.com/plantuml/uml/LSwnReCm40RWtK_XCZbI0qkdJca1jGDjew2A4HdxL91iOzbdjNdx7eb4meJlk_y_SOWe0oPhU2FFSqBUJJZoRfmGefSAU2kjDy0U9gTCqi17H1-VYoB8t_o7icb84OAQ7OB3NCssy4QwvU8-eZRJK9HF--D2tnzDOML424HzIGqvEGYvfonZHmXnTa8-ykpwv2_PZgqfCT1YdVXhHYE26Xs5sZCTjK8HNP-yt5JrfbhTLrVkwpyKG1lwvloMhk_Jx0IcFot_E90gQKmaNR0I98emHRWPWTuObGbWCQybNfYrxrzTtzHlzMSbTkm0JYTh_W40)

Therefore a floating legend can be added via SHOW_FLOATING_LEGEND(), positioned with Lay_Distance() and existing whitespace is reused like below.

- `SHOW_FLOATING_LEGEND(?alias, ?hideStereotype): shows the legend in the drawing area
- `LEGEND()`: is the default alias of the created floating legend and can be used in Lay_Distance() call

```plantuml
@startuml Compact Legend Layout Sample
!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Container.puml

Person(a, "Person A")
Container(b, "Container B", "techn")
System(c, "System C")
Container(d, "Container D", "techn")
Container_Ext(e, "Ext. Container E", "techn")

Rel_R(a, b, "calls")
Rel_D(b, c, "uses")
Rel_D(c, d, "uses")
Rel_R(d, e, "updates")

SHOW_FLOATING_LEGEND()
Lay_Distance(LEGEND(), e, 1)
@enduml
```

[![Compact Legend Layout Sample - open link](https://www.plantuml.com/plantuml/svg/RP5DQxD04CVl-obMUU1HQ9H2JuL2qcZJWar2qjBZigwJH5XNsHrfy-qxniH4w4LdVl-N4Pmwb1RRIZElD4gt1V03OkCUuzZAxsXVug7DMmTMrGR1OAJgAcwqD5rcuh3GPHxF7oRh3ds-Pt4b7O8b9EQKaAK16pLTFsDhiAusWRpZVZ_ocH-omHRa90mRV_PfHL07GwRjaLghG17gHpuVHOjHDBcD3bYaFnW0AVIE-DMTthhP1hJOTD-O4rLIQqUqWqb0H15i7LmPWMB4A4m0EjOK4a-OURlMA2nhmxDqaIwJ86IU1MojuVvErn9BinSQzc0AdtdqlFZPzdNr9LworUpZRRaGv_Ib96IPF8gaT2YDWW6Vpj_JMVQVU_zwof-utnyGqeBqIVgp-cmFIHzrd_cbUaf5z5D_0G00 "Compact Legend Layout Sample")](https://www.plantuml.com/plantuml/uml/RP5DQxD04CVl-obMUU1HQ9H2JuL2qcZJWar2qjBZigwJH5XNsHrfy-qxniH4w4LdVl-N4Pmwb1RRIZElD4gt1V03OkCUuzZAxsXVug7DMmTMrGR1OAJgAcwqD5rcuh3GPHxF7oRh3ds-Pt4b7O8b9EQKaAK16pLTFsDhiAusWRpZVZ_ocH-omHRa90mRV_PfHL07GwRjaLghG17gHpuVHOjHDBcD3bYaFnW0AVIE-DMTthhP1hJOTD-O4rLIQqUqWqb0H15i7LmPWMB4A4m0EjOK4a-OURlMA2nhmxDqaIwJ86IU1MojuVvErn9BinSQzc0AdtdqlFZPzdNr9LworUpZRRaGv_Ib96IPF8gaT2YDWW6Vpj_JMVQVU_zwof-utnyGqeBqIVgp-cmFIHzrd_cbUaf5z5D_0G00)

## LAYOUT_AS_SKETCH() and SET_SKETCH_STYLE(?bgColor, ?fontColor, ?warningColor, ?fontName, ?footerWarning, ?footerText)

C4-PlantUML can be especially helpful during up-front design sessions.
One thing which is often ignored is the fact, that these software architecture sketches are just sketches.

Without any proof

- if they are technically possible
- if they can fulfill all requirements
- if they keep what they promise

More often these sketches are used by many people as facts and are manifested into their documentations.
With `LAYOUT_AS_SKETCH()` you can make a difference.

```plantuml
@startuml LAYOUT_AS_SKETCH Sample
!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Container.puml

LAYOUT_AS_SKETCH()

Person(admin, "Administrator")
System_Boundary(c1, 'Sample') {
    Container(web_app, "Web Application", "C#, ASP.NET Core 2.1 MVC", "Allows users to compare multiple Twitter timelines")
}
System(twitter, "Twitter")

Rel(admin, web_app, "Uses", "HTTPS")
Rel(web_app, twitter, "Gets tweets from", "HTTPS")
@enduml
```

[![LAYOUT_AS_SKETCH Sample - open link](https://www.plantuml.com/plantuml/svg/NL1DI_D04BxlhvYtxw4fj0aLJvuQGx5Wgy6aYgUmILQxi1_BxeHGnF_kBDLMp6N8pFFnFBiAo3qEMi4sVttSrqrUDTNzkYusK77jb63_fEdKq0iu8BfmasMUZ-cxnCFG3a7upXeK1jFEwimfRgBM8c2lP9iLruiohlQxRQPvGE5frHJ4uD88dph2ClRNE9anLWeVh4buhwMPmoIFKmRq7AsVp5Xr937TtDh1zDmVasuvX-afxtG67mpeEziaesWRxXpfl8WMSkUKx3XAQoQqAlxF8Q_Az65T4yKBk4gNi7ikuYrNoeu1Oiq0Q84wEauGFIYKv0NrA95Q0Kej57a5olRvdIx1qv5qJh0Od3q9zTFg4ciVY4bpKzTbHQW8EbylCdS20_sAEDwyrRyfAs7w-9fV "LAYOUT_AS_SKETCH Sample")](https://www.plantuml.com/plantuml/uml/NL1DI_D04BxlhvYtxw4fj0aLJvuQGx5Wgy6aYgUmILQxi1_BxeHGnF_kBDLMp6N8pFFnFBiAo3qEMi4sVttSrqrUDTNzkYusK77jb63_fEdKq0iu8BfmasMUZ-cxnCFG3a7upXeK1jFEwimfRgBM8c2lP9iLruiohlQxRQPvGE5frHJ4uD88dph2ClRNE9anLWeVh4buhwMPmoIFKmRq7AsVp5Xr937TtDh1zDmVasuvX-afxtG67mpeEziaesWRxXpfl8WMSkUKx3XAQoQqAlxF8Q_Az65T4yKBk4gNi7ikuYrNoeu1Oiq0Q84wEauGFIYKv0NrA95Q0Kej57a5olRvdIx1qv5qJh0Od3q9zTFg4ciVY4bpKzTbHQW8EbylCdS20_sAEDwyrRyfAs7w-9fV)

Additional styles and the footer text can be changed with SET_SKETCH_STYLE():

- `SET_SKETCH_STYLE(?bgColor, ?fontColor, ?warningColor, ?fontName, ?footerWarning, ?footerText)`:
  Enables the modification of different sketch styles and footer.

The possible font name(s) depend on the output format (e.g. PNG uses fonts which are installed on the server and SVG fonts have to be installed on the client).
Additional is it possible to define comma separated fall back fonts (if the diagrams are exported as SVG. Atm
PNG does not support fallback fonts based on a PlantUML [bug](https://forum.plantuml.net/14842/specify-fall-back-fonts-is-not-working), but this could be fixed in one of the following versions)

```plantuml
@startuml LAYOUT_AS_SKETCH Sample
!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Container.puml

SET_SKETCH_STYLE($bgColor="lightblue", $fontColor="darkblue", $warningColor="darkred", $footerWarning="Sketch", $footerText="Created for discussion")

' PNG with font jlm_cmmi10 (typically another font is used)
' SET_SKETCH_STYLE($fontName="jlm_cmmi10")

' SVG with fallback fonts MS Gothic,Comic Sans MS, Comic Sans, Chalkboard SE, Comic Neue, cursive, sans-serif (typically without "MS Gothic")
SET_SKETCH_STYLE($fontName="MS Gothic,Comic Sans MS,Comic Sans,Chalkboard SE,Comic Neue,cursive,sans-serif")

LAYOUT_AS_SKETCH()

Person(admin, "Administrator")
System_Boundary(c1, 'Sample') {
    Container(w
Download .txt
gitextract_88wt1q1u/

├── .gitattributes
├── .github/
│   ├── stale.yml
│   └── workflows/
│       └── run-percy-tests.yml
├── .gitignore
├── .scripts/
│   ├── HowToCreateANewRelease.md
│   ├── plantuml-stdlib_C4_README.txt
│   ├── plantuml-stdlib_README_section.txt
│   ├── readme_release_header.txt
│   └── transform_files.py
├── C4.puml
├── C4_Component.puml
├── C4_Container.puml
├── C4_Context.puml
├── C4_Deployment.puml
├── C4_Dynamic.puml
├── C4_Sequence.puml
├── LICENSE
├── LayoutOptions.md
├── README.md
├── Themes.md
├── percy/
│   ├── C4_Component Diagram Sample - bigbankplc.puml
│   ├── C4_Container Diagram Sample - bigbankplc-icons.puml
│   ├── C4_Container Diagram Sample - bigbankplc-newStyle.puml
│   ├── C4_Container Diagram Sample - bigbankplc-styles.puml
│   ├── C4_Container Diagram Sample - bigbankplc.puml
│   ├── C4_Container Diagram Sample - message bus.puml
│   ├── C4_Container Diagram Sample - techtribesjs.puml
│   ├── C4_Context Diagram Sample - bigbankplc-landscape.puml
│   ├── C4_Context Diagram Sample - bigbankplc.puml
│   ├── C4_Context Diagram Sample - enterprise.puml
│   ├── C4_Deployment Diagram Sample - bigbankplc-details.puml
│   ├── C4_Deployment Diagram Sample - bigbankplc.puml
│   ├── C4_Dynamic Diagram Sample - bigbankplc.puml
│   ├── C4_Sequence Diagram Sample - bigbankplc.puml
│   ├── TestAllAddTags.puml
│   ├── TestAllElementsWithLegend.puml
│   ├── TestAllPlantUmlElements.puml
│   ├── TestBoundaryTagSupport.puml
│   ├── TestEmptyLabel.puml
│   ├── TestFloatingLegend.puml
│   ├── TestLabelsWithLineBreak.puml
│   ├── TestLayDirections.puml
│   ├── TestLayoutLandscape.puml
│   ├── TestLayoutLandscapeDynamic.puml
│   ├── TestLayoutLandscapeWithLay.puml
│   ├── TestLayoutLandscapeWithLayNoRotate.puml
│   ├── TestLegend.puml
│   ├── TestLegendDetailsNone.puml
│   ├── TestLegendDetailsNormal.puml
│   ├── TestLegendDetailsSmall.puml
│   ├── TestLegendHidden.puml
│   ├── TestLegendSprite.puml
│   ├── TestLegendTitle.puml
│   ├── TestNewStyle.puml
│   ├── TestPersonOutline.puml
│   ├── TestPersonPortrait.puml
│   ├── TestPredefinedSprites.puml
│   ├── TestProperty.puml
│   ├── TestPropertyMissingColumns.puml
│   ├── TestRelations.puml
│   ├── TestRelationsDynamic.puml
│   ├── TestRelationsTags.puml
│   ├── TestSequenceSkinparams.puml
│   ├── TestSketchStyle.puml
│   ├── TestSystemPersonSupportType.puml
│   ├── TestTagSupportsSprite.puml
│   ├── TestTagSupportsSpriteTechn.puml
│   ├── TestThemeC4_All.puml
│   ├── TestThemeC4_FirstTest.puml
│   ├── TestThemeC4_united.puml
│   ├── TestThemeSupport.puml
│   ├── TestThemeTemplate.puml
│   └── TestVersion.puml
├── samples/
│   ├── C4CoreDiagrams.md
│   ├── C4_Component Diagram Sample - bigbankplc.puml
│   ├── C4_Container Diagram Sample - bigbankplc-icons.puml
│   ├── C4_Container Diagram Sample - bigbankplc-styles.puml
│   ├── C4_Container Diagram Sample - bigbankplc-themes.puml
│   ├── C4_Container Diagram Sample - bigbankplc.puml
│   ├── C4_Container Diagram Sample - message bus.puml
│   ├── C4_Container Diagram Sample - techtribesjs.puml
│   ├── C4_Context Diagram Sample - bigbankplc-landscape.puml
│   ├── C4_Context Diagram Sample - bigbankplc.puml
│   ├── C4_Context Diagram Sample - enterprise.puml
│   ├── C4_Deployment Diagram Sample - bigbankplc-details.puml
│   ├── C4_Deployment Diagram Sample - bigbankplc.puml
│   ├── C4_Dynamic Diagram Sample - bigbankplc.puml
│   ├── C4_Dynamic Diagram Sample - message bus - old format.puml
│   ├── C4_Dynamic Diagram Sample - message bus.puml
│   ├── C4_Sequence Diagram Sample - bigbankplc.puml
│   └── C4_Sequence Diagram Sample - complex.puml
└── themes/
    ├── puml-theme-C4Language_chinese.puml
    ├── puml-theme-C4Language_danish.puml
    ├── puml-theme-C4Language_dutch.puml
    ├── puml-theme-C4Language_english.puml
    ├── puml-theme-C4Language_french.puml
    ├── puml-theme-C4Language_german.puml
    ├── puml-theme-C4Language_italian.puml
    ├── puml-theme-C4Language_japanese.puml
    ├── puml-theme-C4Language_korean.puml
    ├── puml-theme-C4Language_portuguese.puml
    ├── puml-theme-C4Language_russian.puml
    ├── puml-theme-C4Language_spanish.puml
    ├── puml-theme-C4Language_ukrainian.puml
    ├── puml-theme-C4_FirstTest.puml
    ├── puml-theme-C4_blue.puml
    ├── puml-theme-C4_blue_new.puml
    ├── puml-theme-C4_brown.puml
    ├── puml-theme-C4_brown_new.puml
    ├── puml-theme-C4_green.puml
    ├── puml-theme-C4_green_new.puml
    ├── puml-theme-C4_sandstone.puml
    ├── puml-theme-C4_superhero.puml
    ├── puml-theme-C4_united.puml
    ├── puml-theme-C4_violet.puml
    └── puml-theme-C4_violet_new.puml
Download .txt
SYMBOL INDEX (18 symbols across 1 files)

FILE: .scripts/transform_files.py
  function plantuml_encode (line 60) | def plantuml_encode(plantuml_text):
  function plantuml_decode (line 69) | def plantuml_decode(plantuml_url):
  function read_environment_variable (line 80) | def read_environment_variable(env_var, is_required=True):
  function read_environment_variables (line 96) | def read_environment_variables():
  function replace_first_regex_in_file (line 127) | def replace_first_regex_in_file(file_path, search, replace):
  function replace_in_file (line 136) | def replace_in_file(file_path, orig, replace):
  function replace_first_regex_copy_file (line 144) | def replace_first_regex_copy_file(
  function replace_regex_copy_file (line 154) | def replace_regex_copy_file(
  function calculate_next_version (line 166) | def calculate_next_version(release, v1, v2, v3):
  function read_next_plantuml_version (line 183) | def read_next_plantuml_version():
  function update_c4_release_version (line 218) | def update_c4_release_version():
  function update_c4_next_beta_version (line 229) | def update_c4_next_beta_version():
  function update_all_includes (line 240) | def update_all_includes():
  function process_url_match (line 265) | def process_url_match(m: re.Match[str]):
  function replace_images_in_file (line 298) | def replace_images_in_file(file_path):
  function update_all_images (line 310) | def update_all_images():
  function replace_readme_header (line 334) | def replace_readme_header():
  function create_plantuml_stdlib_c4_folder (line 356) | def create_plantuml_stdlib_c4_folder(target_path):
Condensed preview — 116 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (636K chars).
[
  {
    "path": ".gitattributes",
    "chars": 275,
    "preview": "# define GitHub repository languages\n*.puml linguist-language=PlantUML\n*.iuml linguist-language=PlantUML\n*.plantuml ling"
  },
  {
    "path": ".github/stale.yml",
    "chars": 724,
    "preview": "# Number of days of inactivity before an issue becomes stale\ndaysUntilStale: 60\n# Number of days of inactivity before a "
  },
  {
    "path": ".github/workflows/run-percy-tests.yml",
    "chars": 628,
    "preview": "name: Percy Tests\non:\n  push:\n    paths:\n    - '*.puml'\n    - 'percy/**'\n    - '.github/workflows/run-percy-tests.yml'\n "
  },
  {
    "path": ".gitignore",
    "chars": 265,
    "preview": "################################################################################\n# This .gitignore file was automatical"
  },
  {
    "path": ".scripts/HowToCreateANewRelease.md",
    "chars": 6842,
    "preview": "# How to create a new release (WIP)\n\nThe idea is to\n- create a new release in the \"release/$release_version\" branch (tag"
  },
  {
    "path": ".scripts/plantuml-stdlib_C4_README.txt",
    "chars": 4907,
    "preview": "---\nname: C4\ndisplay_name: C4 (C4-PlantUML)\ndescription: The C4 library enables a simple way of describing and communica"
  },
  {
    "path": ".scripts/plantuml-stdlib_README_section.txt",
    "chars": 883,
    "preview": "<!-- start C4 section -->\n![name: C4](https://img.shields.io/badge/name-C4-black)\n![display_name: C4 (C4-PlantUML)](http"
  },
  {
    "path": ".scripts/readme_release_header.txt",
    "chars": 1073,
    "preview": "[![release][Release Badge]][Release Page]\n[![license MIT][License Badge]][License Page]\n &nbsp; &nbsp; &nbsp;\n[![integra"
  },
  {
    "path": ".scripts/transform_files.py",
    "chars": 17683,
    "preview": "#!/usr/bin/python\n\n# If a new version is released, version number and other topics of the source\n# has to be updated.\n# "
  },
  {
    "path": "C4.puml",
    "chars": 67427,
    "preview": "' C4-PlantUML\n\n' Global pre-settings\n' ##################################\n' NEW_C4_STYLE\n'   If NEW_C4_STYLE is set BEFO"
  },
  {
    "path": "C4_Component.puml",
    "chars": 5281,
    "preview": "' convert it with additional command line argument -DRELATIVE_INCLUDE=\"relative/absolute\" to use locally\n!if %variable_e"
  },
  {
    "path": "C4_Container.puml",
    "chars": 5743,
    "preview": "' convert it with additional command line argument -DRELATIVE_INCLUDE=\"relative/absolute\" to use locally\n!if %variable_e"
  },
  {
    "path": "C4_Context.puml",
    "chars": 21661,
    "preview": "' convert it with additional command line argument -DRELATIVE_INCLUDE=\"relative/absolute\" to use locally\n!if %variable_e"
  },
  {
    "path": "C4_Deployment.puml",
    "chars": 7149,
    "preview": "' convert it with additional command line argument -DRELATIVE_INCLUDE=\"relative/absolute\" to use locally\n!if %variable_e"
  },
  {
    "path": "C4_Dynamic.puml",
    "chars": 9947,
    "preview": "' convert it with additional command line argument -DRELATIVE_INCLUDE=\"relative/absolute\" to use locally\n!if %variable_e"
  },
  {
    "path": "C4_Sequence.puml",
    "chars": 15976,
    "preview": "' convert it with additional command line argument -DRELATIVE_INCLUDE=\"relative/absolute\" to use locally\n!if %variable_"
  },
  {
    "path": "LICENSE",
    "chars": 1119,
    "preview": "MIT License\n\n(c) 2018-2020 Ricardo Niepel, 2021-2026 Ricardo Niepel, kirchsth and contributors\n\nPermission is hereby gra"
  },
  {
    "path": "LayoutOptions.md",
    "chars": 58772,
    "preview": "# Layout Options\n\nC4-PlantUML comes with some layout options.\n\n- [📄 C4-PlantUML](README.md#c4-plantuml)\n- [📄 Layout Opti"
  },
  {
    "path": "README.md",
    "chars": 85754,
    "preview": "[![release][Release Badge]][Release Page]\n[![license MIT][License Badge]][License Page]\n &nbsp; &nbsp; &nbsp;\n[![commits"
  },
  {
    "path": "Themes.md",
    "chars": 62839,
    "preview": "# Themes - different styles and languages\n\n[![Rounded (corner) style sample - open link](https://www.plantuml.com/plantu"
  },
  {
    "path": "percy/C4_Component Diagram Sample - bigbankplc.puml",
    "chars": 1904,
    "preview": "@startuml\n' convert it with additional command line argument -DRELATIVE_INCLUDE=\"./..\" to use locally\n!if %variable_exis"
  },
  {
    "path": "percy/C4_Container Diagram Sample - bigbankplc-icons.puml",
    "chars": 2611,
    "preview": "@startuml\n' convert it with additional command line argument -DRELATIVE_INCLUDE=\"./..\" to use locally\n!if %variable_exis"
  },
  {
    "path": "percy/C4_Container Diagram Sample - bigbankplc-newStyle.puml",
    "chars": 2625,
    "preview": "@startuml\n\n!NEW_C4_STYLE = 1\n\n' convert it with additional command line argument -DRELATIVE_INCLUDE=\"./..\" to use locall"
  },
  {
    "path": "percy/C4_Container Diagram Sample - bigbankplc-styles.puml",
    "chars": 2722,
    "preview": "@startuml\n' convert it with additional command line argument -DRELATIVE_INCLUDE=\"./..\" to use locally\n!if %variable_exis"
  },
  {
    "path": "percy/C4_Container Diagram Sample - bigbankplc.puml",
    "chars": 2041,
    "preview": "@startuml\n' convert it with additional command line argument -DRELATIVE_INCLUDE=\"./..\" to use locally\n!if %variable_exis"
  },
  {
    "path": "percy/C4_Container Diagram Sample - message bus.puml",
    "chars": 2054,
    "preview": "@startuml \"messagebus\"\n' convert it with additional command line argument -DRELATIVE_INCLUDE=\"./..\" to use locally\n!if %"
  },
  {
    "path": "percy/C4_Container Diagram Sample - techtribesjs.puml",
    "chars": 2128,
    "preview": "@startuml\n' convert it with additional command line argument -DRELATIVE_INCLUDE=\"./..\" to use locally\n!if %variable_exis"
  },
  {
    "path": "percy/C4_Context Diagram Sample - bigbankplc-landscape.puml",
    "chars": 1741,
    "preview": "@startuml\n' convert it with additional command line argument -DRELATIVE_INCLUDE=\"./..\" to use locally\n!if %variable_exis"
  },
  {
    "path": "percy/C4_Context Diagram Sample - bigbankplc.puml",
    "chars": 1077,
    "preview": "@startuml\n' convert it with additional command line argument -DRELATIVE_INCLUDE=\"./..\" to use locally\n!if %variable_exis"
  },
  {
    "path": "percy/C4_Context Diagram Sample - enterprise.puml",
    "chars": 1569,
    "preview": "@startuml \"enterprise\"\n' convert it with additional command line argument -DRELATIVE_INCLUDE=\"./..\" to use locally\n!if %"
  },
  {
    "path": "percy/C4_Deployment Diagram Sample - bigbankplc-details.puml",
    "chars": 3729,
    "preview": "@startuml\n' convert it with additional command line argument -DRELATIVE_INCLUDE=\"./..\" to use locally\n!if %variable_exis"
  },
  {
    "path": "percy/C4_Deployment Diagram Sample - bigbankplc.puml",
    "chars": 2869,
    "preview": "@startuml\n' convert it with additional command line argument -DRELATIVE_INCLUDE=\"./..\" to use locally\n!if %variable_exis"
  },
  {
    "path": "percy/C4_Dynamic Diagram Sample - bigbankplc.puml",
    "chars": 1118,
    "preview": "@startuml\n' convert it with additional command line argument -DRELATIVE_INCLUDE=\"./..\" to use locally\n!if %variable_exis"
  },
  {
    "path": "percy/C4_Sequence Diagram Sample - bigbankplc.puml",
    "chars": 1121,
    "preview": "@startuml\n' convert it with additional command line argument -DRELATIVE_INCLUDE=\"./..\" to use locally\n!if %variable_exis"
  },
  {
    "path": "percy/TestAllAddTags.puml",
    "chars": 3752,
    "preview": "@startuml \n\n' displays all elements, relationship and legend (can be included in the theme test)\n!if %variable_exists(\"R"
  },
  {
    "path": "percy/TestAllElementsWithLegend.puml",
    "chars": 3185,
    "preview": "@startuml \n\n' displays all elements, relationship and legend (can be included in the theme test)\n!if %variable_exists(\"R"
  },
  {
    "path": "percy/TestAllPlantUmlElements.puml",
    "chars": 9824,
    "preview": "  @startuml\n\n' If ENABLE_ALL_PLANT_ELEMENTS is set BEFORE the first C4_* file is loaded,\n' nearly \"all\" PlantUML element"
  },
  {
    "path": "percy/TestBoundaryTagSupport.puml",
    "chars": 2695,
    "preview": "@startuml\n' convert it with additional command line argument -DRELATIVE_INCLUDE=\"./..\" to use locally\n!if %variable_exis"
  },
  {
    "path": "percy/TestEmptyLabel.puml",
    "chars": 3056,
    "preview": "@startuml\n' convert it with additional command line argument -DRELATIVE_INCLUDE=\"./..\" to use locally\n!if %variable_exis"
  },
  {
    "path": "percy/TestFloatingLegend.puml",
    "chars": 634,
    "preview": "@startuml\n' convert it with additional command line argument -DRELATIVE_INCLUDE=\"./..\" to use locally\n!if %variable_exis"
  },
  {
    "path": "percy/TestLabelsWithLineBreak.puml",
    "chars": 2090,
    "preview": "@startuml\n' convert it with additional command line argument -DRELATIVE_INCLUDE=\"./..\" to use locally\n!if %variable_exis"
  },
  {
    "path": "percy/TestLayDirections.puml",
    "chars": 635,
    "preview": "@startuml\n' convert it with additional command line argument -DRELATIVE_INCLUDE=\"./..\" to use locally\n!if %variable_exis"
  },
  {
    "path": "percy/TestLayoutLandscape.puml",
    "chars": 861,
    "preview": "@startuml\n' convert it with additional command line argument -DRELATIVE_INCLUDE=\"./..\" to use locally\n!if %variable_exis"
  },
  {
    "path": "percy/TestLayoutLandscapeDynamic.puml",
    "chars": 857,
    "preview": "@startuml\n' convert it with additional command line argument -DRELATIVE_INCLUDE=\"./..\" to use locally\n!if %variable_exis"
  },
  {
    "path": "percy/TestLayoutLandscapeWithLay.puml",
    "chars": 1326,
    "preview": "@startuml\n\n' convert it with additional command line argument -DRELATIVE_INCLUDE=\"./..\" to use locally\n!if %variable_exi"
  },
  {
    "path": "percy/TestLayoutLandscapeWithLayNoRotate.puml",
    "chars": 1325,
    "preview": "@startuml\n\n' convert it with additional command line argument -DRELATIVE_INCLUDE=\"./..\" to use locally\n!if %variable_exi"
  },
  {
    "path": "percy/TestLegend.puml",
    "chars": 2884,
    "preview": "@startuml\n' convert it with additional command line argument -DRELATIVE_INCLUDE=\"./..\" to use locally\n!if %variable_exis"
  },
  {
    "path": "percy/TestLegendDetailsNone.puml",
    "chars": 1722,
    "preview": "@startuml\n' convert it with additional command line argument -DRELATIVE_INCLUDE=\"./..\" to use locally\n!if %variable_exis"
  },
  {
    "path": "percy/TestLegendDetailsNormal.puml",
    "chars": 1724,
    "preview": "@startuml\n' convert it with additional command line argument -DRELATIVE_INCLUDE=\"./..\" to use locally\n!if %variable_exis"
  },
  {
    "path": "percy/TestLegendDetailsSmall.puml",
    "chars": 1723,
    "preview": "@startuml\n' convert it with additional command line argument -DRELATIVE_INCLUDE=\"./..\" to use locally\n!if %variable_exis"
  },
  {
    "path": "percy/TestLegendHidden.puml",
    "chars": 1547,
    "preview": "@startuml\n' convert it with additional command line argument -DRELATIVE_INCLUDE=\"./..\" to use locally\n!if %variable_exis"
  },
  {
    "path": "percy/TestLegendSprite.puml",
    "chars": 4185,
    "preview": "@startuml\n' convert it with additional command line argument -DRELATIVE_INCLUDE=\"./..\" to use locally\n!if %variable_exis"
  },
  {
    "path": "percy/TestLegendTitle.puml",
    "chars": 695,
    "preview": "@startuml\n\n' convert it with additional command line argument -DRELATIVE_INCLUDE=\"./..\" to use locally\n!if %variable_exi"
  },
  {
    "path": "percy/TestNewStyle.puml",
    "chars": 770,
    "preview": "@startuml\n' activates new style. It has to be set before a theme or a C4_* file is included\n!NEW_C4_STYLE=1\n\n' convert i"
  },
  {
    "path": "percy/TestPersonOutline.puml",
    "chars": 1072,
    "preview": "@startuml\n' convert it with additional command line argument -DRELATIVE_INCLUDE=\"./..\" to use locally\n!if %variable_exis"
  },
  {
    "path": "percy/TestPersonPortrait.puml",
    "chars": 1219,
    "preview": "@startuml\n' convert it with additional command line argument -DRELATIVE_INCLUDE=\"./..\" to use locally\n!if %variable_exis"
  },
  {
    "path": "percy/TestPredefinedSprites.puml",
    "chars": 516,
    "preview": "@startuml\n' convert it with additional command line argument -DRELATIVE_INCLUDE=\"./..\" to use locally\n!if %variable_exis"
  },
  {
    "path": "percy/TestProperty.puml",
    "chars": 1411,
    "preview": "@startuml\n' convert it with additional command line argument -DRELATIVE_INCLUDE=\"./..\" to use locally\n!if %variable_exis"
  },
  {
    "path": "percy/TestPropertyMissingColumns.puml",
    "chars": 645,
    "preview": "@startuml\n' convert it with additional command line argument -DRELATIVE_INCLUDE=\"./..\" to use locally\n!if %variable_exis"
  },
  {
    "path": "percy/TestRelations.puml",
    "chars": 3090,
    "preview": "@startuml\n' convert it with additional command line argument -DRELATIVE_INCLUDE=\"./..\" to use locally\n!if %variable_exis"
  },
  {
    "path": "percy/TestRelationsDynamic.puml",
    "chars": 3322,
    "preview": "@startuml\n' convert it with additional command line argument -DRELATIVE_INCLUDE=\"./..\" to use locally\n!if %variable_exis"
  },
  {
    "path": "percy/TestRelationsTags.puml",
    "chars": 2260,
    "preview": "@startuml\n' convert it with additional command line argument -DRELATIVE_INCLUDE=\"./..\" to use locally\n!if %variable_exis"
  },
  {
    "path": "percy/TestSequenceSkinparams.puml",
    "chars": 3589,
    "preview": "@startuml\n\n!$UseTheme = \"C4_green\"\n\n' convert it with additional command line argument -DRELATIVE_INCLUDE=\"./..\" to use "
  },
  {
    "path": "percy/TestSketchStyle.puml",
    "chars": 1111,
    "preview": "@startuml LAYOUT_AS_SKETCH Sample\n!if %variable_exists(\"RELATIVE_INCLUDE\")\n  !include %get_variable_value(\"RELATIVE_INCL"
  },
  {
    "path": "percy/TestSystemPersonSupportType.puml",
    "chars": 1359,
    "preview": "@startuml\n' convert it with additional command line argument -DRELATIVE_INCLUDE=\"./..\" to use locally\n!if %variable_exis"
  },
  {
    "path": "percy/TestTagSupportsSprite.puml",
    "chars": 4307,
    "preview": "@startuml\n' convert it with additional command line argument -DRELATIVE_INCLUDE=\"./..\" to use locally\n!if %variable_exis"
  },
  {
    "path": "percy/TestTagSupportsSpriteTechn.puml",
    "chars": 4418,
    "preview": "@startuml\n' convert it with additional command line argument -DRELATIVE_INCLUDE=\"./..\" to use locally\n!if %variable_exis"
  },
  {
    "path": "percy/TestThemeC4_All.puml",
    "chars": 3916,
    "preview": "@startuml\n!$UseTheme=C4_blue\n\n!if %variable_exists(\"RELATIVE_INCLUDE\")\n  !include ./TestThemeTemplate.puml\n!else\n  !incl"
  },
  {
    "path": "percy/TestThemeC4_FirstTest.puml",
    "chars": 244,
    "preview": "@startuml\n\n!$UseTheme=C4_FirstTest\n\n!if %variable_exists(\"RELATIVE_INCLUDE\")\n  !include ./TestThemeTemplate.puml\n!else\n "
  },
  {
    "path": "percy/TestThemeC4_united.puml",
    "chars": 985,
    "preview": "@startuml\n\n!$UseTheme = \"C4_united\"\n\n' convert it with additional command line argument -DRELATIVE_INCLUDE=\"./..\" to use"
  },
  {
    "path": "percy/TestThemeSupport.puml",
    "chars": 643,
    "preview": "@startuml\n\n!$ThemeToBeCHecked=C4_FirstTest\n\n' convert it with additional command line argument -DRELATIVE_INCLUDE=\"./..\""
  },
  {
    "path": "percy/TestThemeTemplate.puml",
    "chars": 990,
    "preview": "@startuml\n\n' please defined $ThemeToBeChecked before the file is included (sample see TestThemeC4_FirstTest.puml)\n' [At]"
  },
  {
    "path": "percy/TestVersion.puml",
    "chars": 630,
    "preview": "@startuml\n' convert it with additional command line argument -DRELATIVE_INCLUDE=\"./..\" to use locally\n!if %variable_exis"
  },
  {
    "path": "samples/C4CoreDiagrams.md",
    "chars": 27756,
    "preview": "# C4 Model Diagrams\n\nThe following samples are reproductions with C4-PlantUML from [C4 model core diagrams](https://c4mo"
  },
  {
    "path": "samples/C4_Component Diagram Sample - bigbankplc.puml",
    "chars": 1785,
    "preview": "@startuml\n!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Component.puml\n' uncomment th"
  },
  {
    "path": "samples/C4_Container Diagram Sample - bigbankplc-icons.puml",
    "chars": 2393,
    "preview": "@startuml\n!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Container.puml\n!define DEVICO"
  },
  {
    "path": "samples/C4_Container Diagram Sample - bigbankplc-styles.puml",
    "chars": 2236,
    "preview": "@startuml\n!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Container.puml\n\nSHOW_PERSON_O"
  },
  {
    "path": "samples/C4_Container Diagram Sample - bigbankplc-themes.puml",
    "chars": 1898,
    "preview": "@startuml\n' theme from C4-Stdlib (The !theme statement has to be BEFORE the !include statements.)\n!theme C4_united from "
  },
  {
    "path": "samples/C4_Container Diagram Sample - bigbankplc.puml",
    "chars": 1921,
    "preview": "@startuml\n!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Container.puml\n' uncomment th"
  },
  {
    "path": "samples/C4_Container Diagram Sample - message bus.puml",
    "chars": 2221,
    "preview": "@startuml \"messagebus\"\n!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Container.puml\n'"
  },
  {
    "path": "samples/C4_Container Diagram Sample - techtribesjs.puml",
    "chars": 2024,
    "preview": "@startuml \"techtribesjs\"\n!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Container.puml"
  },
  {
    "path": "samples/C4_Context Diagram Sample - bigbankplc-landscape.puml",
    "chars": 1621,
    "preview": "@startuml\n!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Context.puml\n' uncomment the "
  },
  {
    "path": "samples/C4_Context Diagram Sample - bigbankplc.puml",
    "chars": 957,
    "preview": "@startuml\n!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Context.puml\n' uncomment the "
  },
  {
    "path": "samples/C4_Context Diagram Sample - enterprise.puml",
    "chars": 1449,
    "preview": "@startuml \"enterprise\"\n!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Context.puml\n' u"
  },
  {
    "path": "samples/C4_Deployment Diagram Sample - bigbankplc-details.puml",
    "chars": 3611,
    "preview": "@startuml\n  !include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Deployment.puml\n' uncomment"
  },
  {
    "path": "samples/C4_Deployment Diagram Sample - bigbankplc.puml",
    "chars": 2751,
    "preview": "@startuml\n  !include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Deployment.puml\n' uncomment"
  },
  {
    "path": "samples/C4_Dynamic Diagram Sample - bigbankplc.puml",
    "chars": 903,
    "preview": "@startuml\n!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Dynamic.puml\n\nLAYOUT_WITH_LEG"
  },
  {
    "path": "samples/C4_Dynamic Diagram Sample - message bus - old format.puml",
    "chars": 1878,
    "preview": "@startuml\n!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Dynamic.puml\n\nLAYOUT_TOP_DOWN"
  },
  {
    "path": "samples/C4_Dynamic Diagram Sample - message bus.puml",
    "chars": 1974,
    "preview": "@startuml\n!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Dynamic.puml\n\nLAYOUT_TOP_DOWN"
  },
  {
    "path": "samples/C4_Sequence Diagram Sample - bigbankplc.puml",
    "chars": 906,
    "preview": "@startuml\n!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Sequence.puml\n\nContainer(c1, "
  },
  {
    "path": "samples/C4_Sequence Diagram Sample - complex.puml",
    "chars": 887,
    "preview": "@startuml\n!theme C4_united from https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/themes\n!include htt"
  },
  {
    "path": "themes/puml-theme-C4Language_chinese.puml",
    "chars": 1452,
    "preview": "!$THEME = \"C4Language_chinese\"\n\n!$BOUNDARY_LEGEND_TEXT ?= \"边界\"\n\n!$LEGEND_TITLE_TEXT ?= \"图例\"\n\n!$LEGEND_BOUNDARY ?= \"边界\"\n"
  },
  {
    "path": "themes/puml-theme-C4Language_danish.puml",
    "chars": 1836,
    "preview": "' all available language specific text labels (orig. English)\n!$THEME = \"C4Language_danish\"\n\n!$BOUNDARY_LEGEND_TEXT ?= "
  },
  {
    "path": "themes/puml-theme-C4Language_dutch.puml",
    "chars": 1758,
    "preview": "!$THEME = \"C4Language_dutch\"\n\n!$BOUNDARY_LEGEND_TEXT ?= \"grens\"\n\n!$LEGEND_TITLE_TEXT ?= \"Legenda\"\n\n!$LEGEND_BOUNDARY ?="
  },
  {
    "path": "themes/puml-theme-C4Language_english.puml",
    "chars": 1801,
    "preview": "' all available language specific text labels (orig. English)\n!$THEME = \"C4Language_english\"\n\n!$BOUNDARY_LEGEND_TEXT ?="
  },
  {
    "path": "themes/puml-theme-C4Language_french.puml",
    "chars": 1791,
    "preview": "!$THEME = \"C4Language_french\"\n\n!$BOUNDARY_LEGEND_TEXT ?= \"frontière\"\n\n!$LEGEND_TITLE_TEXT ?= \"Légende\"\n\n!$LEGEND_BOUNDA"
  },
  {
    "path": "themes/puml-theme-C4Language_german.puml",
    "chars": 1830,
    "preview": "!$THEME = \"C4Language_deutsch\"\n\n!$BOUNDARY_LEGEND_TEXT ?= \"Grenze\"\n\n!$LEGEND_TITLE_TEXT ?= \"Legende\"\n\n!$LEGEND_BOUNDARY"
  },
  {
    "path": "themes/puml-theme-C4Language_italian.puml",
    "chars": 1827,
    "preview": "!$THEME = \"C4Language_italian\"\n\n!$BOUNDARY_LEGEND_TEXT ?= \"confine\"\n\n!$LEGEND_TITLE_TEXT ?= \"Legenda\"\n\n!$LEGEND_BOUNDAR"
  },
  {
    "path": "themes/puml-theme-C4Language_japanese.puml",
    "chars": 1494,
    "preview": "!$THEME = \"C4Language_japanese\"\n\n!$BOUNDARY_LEGEND_TEXT ?= \"境界\"\n\n!$LEGEND_TITLE_TEXT ?= \"凡例\"\n\n!$LEGEND_BOUNDARY ?= \"境界\""
  },
  {
    "path": "themes/puml-theme-C4Language_korean.puml",
    "chars": 1520,
    "preview": "!$THEME = \"C4Language_korean\"\n\n!$BOUNDARY_LEGEND_TEXT ?= \"경계\"\n\n!$LEGEND_TITLE_TEXT ?= \"범례\"\n\n!$LEGEND_BOUNDARY ?= \"경계\"\n'"
  },
  {
    "path": "themes/puml-theme-C4Language_portuguese.puml",
    "chars": 1759,
    "preview": "!$THEME = \"C4Language_portuguese\"\n\n!$BOUNDARY_LEGEND_TEXT ?= \"limite\"\n\n!$LEGEND_TITLE_TEXT ?= \"Legenda\"\n\n!$LEGEND_BOUND"
  },
  {
    "path": "themes/puml-theme-C4Language_russian.puml",
    "chars": 1810,
    "preview": "!$THEME = \"C4Language_russian\"\n\n!$BOUNDARY_LEGEND_TEXT ?= \"граница\"\n\n!$LEGEND_TITLE_TEXT ?= \"Легенда\"\n\n!$LEGEND_BOUNDAR"
  },
  {
    "path": "themes/puml-theme-C4Language_spanish.puml",
    "chars": 1776,
    "preview": "!$THEME = \"C4Language_spanish\"\n\n!$BOUNDARY_LEGEND_TEXT ?= \"límite\"\n\n!$LEGEND_TITLE_TEXT ?= \"Leyenda\"\n\n!$LEGEND_BOUNDARY"
  },
  {
    "path": "themes/puml-theme-C4Language_ukrainian.puml",
    "chars": 1809,
    "preview": "!$THEME = \"C4Language_ukrainian\"\n\n!$BOUNDARY_LEGEND_TEXT ?= \"межа\"\n\n!$LEGEND_TITLE_TEXT ?= \"Легенда\"\n\n!$LEGEND_BOUNDARY"
  },
  {
    "path": "themes/puml-theme-C4_FirstTest.puml",
    "chars": 3412,
    "preview": "' First test with themes (it depends on a original theme, changes colors and set $TECHN_FONT_SIZE)\n\n!theme cyborg\n\n!$THE"
  },
  {
    "path": "themes/puml-theme-C4_blue.puml",
    "chars": 2946,
    "preview": "!$THEME = \"C4_blue\"\n\n!$ELEMENT_FONT_COLOR ?= \"#FFFFFF\"\n\n!$PERSON_FONT_COLOR ?= $ELEMENT_FONT_COLOR \n!$PERSON_BG_COLOR ?="
  },
  {
    "path": "themes/puml-theme-C4_blue_new.puml",
    "chars": 3344,
    "preview": "!$THEME = \"C4_blue_new\"\n\n' new style uses rounded rectangles by default, and wireframe style is defined via swapped colo"
  },
  {
    "path": "themes/puml-theme-C4_brown.puml",
    "chars": 2948,
    "preview": "!$THEME = \"C4_brown\"\n\n!$ELEMENT_FONT_COLOR ?= \"#FFFFFF\"\n\n!$PERSON_FONT_COLOR ?= $ELEMENT_FONT_COLOR \n!$PERSON_BG_COLOR ?"
  },
  {
    "path": "themes/puml-theme-C4_brown_new.puml",
    "chars": 3346,
    "preview": "!$THEME = \"C4_brown_new\"\n\n' new style uses rounded rectangles by default, and wireframe style is defined via swapped col"
  },
  {
    "path": "themes/puml-theme-C4_green.puml",
    "chars": 2948,
    "preview": "!$THEME = \"C4_green\"\n\n!$ELEMENT_FONT_COLOR ?= \"#FFFFFF\"\n\n!$PERSON_FONT_COLOR ?= $ELEMENT_FONT_COLOR \n!$PERSON_BG_COLOR ?"
  },
  {
    "path": "themes/puml-theme-C4_green_new.puml",
    "chars": 3346,
    "preview": "!$THEME = \"C4_green_new\"\n\n' new style uses rounded rectangles by default, and wireframe style is defined via swapped col"
  },
  {
    "path": "themes/puml-theme-C4_sandstone.puml",
    "chars": 2116,
    "preview": "!theme sandstone\n\n!$THEME = \"C4_sandstone\"\n\n!$ELEMENT_FONT_COLOR ?= $PRIMARY_TEXT\n\n!$ARROW_COLOR ?= $PRIMARY_LIGHT\n!$ARR"
  },
  {
    "path": "themes/puml-theme-C4_superhero.puml",
    "chars": 2169,
    "preview": "!$BGCOLOR ?= black\n\n!theme superhero\n\n!$THEME = \"C4_superhero\"\n\n!$ELEMENT_FONT_COLOR ?= $PRIMARY_TEXT\n\n!$ARROW_COLOR ?= "
  },
  {
    "path": "themes/puml-theme-C4_united.puml",
    "chars": 2110,
    "preview": "!theme united\n\n!$THEME = \"C4_united\"\n\n!$ELEMENT_FONT_COLOR ?= $PRIMARY_TEXT\n\n!$ARROW_COLOR ?= $PRIMARY_LIGHT\n!$ARROW_FON"
  },
  {
    "path": "themes/puml-theme-C4_violet.puml",
    "chars": 2949,
    "preview": "!$THEME = \"C4_violet\"\n\n!$ELEMENT_FONT_COLOR ?= \"#FFFFFF\"\n\n!$PERSON_FONT_COLOR ?= $ELEMENT_FONT_COLOR \n!$PERSON_BG_COLOR "
  },
  {
    "path": "themes/puml-theme-C4_violet_new.puml",
    "chars": 3347,
    "preview": "!$THEME = \"C4_violet_new\"\n\n' new style uses rounded rectangles by default, and wireframe style is defined via swapped co"
  }
]

About this extraction

This page contains the full source code of the plantuml-stdlib/C4-PlantUML GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 116 files (588.0 KB), approximately 214.2k tokens, and a symbol index with 18 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!