3535import java .util .stream .Collectors ;
3636import java .util .stream .Stream ;
3737
38+ import com .structurizr .export .plantuml .AbstractPlantUMLExporter ;
3839import org .springframework .lang .Nullable ;
3940import org .springframework .modulith .core .ApplicationModule ;
4041import org .springframework .modulith .core .ApplicationModuleDependency ;
@@ -565,6 +566,7 @@ private String render(ComponentView view, DiagramOptions options) {
565566 case C4 :
566567
567568 var c4PlantUmlExporter = new C4PlantUMLExporter ();
569+ addSkinParamsFromOptions (c4PlantUmlExporter , options );
568570 var diagram = c4PlantUmlExporter .export (view );
569571
570572 return diagram .getDefinition ();
@@ -573,12 +575,19 @@ private String render(ComponentView view, DiagramOptions options) {
573575 default :
574576
575577 var plantUmlExporter = new CustomizedPlantUmlExporter ();
578+ addSkinParamsFromOptions (plantUmlExporter , options );
576579 plantUmlExporter .addSkinParam ("componentStyle" , "uml1" );
577580
578581 return plantUmlExporter .export (view ).getDefinition ();
579582 }
580583 }
581584
585+ private void addSkinParamsFromOptions (AbstractPlantUMLExporter exporter , DiagramOptions options ) {
586+ for (var skinParamEntry : options .skinParams .entrySet ()) {
587+ exporter .addSkinParam (skinParamEntry .getKey (), skinParamEntry .getValue ());
588+ }
589+ }
590+
582591 private String createPlantUml (DiagramOptions options ) {
583592
584593 ComponentView componentView = createComponentView (options );
@@ -692,6 +701,7 @@ public static class DiagramOptions {
692701 private final Function <ApplicationModule , String > defaultDisplayName ;
693702 private final DiagramStyle style ;
694703 private final ElementsWithoutRelationships elementsWithoutRelationships ;
704+ private final Map <String , String > skinParams ;
695705
696706 /**
697707 * @param dependencyTypes must not be {@literal null}.
@@ -704,13 +714,15 @@ public static class DiagramOptions {
704714 * @param defaultDisplayName must not be {@literal null}.
705715 * @param style must not be {@literal null}.
706716 * @param elementsWithoutRelationships must not be {@literal null}.
717+ * @param skinParams must not be {@literal null}.
707718 */
708719 DiagramOptions (Set <DependencyType > dependencyTypes , DependencyDepth dependencyDepth ,
709720 Predicate <ApplicationModule > exclusions , Predicate <Component > componentFilter ,
710721 Predicate <ApplicationModule > targetOnly , @ Nullable String targetFileName ,
711722 Function <ApplicationModule , Optional <String >> colorSelector ,
712723 Function <ApplicationModule , String > defaultDisplayName , DiagramStyle style ,
713- ElementsWithoutRelationships elementsWithoutRelationships ) {
724+ ElementsWithoutRelationships elementsWithoutRelationships ,
725+ Map <String , String > skinParams ) {
714726
715727 Assert .notNull (dependencyTypes , "Dependency types must not be null!" );
716728 Assert .notNull (dependencyDepth , "Dependency depth must not be null!" );
@@ -721,6 +733,7 @@ public static class DiagramOptions {
721733 Assert .notNull (defaultDisplayName , "Default display name must not be null!" );
722734 Assert .notNull (style , "DiagramStyle must not be null!" );
723735 Assert .notNull (elementsWithoutRelationships , "ElementsWithoutRelationships must not be null!" );
736+ Assert .notNull (skinParams , "SkinParams must not be null!" );
724737
725738 this .dependencyTypes = dependencyTypes ;
726739 this .dependencyDepth = dependencyDepth ;
@@ -732,30 +745,31 @@ public static class DiagramOptions {
732745 this .defaultDisplayName = defaultDisplayName ;
733746 this .style = style ;
734747 this .elementsWithoutRelationships = elementsWithoutRelationships ;
748+ this .skinParams = skinParams ;
735749 }
736750
737751 /**
738752 * The {@link DependencyDepth} to define which other modules to be included in the diagram to be created.
739753 */
740754 public DiagramOptions withDependencyDepth (DependencyDepth dependencyDepth ) {
741755 return new DiagramOptions (dependencyTypes , dependencyDepth , exclusions , componentFilter , targetOnly ,
742- targetFileName , colorSelector , defaultDisplayName , style , elementsWithoutRelationships );
756+ targetFileName , colorSelector , defaultDisplayName , style , elementsWithoutRelationships , skinParams );
743757 }
744758
745759 /**
746760 * A {@link Predicate} to define the which modules to exclude from the diagram to be created.
747761 */
748762 public DiagramOptions withExclusions (Predicate <ApplicationModule > exclusions ) {
749763 return new DiagramOptions (dependencyTypes , dependencyDepth , exclusions , componentFilter , targetOnly ,
750- targetFileName , colorSelector , defaultDisplayName , style , elementsWithoutRelationships );
764+ targetFileName , colorSelector , defaultDisplayName , style , elementsWithoutRelationships , skinParams );
751765 }
752766
753767 /**
754768 * A {@link Predicate} to define which Structurizr {@link Component}s to be included in the diagram to be created.
755769 */
756770 public DiagramOptions withComponentFilter (Predicate <Component > componentFilter ) {
757771 return new DiagramOptions (dependencyTypes , dependencyDepth , exclusions , componentFilter , targetOnly ,
758- targetFileName , colorSelector , defaultDisplayName , style , elementsWithoutRelationships );
772+ targetFileName , colorSelector , defaultDisplayName , style , elementsWithoutRelationships , skinParams );
759773 }
760774
761775 /**
@@ -765,7 +779,7 @@ public DiagramOptions withComponentFilter(Predicate<Component> componentFilter)
765779 */
766780 public DiagramOptions withTargetOnly (Predicate <ApplicationModule > targetOnly ) {
767781 return new DiagramOptions (dependencyTypes , dependencyDepth , exclusions , componentFilter , targetOnly ,
768- targetFileName , colorSelector , defaultDisplayName , style , elementsWithoutRelationships );
782+ targetFileName , colorSelector , defaultDisplayName , style , elementsWithoutRelationships , skinParams );
769783 }
770784
771785 /**
@@ -777,15 +791,15 @@ public DiagramOptions withTargetFileName(String targetFileName) {
777791 Assert .isTrue (targetFileName .contains ("%s" ), () -> INVALID_FILE_NAME_PATTERN .formatted (targetFileName ));
778792
779793 return new DiagramOptions (dependencyTypes , dependencyDepth , exclusions , componentFilter , targetOnly ,
780- targetFileName , colorSelector , defaultDisplayName , style , elementsWithoutRelationships );
794+ targetFileName , colorSelector , defaultDisplayName , style , elementsWithoutRelationships , skinParams );
781795 }
782796
783797 /**
784798 * A callback to return a hex-encoded color per {@link ApplicationModule}.
785799 */
786800 public DiagramOptions withColorSelector (Function <ApplicationModule , Optional <String >> colorSelector ) {
787801 return new DiagramOptions (dependencyTypes , dependencyDepth , exclusions , componentFilter , targetOnly ,
788- targetFileName , colorSelector , defaultDisplayName , style , elementsWithoutRelationships );
802+ targetFileName , colorSelector , defaultDisplayName , style , elementsWithoutRelationships , skinParams );
789803 }
790804
791805 /**
@@ -794,15 +808,15 @@ public DiagramOptions withColorSelector(Function<ApplicationModule, Optional<Str
794808 */
795809 public DiagramOptions withDefaultDisplayName (Function <ApplicationModule , String > defaultDisplayName ) {
796810 return new DiagramOptions (dependencyTypes , dependencyDepth , exclusions , componentFilter , targetOnly ,
797- targetFileName , colorSelector , defaultDisplayName , style , elementsWithoutRelationships );
811+ targetFileName , colorSelector , defaultDisplayName , style , elementsWithoutRelationships , skinParams );
798812 }
799813
800814 /**
801815 * Which style to render the diagram in. Defaults to {@link DiagramStyle#UML}.
802816 */
803817 public DiagramOptions withStyle (DiagramStyle style ) {
804818 return new DiagramOptions (dependencyTypes , dependencyDepth , exclusions , componentFilter , targetOnly ,
805- targetFileName , colorSelector , defaultDisplayName , style , elementsWithoutRelationships );
819+ targetFileName , colorSelector , defaultDisplayName , style , elementsWithoutRelationships , skinParams );
806820 }
807821
808822 /**
@@ -815,7 +829,18 @@ public DiagramOptions withStyle(DiagramStyle style) {
815829 */
816830 public DiagramOptions withElementsWithoutRelationships (ElementsWithoutRelationships elementsWithoutRelationships ) {
817831 return new DiagramOptions (dependencyTypes , dependencyDepth , exclusions , componentFilter , targetOnly ,
818- targetFileName , colorSelector , defaultDisplayName , style , elementsWithoutRelationships );
832+ targetFileName , colorSelector , defaultDisplayName , style , elementsWithoutRelationships , skinParams );
833+ }
834+
835+ /**
836+ * Configuration setting to add arbitrary skin parameters to the created diagrams.
837+ *
838+ * Applies to both the UML and C4 {@link DiagramStyle styles}.
839+ */
840+ public DiagramOptions withSkinParam (String name , String value ) {
841+ skinParams .put (name , value );
842+ return new DiagramOptions (dependencyTypes , dependencyDepth , exclusions , componentFilter , targetOnly ,
843+ targetFileName , colorSelector , defaultDisplayName , style , elementsWithoutRelationships , skinParams );
819844 }
820845
821846 /**
@@ -828,7 +853,7 @@ public DiagramOptions withElementsWithoutRelationships(ElementsWithoutRelationsh
828853 public static DiagramOptions defaults () {
829854 return new DiagramOptions (ALL_TYPES , DependencyDepth .IMMEDIATE , it -> false , it -> true , it -> false , null ,
830855 __ -> Optional .empty (), it -> it .getDisplayName (), DiagramStyle .C4 ,
831- ElementsWithoutRelationships .HIDDEN );
856+ ElementsWithoutRelationships .HIDDEN , new LinkedHashMap <>() );
832857 }
833858
834859 /**
@@ -845,7 +870,7 @@ public DiagramOptions withDependencyTypes(DependencyType... types) {
845870
846871 return new DiagramOptions (dependencyTypes , dependencyDepth , exclusions , componentFilter , targetOnly ,
847872 targetFileName ,
848- colorSelector , defaultDisplayName , style , elementsWithoutRelationships );
873+ colorSelector , defaultDisplayName , style , elementsWithoutRelationships , skinParams );
849874 }
850875
851876 private Optional <String > getTargetFileName () {
0 commit comments