diff --git a/pom.xml b/pom.xml
index e163f67e5..bec6253cb 100644
--- a/pom.xml
+++ b/pom.xml
@@ -35,7 +35,7 @@
 
     <groupId>net.sourceforge.plantuml</groupId>
     <artifactId>plantuml</artifactId>
-    <version>1.2020.1-SNAPSHOT</version>
+    <version>1.2020.2-SNAPSHOT</version>
     <packaging>jar</packaging>
 
     <name>PlantUML</name>
diff --git a/src/net/sourceforge/plantuml/FontParam.java b/src/net/sourceforge/plantuml/FontParam.java
index a29fc1215..1842eb5d1 100644
--- a/src/net/sourceforge/plantuml/FontParam.java
+++ b/src/net/sourceforge/plantuml/FontParam.java
@@ -94,11 +94,11 @@ public enum FontParam {
 	SEQUENCE_GROUP_HEADER(13, Font.BOLD), //
 	PARTICIPANT(14, Font.PLAIN), //
 	PARTICIPANT_STEREOTYPE(14, Font.ITALIC), //
-	SEQUENCE_TITLE(14, Font.BOLD), //
 	STATE(14, Font.PLAIN), //
 	STATE_ATTRIBUTE(12, Font.PLAIN), //
 	LEGEND(14, Font.PLAIN), //
 	TITLE(18, Font.PLAIN), //
+	// SEQUENCE_TITLE(14, Font.BOLD), //
 	CAPTION(14, Font.PLAIN), //
 	SWIMLANE_TITLE(18, Font.PLAIN), //
 	FOOTER(10, Font.PLAIN, "#888888", FontParamConstant.FAMILY), //
diff --git a/src/net/sourceforge/plantuml/PSystemBuilder.java b/src/net/sourceforge/plantuml/PSystemBuilder.java
index 0ce4e66a5..3fbbe0459 100644
--- a/src/net/sourceforge/plantuml/PSystemBuilder.java
+++ b/src/net/sourceforge/plantuml/PSystemBuilder.java
@@ -75,10 +75,11 @@ import net.sourceforge.plantuml.nwdiag.NwDiagramFactory;
 import net.sourceforge.plantuml.openiconic.PSystemListOpenIconicFactory;
 import net.sourceforge.plantuml.openiconic.PSystemOpenIconicFactory;
 import net.sourceforge.plantuml.oregon.PSystemOregonFactory;
-import net.sourceforge.plantuml.project3.GanttDiagramFactory;
+import net.sourceforge.plantuml.project.GanttDiagramFactory;
 import net.sourceforge.plantuml.salt.PSystemSaltFactory;
 import net.sourceforge.plantuml.sequencediagram.SequenceDiagramFactory;
 import net.sourceforge.plantuml.sprite.ListSpriteDiagramFactory;
+import net.sourceforge.plantuml.sprite.StdlibDiagramFactory;
 import net.sourceforge.plantuml.sprite.PSystemListInternalSpritesFactory;
 import net.sourceforge.plantuml.statediagram.StateDiagramFactory;
 import net.sourceforge.plantuml.stats.StatsUtilsIncrement;
@@ -88,6 +89,7 @@ import net.sourceforge.plantuml.version.License;
 import net.sourceforge.plantuml.version.PSystemLicenseFactory;
 import net.sourceforge.plantuml.version.PSystemVersionFactory;
 import net.sourceforge.plantuml.wbs.WBSDiagramFactory;
+import net.sourceforge.plantuml.wire.WireDiagramFactory;
 
 public class PSystemBuilder {
 
@@ -178,6 +180,7 @@ public class PSystemBuilder {
 		}
 		factories.add(new PSystemDefinitionFactory());
 		factories.add(new ListSpriteDiagramFactory(skinParam));
+		factories.add(new StdlibDiagramFactory(skinParam));
 		factories.add(new PSystemMathFactory(DiagramType.MATH));
 		factories.add(new PSystemLatexFactory(DiagramType.LATEX));
 		// factories.add(new PSystemStatsFactory());
@@ -200,6 +203,7 @@ public class PSystemBuilder {
 		factories.add(new PSystemDedicationFactory());
 		factories.add(new TimingDiagramFactory());
 		factories.add(new HelpFactory());
+		factories.add(new WireDiagramFactory());
 		return factories;
 	}
 
diff --git a/src/net/sourceforge/plantuml/PSystemUtils.java b/src/net/sourceforge/plantuml/PSystemUtils.java
index 060f50966..c3fe74f62 100644
--- a/src/net/sourceforge/plantuml/PSystemUtils.java
+++ b/src/net/sourceforge/plantuml/PSystemUtils.java
@@ -52,7 +52,7 @@ import net.sourceforge.plantuml.cucadiagram.CucaDiagram;
 import net.sourceforge.plantuml.graphic.HtmlColorUtils;
 import net.sourceforge.plantuml.html.CucaDiagramHtmlMaker;
 import net.sourceforge.plantuml.png.PngSplitter;
-import net.sourceforge.plantuml.project3.GanttDiagram;
+import net.sourceforge.plantuml.project.GanttDiagram;
 import net.sourceforge.plantuml.sequencediagram.SequenceDiagram;
 
 public class PSystemUtils {
diff --git a/src/net/sourceforge/plantuml/Pipe.java b/src/net/sourceforge/plantuml/Pipe.java
index cb9031133..4f868072b 100644
--- a/src/net/sourceforge/plantuml/Pipe.java
+++ b/src/net/sourceforge/plantuml/Pipe.java
@@ -153,6 +153,8 @@ public class Pipe {
 			final String s = readOneLine();
 			if (s == null) {
 				closed = true;
+			} else if (s.startsWith("@@@format ")) {
+				manageFormat(s);
 			} else {
 				sb.append(s);
 				sb.append(BackSlash.NEWLINE);
@@ -171,6 +173,14 @@ public class Pipe {
 		return source;
 	}
 
+	private void manageFormat(String s) {
+		if (s.contains("png")) {
+			option.setFileFormatOption(new FileFormatOption(FileFormat.PNG));
+		} else if (s.contains("svg")) {
+			option.setFileFormatOption(new FileFormatOption(FileFormat.SVG));
+		}
+	}
+
 	private String readOneLine() throws IOException {
 		final ByteArrayOutputStream baos = new ByteArrayOutputStream();
 		while (true) {
diff --git a/src/net/sourceforge/plantuml/Pragma.java b/src/net/sourceforge/plantuml/Pragma.java
index 59c69b60e..9e69cc7fb 100644
--- a/src/net/sourceforge/plantuml/Pragma.java
+++ b/src/net/sourceforge/plantuml/Pragma.java
@@ -62,6 +62,14 @@ public class Pragma {
 		return isDefine("horizontallinebetweendifferentpackageallowed");
 	}
 
+	public boolean backToLegacyPackage() {
+		return isDefine("backtolegacypackage");
+	}
+
+	public boolean useNewPackage() {
+		return isDefine("usenewpackage");
+	}
+
 	public boolean useVerticalIf() {
 		final String teoz = getValue("useverticalif");
 		return "true".equalsIgnoreCase(teoz) || "on".equalsIgnoreCase(teoz);
diff --git a/src/net/sourceforge/plantuml/UmlDiagramType.java b/src/net/sourceforge/plantuml/UmlDiagramType.java
index d2a1d76f3..68947c1cc 100644
--- a/src/net/sourceforge/plantuml/UmlDiagramType.java
+++ b/src/net/sourceforge/plantuml/UmlDiagramType.java
@@ -36,5 +36,5 @@
 package net.sourceforge.plantuml;
 
 public enum UmlDiagramType {
-	SEQUENCE, STATE, CLASS, OBJECT, ACTIVITY, DESCRIPTION, COMPOSITE, FLOW, TIMING, BPM, NWDIAG, MINDMAP, WBS, HELP
+	SEQUENCE, STATE, CLASS, OBJECT, ACTIVITY, DESCRIPTION, COMPOSITE, FLOW, TIMING, BPM, NWDIAG, MINDMAP, WBS, WIRE, HELP
 }
diff --git a/src/net/sourceforge/plantuml/activitydiagram/ActivityDiagramFactory.java b/src/net/sourceforge/plantuml/activitydiagram/ActivityDiagramFactory.java
index 2c5146cb7..7d6c7a750 100644
--- a/src/net/sourceforge/plantuml/activitydiagram/ActivityDiagramFactory.java
+++ b/src/net/sourceforge/plantuml/activitydiagram/ActivityDiagramFactory.java
@@ -51,8 +51,8 @@ import net.sourceforge.plantuml.command.Command;
 import net.sourceforge.plantuml.command.CommandFootboxIgnored;
 import net.sourceforge.plantuml.command.CommandRankDir;
 import net.sourceforge.plantuml.command.UmlDiagramFactory;
-import net.sourceforge.plantuml.command.note.FactoryNoteActivityCommand;
-import net.sourceforge.plantuml.command.note.FactoryNoteOnLinkCommand;
+import net.sourceforge.plantuml.command.note.CommandFactoryNoteActivity;
+import net.sourceforge.plantuml.command.note.CommandFactoryNoteOnLink;
 
 public class ActivityDiagramFactory extends UmlDiagramFactory {
 
@@ -79,11 +79,11 @@ public class ActivityDiagramFactory extends UmlDiagramFactory {
 		cmds.add(new CommandEndPartition());
 		cmds.add(new CommandLinkLongActivity());
 
-		final FactoryNoteActivityCommand factoryNoteActivityCommand = new FactoryNoteActivityCommand();
+		final CommandFactoryNoteActivity factoryNoteActivityCommand = new CommandFactoryNoteActivity();
 		cmds.add(factoryNoteActivityCommand.createSingleLine());
 		cmds.add(factoryNoteActivityCommand.createMultiLine(false));
 
-		final FactoryNoteOnLinkCommand factoryNoteOnLinkCommand = new FactoryNoteOnLinkCommand();
+		final CommandFactoryNoteOnLink factoryNoteOnLinkCommand = new CommandFactoryNoteOnLink();
 		cmds.add(factoryNoteOnLinkCommand.createSingleLine());
 		cmds.add(factoryNoteOnLinkCommand.createMultiLine(false));
 
diff --git a/src/net/sourceforge/plantuml/activitydiagram3/ActivityDiagram3.java b/src/net/sourceforge/plantuml/activitydiagram3/ActivityDiagram3.java
index 2e912745d..f1c2c1c72 100644
--- a/src/net/sourceforge/plantuml/activitydiagram3/ActivityDiagram3.java
+++ b/src/net/sourceforge/plantuml/activitydiagram3/ActivityDiagram3.java
@@ -371,10 +371,10 @@ public class ActivityDiagram3 extends UmlDiagram {
 		return CommandExecutionResult.error("Cannot find if");
 	}
 
-	public void startRepeat(HtmlColor color, Display label) {
+	public void startRepeat(HtmlColor color, Display label, BoxStyle boxStyleIn, Colors colors) {
 		manageSwimlaneStrategy();
 		final InstructionRepeat instructionRepeat = new InstructionRepeat(swinlanes.getCurrentSwimlane(), current(),
-				nextLinkRenderer(), color, label);
+				nextLinkRenderer(), color, label, boxStyleIn, colors);
 		current().add(instructionRepeat);
 		setCurrent(instructionRepeat);
 		setNextLinkRendererInternal(LinkRendering.none());
diff --git a/src/net/sourceforge/plantuml/activitydiagram3/ActivityDiagramFactory3.java b/src/net/sourceforge/plantuml/activitydiagram3/ActivityDiagramFactory3.java
index 2a83e2060..2ef005d91 100644
--- a/src/net/sourceforge/plantuml/activitydiagram3/ActivityDiagramFactory3.java
+++ b/src/net/sourceforge/plantuml/activitydiagram3/ActivityDiagramFactory3.java
@@ -111,6 +111,7 @@ public class ActivityDiagramFactory3 extends UmlDiagramFactory {
 		cmds.add(new CommandGroupEnd3());
 		cmds.add(new CommandArrow3());
 		cmds.add(new CommandArrowLong3());
+		cmds.add(new CommandRepeat3());
 		cmds.add(new CommandActivity3());
 		cmds.add(new CommandIf4());
 		cmds.add(new CommandIf2());
@@ -126,7 +127,6 @@ public class ActivityDiagramFactory3 extends UmlDiagramFactory {
 		cmds.add(new CommandCase());
 		cmds.add(new CommandEndSwitch());
 
-		cmds.add(new CommandRepeat3());
 		cmds.add(new CommandRepeatWhile3());
 		cmds.add(new CommandRepeatWhile3Multilines());
 		cmds.add(new CommandBackward3());
diff --git a/src/net/sourceforge/plantuml/activitydiagram3/InstructionRepeat.java b/src/net/sourceforge/plantuml/activitydiagram3/InstructionRepeat.java
index ef21f3b56..40467e168 100644
--- a/src/net/sourceforge/plantuml/activitydiagram3/InstructionRepeat.java
+++ b/src/net/sourceforge/plantuml/activitydiagram3/InstructionRepeat.java
@@ -55,8 +55,9 @@ public class InstructionRepeat implements Instruction {
 	private final LinkRendering nextLinkRenderer;
 	private final Swimlane swimlane;
 	private Swimlane swimlaneOut;
-	private final HtmlColor color;
+//	private final HtmlColor color;
 	private boolean killed = false;
+	private final BoxStyle boxStyleIn;
 
 	private Display backward = Display.NULL;
 	private Display test = Display.NULL;
@@ -66,13 +67,15 @@ public class InstructionRepeat implements Instruction {
 	private boolean testCalled = false;
 	private LinkRendering endRepeatLinkRendering = LinkRendering.none();
 	private LinkRendering backRepeatLinkRendering = LinkRendering.none();
+	private final Colors colors;
 
 	public boolean containsBreak() {
 		return repeatList.containsBreak();
 	}
 
 	public InstructionRepeat(Swimlane swimlane, Instruction parent, LinkRendering nextLinkRenderer, HtmlColor color,
-			Display startLabel) {
+			Display startLabel, BoxStyle boxStyleIn, Colors colors) {
+		this.boxStyleIn = boxStyleIn;
 		this.startLabel = startLabel;
 		this.parent = parent;
 		this.swimlane = swimlane;
@@ -80,7 +83,7 @@ public class InstructionRepeat implements Instruction {
 		if (nextLinkRenderer == null) {
 			throw new IllegalArgumentException();
 		}
-		this.color = color;
+		this.colors = colors;
 	}
 
 	private boolean isLastOfTheParent() {
@@ -100,11 +103,11 @@ public class InstructionRepeat implements Instruction {
 	}
 
 	public Ftile createFtile(FtileFactory factory) {
-		final Ftile back = Display.isNull(backward) ? null : factory.activity(backward, swimlane, BoxStyle.PLAIN,
-				Colors.empty());
-		final Ftile result = factory.repeat(swimlane, swimlaneOut, startLabel,
-				factory.decorateOut(repeatList.createFtile(factory), endRepeatLinkRendering), test, yes, out, color,
-				backRepeatLinkRendering, back, isLastOfTheParent());
+		final Ftile back = Display.isNull(backward) ? null
+				: factory.activity(backward, swimlane, BoxStyle.PLAIN, Colors.empty());
+		final Ftile decorateOut = factory.decorateOut(repeatList.createFtile(factory), endRepeatLinkRendering);
+		final Ftile result = factory.repeat(boxStyleIn, swimlane, swimlaneOut, startLabel, decorateOut, test, yes, out,
+				colors, backRepeatLinkRendering, back, isLastOfTheParent());
 		if (killed) {
 			return new FtileKilled(result);
 		}
diff --git a/src/net/sourceforge/plantuml/activitydiagram3/command/CommandRepeat3.java b/src/net/sourceforge/plantuml/activitydiagram3/command/CommandRepeat3.java
index 1f9dfff74..929768ce3 100644
--- a/src/net/sourceforge/plantuml/activitydiagram3/command/CommandRepeat3.java
+++ b/src/net/sourceforge/plantuml/activitydiagram3/command/CommandRepeat3.java
@@ -35,8 +35,10 @@
  */
 package net.sourceforge.plantuml.activitydiagram3.command;
 
+import net.sourceforge.plantuml.ColorParam;
 import net.sourceforge.plantuml.LineLocation;
 import net.sourceforge.plantuml.activitydiagram3.ActivityDiagram3;
+import net.sourceforge.plantuml.activitydiagram3.ftile.BoxStyle;
 import net.sourceforge.plantuml.command.CommandExecutionResult;
 import net.sourceforge.plantuml.command.SingleLineCommand2;
 import net.sourceforge.plantuml.command.regex.IRegex;
@@ -45,8 +47,11 @@ import net.sourceforge.plantuml.command.regex.RegexLeaf;
 import net.sourceforge.plantuml.command.regex.RegexOptional;
 import net.sourceforge.plantuml.command.regex.RegexResult;
 import net.sourceforge.plantuml.cucadiagram.Display;
+import net.sourceforge.plantuml.cucadiagram.Stereotype;
 import net.sourceforge.plantuml.graphic.HtmlColor;
 import net.sourceforge.plantuml.graphic.color.ColorParser;
+import net.sourceforge.plantuml.graphic.color.ColorType;
+import net.sourceforge.plantuml.graphic.color.Colors;
 
 public class CommandRepeat3 extends SingleLineCommand2<ActivityDiagram3> {
 
@@ -56,20 +61,39 @@ public class CommandRepeat3 extends SingleLineCommand2<ActivityDiagram3> {
 
 	static IRegex getRegexConcat() {
 		return RegexConcat.build(CommandRepeat3.class.getName(), RegexLeaf.start(), //
+				new RegexLeaf("STEREO", "(\\<{2}.*\\>{2})?"), //
 				ColorParser.exp4(), //
 				new RegexLeaf("repeat"), //
 				RegexLeaf.spaceZeroOrMore(), //
 				new RegexOptional(new RegexLeaf("LABEL", ":(.*?)")), //
-				new RegexLeaf(";?"), //
+				new RegexOptional(new RegexLeaf("STYLE", CommandActivity3.ENDING_GROUP)), //
+				// new RegexLeaf(";?"), //
 				RegexLeaf.end());
 	}
 
+	private static ColorParser color() {
+		return ColorParser.simpleColor(ColorType.BACK);
+	}
+
 	@Override
 	protected CommandExecutionResult executeArg(ActivityDiagram3 diagram, LineLocation location, RegexResult arg) {
 		final HtmlColor color = diagram.getSkinParam().getIHtmlColorSet().getColorIfValid(arg.get("COLOR", 0));
 		final Display label = Display.getWithNewlines(arg.get("LABEL", 0));
+		final BoxStyle boxStyle;
+		final String styleString = arg.get("STYLE", 0);
+		if (styleString == null) {
+			boxStyle = BoxStyle.PLAIN;
+		} else {
+			boxStyle = BoxStyle.fromChar(styleString.charAt(0));
+		}
+		Colors colors = color().getColor(arg, diagram.getSkinParam().getIHtmlColorSet());
+		final String stereo = arg.get("STEREO", 0);
+		if (stereo != null) {
+			final Stereotype stereotype = new Stereotype(stereo);
+			colors = colors.applyStereotype(stereotype, diagram.getSkinParam(), ColorParam.activityBackground);
+		}
 
-		diagram.startRepeat(color, label);
+		diagram.startRepeat(color, label, boxStyle, colors);
 
 		return CommandExecutionResult.ok();
 	}
diff --git a/src/net/sourceforge/plantuml/activitydiagram3/ftile/FtileFactory.java b/src/net/sourceforge/plantuml/activitydiagram3/ftile/FtileFactory.java
index 2f38386b4..3ad653946 100644
--- a/src/net/sourceforge/plantuml/activitydiagram3/ftile/FtileFactory.java
+++ b/src/net/sourceforge/plantuml/activitydiagram3/ftile/FtileFactory.java
@@ -77,9 +77,9 @@ public interface FtileFactory {
 
 	public Ftile assembly(Ftile tile1, Ftile tile2);
 
-	public Ftile repeat(Swimlane swimlane, Swimlane swimlaneOut, Display startLabel, Ftile repeat, Display test,
-			Display yes, Display out, HtmlColor color, LinkRendering backRepeatLinkRendering, Ftile backward,
-			boolean noOut);
+	public Ftile repeat(BoxStyle boxStyleIn, Swimlane swimlane, Swimlane swimlaneOut, Display startLabel, Ftile repeat,
+			Display test, Display yes, Display out, Colors colors, LinkRendering backRepeatLinkRendering,
+			Ftile backward, boolean noOut);
 
 	public Ftile createWhile(Swimlane swimlane, Ftile whileBlock, Display test, Display yes, Display out,
 			LinkRendering afterEndwhile, HtmlColor color, Instruction specialOut);
diff --git a/src/net/sourceforge/plantuml/activitydiagram3/ftile/FtileFactoryDelegator.java b/src/net/sourceforge/plantuml/activitydiagram3/ftile/FtileFactoryDelegator.java
index 04cd426d1..fce49ae03 100644
--- a/src/net/sourceforge/plantuml/activitydiagram3/ftile/FtileFactoryDelegator.java
+++ b/src/net/sourceforge/plantuml/activitydiagram3/ftile/FtileFactoryDelegator.java
@@ -85,8 +85,8 @@ public class FtileFactoryDelegator implements FtileFactory {
 		final LinkRendering linkRendering = tile.getInLinkRendering();
 		if (linkRendering == null) {
 			if (SkinParam.USE_STYLES()) {
-				final Style style = getDefaultStyleDefinitionArrow().getMergedStyle(
-						skinParam().getCurrentStyleBuilder());
+				final Style style = getDefaultStyleDefinitionArrow()
+						.getMergedStyle(skinParam().getCurrentStyleBuilder());
 				return Rainbow.build(style, skinParam().getIHtmlColorSet());
 			} else {
 				color = Rainbow.build(skinParam());
@@ -96,8 +96,8 @@ public class FtileFactoryDelegator implements FtileFactory {
 		}
 		if (color.size() == 0) {
 			if (SkinParam.USE_STYLES()) {
-				final Style style = getDefaultStyleDefinitionArrow().getMergedStyle(
-						skinParam().getCurrentStyleBuilder());
+				final Style style = getDefaultStyleDefinitionArrow()
+						.getMergedStyle(skinParam().getCurrentStyleBuilder());
 				return Rainbow.build(style, skinParam().getIHtmlColorSet());
 			} else {
 				color = Rainbow.build(skinParam());
@@ -179,10 +179,10 @@ public class FtileFactoryDelegator implements FtileFactory {
 		return factory.assembly(tile1, tile2);
 	}
 
-	public Ftile repeat(Swimlane swimlane, Swimlane swimlaneOut, Display startLabel, Ftile repeat, Display test,
-			Display yes, Display out, HtmlColor color, LinkRendering backRepeatLinkRendering, Ftile backward,
+	public Ftile repeat(BoxStyle boxStyleIn, Swimlane swimlane, Swimlane swimlaneOut, Display startLabel, Ftile repeat,
+			Display test, Display yes, Display out, Colors colors, LinkRendering backRepeatLinkRendering, Ftile backward,
 			boolean noOut) {
-		return factory.repeat(swimlane, swimlaneOut, startLabel, repeat, test, yes, out, color,
+		return factory.repeat(boxStyleIn, swimlane, swimlaneOut, startLabel, repeat, test, yes, out, colors,
 				backRepeatLinkRendering, backward, noOut);
 	}
 
diff --git a/src/net/sourceforge/plantuml/activitydiagram3/ftile/vcompact/FtileFactoryDelegatorRepeat.java b/src/net/sourceforge/plantuml/activitydiagram3/ftile/vcompact/FtileFactoryDelegatorRepeat.java
index 94266dd75..2086d28ea 100644
--- a/src/net/sourceforge/plantuml/activitydiagram3/ftile/vcompact/FtileFactoryDelegatorRepeat.java
+++ b/src/net/sourceforge/plantuml/activitydiagram3/ftile/vcompact/FtileFactoryDelegatorRepeat.java
@@ -59,6 +59,7 @@ import net.sourceforge.plantuml.cucadiagram.Display;
 import net.sourceforge.plantuml.graphic.FontConfiguration;
 import net.sourceforge.plantuml.graphic.HtmlColor;
 import net.sourceforge.plantuml.graphic.Rainbow;
+import net.sourceforge.plantuml.graphic.color.ColorType;
 import net.sourceforge.plantuml.graphic.color.Colors;
 import net.sourceforge.plantuml.style.PName;
 import net.sourceforge.plantuml.style.Style;
@@ -73,31 +74,36 @@ public class FtileFactoryDelegatorRepeat extends FtileFactoryDelegator {
 	}
 
 	@Override
-	public Ftile repeat(Swimlane swimlane, Swimlane swimlaneOut, Display startLabel, final Ftile repeat, Display test,
-			Display yes, Display out, HtmlColor color, LinkRendering backRepeatLinkRendering, Ftile backward,
-			boolean noOut) {
+	public Ftile repeat(BoxStyle boxStyleIn, Swimlane swimlane, Swimlane swimlaneOut, Display startLabel,
+			final Ftile repeat, Display test, Display yes, Display out, Colors colors,
+			LinkRendering backRepeatLinkRendering, Ftile backward, boolean noOut) {
 
 		final ConditionStyle conditionStyle = skinParam().getConditionStyle();
 
 		final HtmlColor borderColor;
-		final HtmlColor backColor;
+		final HtmlColor diamondColor;
 		final Rainbow arrowColor;
 		final FontConfiguration fcDiamond;
 		final FontConfiguration fcArrow;
 		if (SkinParam.USE_STYLES()) {
-			final Style styleArrow = getDefaultStyleDefinitionArrow().getMergedStyle(
-					skinParam().getCurrentStyleBuilder());
-			final Style styleDiamond = getDefaultStyleDefinitionDiamond().getMergedStyle(
-					skinParam().getCurrentStyleBuilder());
+			final Style styleArrow = getDefaultStyleDefinitionArrow()
+					.getMergedStyle(skinParam().getCurrentStyleBuilder());
+			final Style styleDiamond = getDefaultStyleDefinitionDiamond()
+					.getMergedStyle(skinParam().getCurrentStyleBuilder());
 			borderColor = styleDiamond.value(PName.LineColor).asColor(skinParam().getIHtmlColorSet());
-			backColor = styleDiamond.value(PName.BackGroundColor).asColor(skinParam().getIHtmlColorSet());
+			diamondColor = styleDiamond.value(PName.BackGroundColor).asColor(skinParam().getIHtmlColorSet());
 			arrowColor = Rainbow.build(styleArrow, skinParam().getIHtmlColorSet());
 			fcDiamond = styleDiamond.getFontConfiguration(skinParam().getIHtmlColorSet());
 			fcArrow = styleArrow.getFontConfiguration(skinParam().getIHtmlColorSet());
 		} else {
 			borderColor = getRose().getHtmlColor(skinParam(), ColorParam.activityDiamondBorder);
-			backColor = color == null ? getRose().getHtmlColor(skinParam(), ColorParam.activityDiamondBackground)
-					: color;
+			// diamondColor = color == null ? getRose().getHtmlColor(skinParam(),
+			// ColorParam.activityDiamondBackground) : color;
+			if (colors.getColor(ColorType.BACK) != null && Display.isNull(startLabel)) {
+				diamondColor = colors.getColor(ColorType.BACK);
+			} else {
+				diamondColor = getRose().getHtmlColor(skinParam(), ColorParam.activityDiamondBackground);
+			}
 			arrowColor = Rainbow.build(skinParam());
 			fcDiamond = new FontConfiguration(skinParam(), FontParam.ACTIVITY_DIAMOND, null);
 			fcArrow = new FontConfiguration(skinParam(), FontParam.ARROW, null);
@@ -106,18 +112,17 @@ public class FtileFactoryDelegatorRepeat extends FtileFactoryDelegator {
 		final LinkRendering endRepeatLinkRendering = repeat.getOutLinkRendering();
 		final Rainbow endRepeatLinkColor = endRepeatLinkRendering == null ? null : endRepeatLinkRendering.getRainbow();
 
-		final Ftile backStart = Display.isNull(startLabel) ? null : this.activity(startLabel, swimlane, BoxStyle.PLAIN,
-				Colors.empty());
+		final Ftile entry = getEntry(swimlane, startLabel, colors, boxStyleIn);
 
-		Ftile result = FtileRepeat.create(backRepeatLinkRendering, swimlane, swimlaneOut, backStart, repeat, test, yes,
-				out, borderColor, backColor, arrowColor, endRepeatLinkColor, conditionStyle, this.skinParam(),
-				fcDiamond, fcArrow, backward, noOut);
+		Ftile result = FtileRepeat.create(backRepeatLinkRendering, swimlane, swimlaneOut, entry, repeat, test, yes, out,
+				borderColor, diamondColor, arrowColor, endRepeatLinkColor, conditionStyle, this.skinParam(), fcDiamond,
+				fcArrow, backward, noOut);
 
 		final List<WeldingPoint> weldingPoints = repeat.getWeldingPoints();
 		if (weldingPoints.size() > 0) {
 			// printAllChild(repeat);
 
-			final Ftile diamondBreak = new FtileDiamond(repeat.skinParam(), backColor, borderColor, swimlane);
+			final Ftile diamondBreak = new FtileDiamond(repeat.skinParam(), diamondColor, borderColor, swimlane);
 			result = assembly(FtileUtils.addHorizontalMargin(result, 10, 0), diamondBreak);
 			final Genealogy genealogy = new Genealogy(result);
 
@@ -129,8 +134,8 @@ public class FtileFactoryDelegatorRepeat extends FtileFactoryDelegator {
 					final UTranslate tr2 = genealogy.getTranslate(diamondBreak, ug.getStringBounder());
 					final Dimension2D dimDiamond = diamondBreak.calculateDimension(ug.getStringBounder());
 
-					final Snake snake = new Snake(getFtile1().arrowHorizontalAlignment(), arrowColor, Arrows
-							.asToRight());
+					final Snake snake = new Snake(getFtile1().arrowHorizontalAlignment(), arrowColor,
+							Arrows.asToRight());
 					snake.addPoint(tr1.getDx(), tr1.getDy());
 					snake.addPoint(0, tr1.getDy());
 					snake.addPoint(0, tr2.getDy() + dimDiamond.getHeight() / 2);
@@ -151,4 +156,12 @@ public class FtileFactoryDelegatorRepeat extends FtileFactoryDelegator {
 		}
 		return result;
 	}
+
+	private Ftile getEntry(Swimlane swimlane, Display startLabel, Colors colors, BoxStyle boxStyleIn) {
+		if (Display.isNull(startLabel)) {
+			return null;
+		}
+		// final Colors colors = Colors.empty().add(ColorType.BACK, back);
+		return this.activity(startLabel, swimlane, boxStyleIn, colors);
+	}
 }
diff --git a/src/net/sourceforge/plantuml/activitydiagram3/ftile/vcompact/FtileRepeat.java b/src/net/sourceforge/plantuml/activitydiagram3/ftile/vcompact/FtileRepeat.java
index b3becbd6d..3fe4b96fd 100644
--- a/src/net/sourceforge/plantuml/activitydiagram3/ftile/vcompact/FtileRepeat.java
+++ b/src/net/sourceforge/plantuml/activitydiagram3/ftile/vcompact/FtileRepeat.java
@@ -111,8 +111,8 @@ class FtileRepeat extends AbstractFtile {
 	}
 
 	public static Ftile create(LinkRendering backRepeatLinkRendering, Swimlane swimlane, Swimlane swimlaneOut,
-			Ftile backStart, Ftile repeat, Display test, Display yes, Display out, HtmlColor borderColor,
-			HtmlColor backColor, Rainbow arrowColor, Rainbow endRepeatLinkColor, ConditionStyle conditionStyle,
+			Ftile entry, Ftile repeat, Display test, Display yes, Display out, HtmlColor borderColor,
+			HtmlColor diamondColor, Rainbow arrowColor, Rainbow endRepeatLinkColor, ConditionStyle conditionStyle,
 			ISkinSimple spriteContainer, FontConfiguration fcDiamond, FontConfiguration fcArrow, Ftile backward,
 			boolean noOut) {
 
@@ -126,10 +126,10 @@ class FtileRepeat extends AbstractFtile {
 
 		final Ftile diamond1;
 		// assert swimlane == repeat.getSwimlaneIn();
-		if (backStart == null) {
-			diamond1 = new FtileDiamond(repeat.skinParam(), backColor, borderColor, repeat.getSwimlaneIn());
+		if (entry == null) {
+			diamond1 = new FtileDiamond(repeat.skinParam(), diamondColor, borderColor, repeat.getSwimlaneIn());
 		} else {
-			diamond1 = backStart;
+			diamond1 = entry;
 		}
 		final FtileRepeat result;
 		if (conditionStyle == ConditionStyle.INSIDE) {
@@ -137,16 +137,16 @@ class FtileRepeat extends AbstractFtile {
 			if (noOut && Display.isNull(test)) {
 				diamond2 = new FtileEmpty(repeat.skinParam());
 			} else {
-				diamond2 = new FtileDiamondInside(repeat.skinParam(), backColor, borderColor, swimlaneOut, tbTest)
+				diamond2 = new FtileDiamondInside(repeat.skinParam(), diamondColor, borderColor, swimlaneOut, tbTest)
 						.withEast(yesTb).withSouth(outTb);
 			}
 			result = new FtileRepeat(repeat, diamond1, diamond2, TextBlockUtils.empty(0, 0), backward);
 		} else if (conditionStyle == ConditionStyle.DIAMOND) {
-			final Ftile diamond2 = new FtileDiamond(repeat.skinParam(), backColor, borderColor, swimlane)
+			final Ftile diamond2 = new FtileDiamond(repeat.skinParam(), diamondColor, borderColor, swimlane)
 					.withEast(tbTest);
 			result = new FtileRepeat(repeat, diamond1, diamond2, tbTest, backward);
 		} else if (conditionStyle == ConditionStyle.FOO1) {
-			final Ftile diamond2 = new FtileDiamondFoo1(repeat.skinParam(), backColor, borderColor, swimlane, tbTest);
+			final Ftile diamond2 = new FtileDiamondFoo1(repeat.skinParam(), diamondColor, borderColor, swimlane, tbTest);
 			result = new FtileRepeat(repeat, diamond1, diamond2, TextBlockUtils.empty(0, 0), backward);
 		} else {
 			throw new IllegalStateException();
diff --git a/src/net/sourceforge/plantuml/activitydiagram3/ftile/vcompact/VCompactFactory.java b/src/net/sourceforge/plantuml/activitydiagram3/ftile/vcompact/VCompactFactory.java
index a7fe6e000..3dedf46a4 100644
--- a/src/net/sourceforge/plantuml/activitydiagram3/ftile/vcompact/VCompactFactory.java
+++ b/src/net/sourceforge/plantuml/activitydiagram3/ftile/vcompact/VCompactFactory.java
@@ -122,7 +122,8 @@ public class VCompactFactory implements FtileFactory {
 	}
 
 	public Ftile spot(Swimlane swimlane, String spot, HtmlColor color) {
-		// final HtmlColor color = rose.getHtmlColor(skinParam, ColorParam.activityBackground);
+		// final HtmlColor color = rose.getHtmlColor(skinParam,
+		// ColorParam.activityBackground);
 		final UFont font = skinParam.getFont(null, false, FontParam.ACTIVITY);
 		return new FtileCircleSpot(skinParam(), swimlane, spot, font, color);
 	}
@@ -140,8 +141,10 @@ public class VCompactFactory implements FtileFactory {
 	}
 
 	public Ftile activity(Display label, Swimlane swimlane, BoxStyle boxStyle, Colors colors) {
-		// final HtmlColor borderColor = rose.getHtmlColor(skinParam, ColorParam.activityBorder);
-		// final HtmlColor backColor = color == null ? rose.getHtmlColor(skinParam, ColorParam.activityBackground) :
+		// final HtmlColor borderColor = rose.getHtmlColor(skinParam,
+		// ColorParam.activityBorder);
+		// final HtmlColor backColor = color == null ? rose.getHtmlColor(skinParam,
+		// ColorParam.activityBackground) :
 		// color;
 		final UFont font = skinParam.getFont(null, false, FontParam.ACTIVITY);
 		return FtileBox.create(colors.mute(skinParam), label, swimlane, boxStyle);
@@ -159,9 +162,9 @@ public class VCompactFactory implements FtileFactory {
 		return new FtileAssemblySimple(tile1, tile2);
 	}
 
-	public Ftile repeat(Swimlane swimlane, Swimlane swimlaneOut, Display startLabel, Ftile repeat, Display test,
-			Display yes, Display out, HtmlColor color, LinkRendering backRepeatLinkRendering, Ftile backward,
-			boolean noOut) {
+	public Ftile repeat(BoxStyle boxStyleIn, Swimlane swimlane, Swimlane swimlaneOut, Display startLabel, Ftile repeat,
+			Display test, Display yes, Display out, Colors colors, LinkRendering backRepeatLinkRendering,
+			Ftile backward, boolean noOut) {
 		return repeat;
 	}
 
diff --git a/src/net/sourceforge/plantuml/classdiagram/ClassDiagram.java b/src/net/sourceforge/plantuml/classdiagram/ClassDiagram.java
index 4e17c1041..16c7b1257 100644
--- a/src/net/sourceforge/plantuml/classdiagram/ClassDiagram.java
+++ b/src/net/sourceforge/plantuml/classdiagram/ClassDiagram.java
@@ -37,6 +37,7 @@ package net.sourceforge.plantuml.classdiagram;
 
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.Set;
 
 import net.sourceforge.plantuml.FileFormatOption;
 import net.sourceforge.plantuml.ISkinSimple;
@@ -53,6 +54,7 @@ import net.sourceforge.plantuml.cucadiagram.ILeaf;
 import net.sourceforge.plantuml.cucadiagram.Ident;
 import net.sourceforge.plantuml.cucadiagram.LeafType;
 import net.sourceforge.plantuml.cucadiagram.Link;
+import net.sourceforge.plantuml.cucadiagram.SuperGroup;
 import net.sourceforge.plantuml.graphic.TextBlock;
 import net.sourceforge.plantuml.graphic.USymbol;
 import net.sourceforge.plantuml.objectdiagram.AbstractClassOrObjectDiagram;
diff --git a/src/net/sourceforge/plantuml/classdiagram/ClassDiagramFactory.java b/src/net/sourceforge/plantuml/classdiagram/ClassDiagramFactory.java
index f9c87602d..068293331 100644
--- a/src/net/sourceforge/plantuml/classdiagram/ClassDiagramFactory.java
+++ b/src/net/sourceforge/plantuml/classdiagram/ClassDiagramFactory.java
@@ -67,12 +67,14 @@ import net.sourceforge.plantuml.command.CommandPackageEmpty;
 import net.sourceforge.plantuml.command.CommandPage;
 import net.sourceforge.plantuml.command.CommandRankDir;
 import net.sourceforge.plantuml.command.UmlDiagramFactory;
-import net.sourceforge.plantuml.command.note.FactoryNoteCommand;
-import net.sourceforge.plantuml.command.note.FactoryNoteOnEntityCommand;
-import net.sourceforge.plantuml.command.note.FactoryNoteOnLinkCommand;
-import net.sourceforge.plantuml.command.note.FactoryTipOnEntityCommand;
+import net.sourceforge.plantuml.command.note.CommandConstraintOnLinks;
+import net.sourceforge.plantuml.command.note.CommandFactoryNote;
+import net.sourceforge.plantuml.command.note.CommandFactoryNoteOnEntity;
+import net.sourceforge.plantuml.command.note.CommandFactoryNoteOnLink;
+import net.sourceforge.plantuml.command.note.CommandFactoryTipOnEntity;
 import net.sourceforge.plantuml.command.regex.RegexLeaf;
 import net.sourceforge.plantuml.descdiagram.command.CommandCreateElementMultilines;
+import net.sourceforge.plantuml.descdiagram.command.CommandCreateElementParenthesis;
 import net.sourceforge.plantuml.descdiagram.command.CommandNewpage;
 import net.sourceforge.plantuml.descdiagram.command.CommandPackageWithUSymbol;
 import net.sourceforge.plantuml.objectdiagram.command.CommandCreateEntityObject;
@@ -112,6 +114,7 @@ public class ClassDiagramFactory extends UmlDiagramFactory {
 		cmds.add(new CommandCreateEntityObject());
 
 		cmds.add(new CommandAllowMixing());
+		cmds.add(new CommandCreateElementParenthesis());
 		cmds.add(new CommandLayoutNewLine());
 
 		cmds.add(new CommandPackage());
@@ -121,7 +124,7 @@ public class ClassDiagramFactory extends UmlDiagramFactory {
 
 		cmds.add(new CommandCreateElementFull2(Mode.NORMAL_KEYWORD));
 		cmds.add(new CommandCreateElementFull2(Mode.WITH_MIX_PREFIX));
-		final FactoryNoteCommand factoryNoteCommand = new FactoryNoteCommand();
+		final CommandFactoryNote factoryNoteCommand = new CommandFactoryNote();
 		cmds.add(factoryNoteCommand.createSingleLine());
 
 		cmds.add(new CommandNamespace());
@@ -134,12 +137,12 @@ public class ClassDiagramFactory extends UmlDiagramFactory {
 
 		cmds.add(new CommandImport());
 
-		final FactoryTipOnEntityCommand factoryTipOnEntityCommand = new FactoryTipOnEntityCommand("a", new RegexLeaf(
+		final CommandFactoryTipOnEntity factoryTipOnEntityCommand = new CommandFactoryTipOnEntity("a", new RegexLeaf(
 				"ENTITY", "(" + CommandCreateClass.CODE_NO_DOTDOT + "|[%g][^%g]+[%g])::([%g][^%g]+[%g]|[^%s]+)"));
 		cmds.add(factoryTipOnEntityCommand.createMultiLine(true));
 		cmds.add(factoryTipOnEntityCommand.createMultiLine(false));
 
-		final FactoryNoteOnEntityCommand factoryNoteOnEntityCommand = new FactoryNoteOnEntityCommand("class",
+		final CommandFactoryNoteOnEntity factoryNoteOnEntityCommand = new CommandFactoryNoteOnEntity("class",
 				new RegexLeaf("ENTITY", "(" + CommandCreateClass.CODE + "|[%g][^%g]+[%g])"));
 		cmds.add(factoryNoteOnEntityCommand.createSingleLine());
 		cmds.add(new CommandUrl());
@@ -148,9 +151,10 @@ public class ClassDiagramFactory extends UmlDiagramFactory {
 		cmds.add(factoryNoteOnEntityCommand.createMultiLine(false));
 		cmds.add(factoryNoteCommand.createMultiLine(false));
 
-		final FactoryNoteOnLinkCommand factoryNoteOnLinkCommand = new FactoryNoteOnLinkCommand();
+		final CommandFactoryNoteOnLink factoryNoteOnLinkCommand = new CommandFactoryNoteOnLink();
 		cmds.add(factoryNoteOnLinkCommand.createSingleLine());
 		cmds.add(factoryNoteOnLinkCommand.createMultiLine(false));
+		cmds.add(new CommandConstraintOnLinks());
 
 		cmds.add(new CommandDiamondAssociation());
 
diff --git a/src/net/sourceforge/plantuml/classdiagram/command/CommandCreateClassMultilines.java b/src/net/sourceforge/plantuml/classdiagram/command/CommandCreateClassMultilines.java
index e34aff45a..b6da5b900 100644
--- a/src/net/sourceforge/plantuml/classdiagram/command/CommandCreateClassMultilines.java
+++ b/src/net/sourceforge/plantuml/classdiagram/command/CommandCreateClassMultilines.java
@@ -247,6 +247,7 @@ public class CommandCreateClassMultilines extends CommandMultilines2<ClassDiagra
 			result = diagram.getLeafSmart(ident);
 			if (result != null) {
 				// result = diagram.getOrCreateLeaf(ident, code, null, null);
+				diagram.setLastEntity(result);
 				if (result.muteToType(type, null) == false) {
 					return null;
 				}
diff --git a/src/net/sourceforge/plantuml/classdiagram/command/CommandLinkClass.java b/src/net/sourceforge/plantuml/classdiagram/command/CommandLinkClass.java
index e3bb06c68..ef09eba4e 100644
--- a/src/net/sourceforge/plantuml/classdiagram/command/CommandLinkClass.java
+++ b/src/net/sourceforge/plantuml/classdiagram/command/CommandLinkClass.java
@@ -82,15 +82,15 @@ final public class CommandLinkClass extends SingleLineCommand2<AbstractClassOrOb
 								new RegexLeaf("HEADER", "@([\\d.]+)"), //
 								RegexLeaf.spaceOneOrMore() //
 						)), new RegexOr( //
-						new RegexLeaf("ENT1", getClassIdentifier()),//
-						new RegexLeaf("COUPLE1", COUPLE)), //
+								new RegexLeaf("ENT1", getClassIdentifier()), //
+								new RegexLeaf("COUPLE1", COUPLE)), //
 				RegexLeaf.spaceZeroOrMore(), //
 				new RegexOptional(new RegexLeaf("FIRST_LABEL", "[%g]([^%g]+)[%g]")), //
 
 				RegexLeaf.spaceZeroOrMore(), //
 
 				new RegexConcat(
-				//
+						//
 						new RegexLeaf("ARROW_HEAD1", "([%s]+[ox]|[)#\\[<*+^}]|[<\\[]\\||\\}o|\\}\\||\\|o|\\|\\|)?"), //
 						new RegexLeaf("ARROW_BODY1", "([-=.]+)"), //
 						new RegexLeaf("ARROW_STYLE1", "(?:\\[(" + CommandLinkElement.LINE_STYLE + ")\\])?"), //
@@ -208,24 +208,24 @@ final public class CommandLinkClass extends SingleLineCommand2<AbstractClassOrOb
 				final Matcher2 m1 = p1.matcher(labelLink);
 				if (m1.matches()) {
 					firstLabel = m1.group(1);
-					labelLink = StringUtils.trin(StringUtils.eventuallyRemoveStartingAndEndingDoubleQuote(
-							StringUtils.trin(m1.group(2)), "\""));
+					labelLink = StringUtils.trin(StringUtils
+							.eventuallyRemoveStartingAndEndingDoubleQuote(StringUtils.trin(m1.group(2)), "\""));
 					secondLabel = m1.group(3);
 				} else {
 					final Pattern2 p2 = MyPattern.cmpile("^[%g]([^%g]+)[%g]([^%g]+)$");
 					final Matcher2 m2 = p2.matcher(labelLink);
 					if (m2.matches()) {
 						firstLabel = m2.group(1);
-						labelLink = StringUtils.trin(StringUtils.eventuallyRemoveStartingAndEndingDoubleQuote(
-								StringUtils.trin(m2.group(2)), "\""));
+						labelLink = StringUtils.trin(StringUtils
+								.eventuallyRemoveStartingAndEndingDoubleQuote(StringUtils.trin(m2.group(2)), "\""));
 						secondLabel = null;
 					} else {
 						final Pattern2 p3 = MyPattern.cmpile("^([^%g]+)[%g]([^%g]+)[%g]$");
 						final Matcher2 m3 = p3.matcher(labelLink);
 						if (m3.matches()) {
 							firstLabel = null;
-							labelLink = StringUtils.trin(StringUtils.eventuallyRemoveStartingAndEndingDoubleQuote(
-									StringUtils.trin(m3.group(1)), "\""));
+							labelLink = StringUtils.trin(StringUtils
+									.eventuallyRemoveStartingAndEndingDoubleQuote(StringUtils.trin(m3.group(1)), "\""));
 							secondLabel = m3.group(2);
 						}
 					}
@@ -281,8 +281,8 @@ final public class CommandLinkClass extends SingleLineCommand2<AbstractClassOrOb
 			if (diagram.V1972()) {
 				return diagram.getGroupVerySmart(ident);
 			}
-			final Code tap = ident.toCode(diagram);
-			return diagram.getGroup(tap);
+//			final Code tap = ident.toCode(diagram);
+			return diagram.getGroup(code);
 		}
 		if (diagram.V1972()) {
 			final IEntity result = pure.size() == 1 ? diagram.getLeafVerySmart(ident) : diagram.getLeafStrict(ident);
@@ -361,10 +361,10 @@ final public class CommandLinkClass extends SingleLineCommand2<AbstractClassOrOb
 	private CommandExecutionResult executePackageLink(AbstractClassOrObjectDiagram diagram, RegexResult arg) {
 		final String ent1String = StringUtils.eventuallyRemoveStartingAndEndingDoubleQuote(arg.get("ENT1", 0), "\"");
 		final String ent2String = StringUtils.eventuallyRemoveStartingAndEndingDoubleQuote(arg.get("ENT2", 0), "\"");
-		final IEntity cl1 = diagram.V1972() ? diagram.getGroupVerySmart(diagram.buildLeafIdent(ent1String)) : diagram
-				.getGroup(diagram.buildCode(ent1String));
-		final IEntity cl2 = diagram.V1972() ? diagram.getGroupVerySmart(diagram.buildLeafIdent(ent2String)) : diagram
-				.getGroup(diagram.buildCode(ent2String));
+		final IEntity cl1 = diagram.V1972() ? diagram.getGroupVerySmart(diagram.buildLeafIdent(ent1String))
+				: diagram.getGroup(diagram.buildCode(ent1String));
+		final IEntity cl2 = diagram.V1972() ? diagram.getGroupVerySmart(diagram.buildLeafIdent(ent2String))
+				: diagram.getGroup(diagram.buildCode(ent2String));
 
 		final LinkType linkType = getLinkType(arg);
 		final Direction dir = getDirection(arg);
diff --git a/src/net/sourceforge/plantuml/command/BlocLines.java b/src/net/sourceforge/plantuml/command/BlocLines.java
index c2d49759d..74c662dce 100644
--- a/src/net/sourceforge/plantuml/command/BlocLines.java
+++ b/src/net/sourceforge/plantuml/command/BlocLines.java
@@ -100,6 +100,14 @@ public class BlocLines implements Iterable<StringLocated> {
 		return new BlocLines(result);
 	}
 
+	public static BlocLines fromArray(String[] array) {
+		final List<StringLocated> result = new ArrayList<StringLocated>();
+		for (String single : array) {
+			result.add(new StringLocated(single, null));
+		}
+		return new BlocLines(result);
+	}
+
 	public static BlocLines getWithNewlines(String s) {
 		final List<StringLocated> result = new ArrayList<StringLocated>();
 		for (String cs : BackSlash.getWithNewlines(s)) {
diff --git a/src/net/sourceforge/plantuml/command/FactorySpriteCommand.java b/src/net/sourceforge/plantuml/command/CommandFactorySprite.java
similarity index 94%
rename from src/net/sourceforge/plantuml/command/FactorySpriteCommand.java
rename to src/net/sourceforge/plantuml/command/CommandFactorySprite.java
index 7c9bc6e5b..5033e9c03 100644
--- a/src/net/sourceforge/plantuml/command/FactorySpriteCommand.java
+++ b/src/net/sourceforge/plantuml/command/CommandFactorySprite.java
@@ -52,14 +52,14 @@ import net.sourceforge.plantuml.sprite.Sprite;
 import net.sourceforge.plantuml.sprite.SpriteColorBuilder4096;
 import net.sourceforge.plantuml.sprite.SpriteGrayLevel;
 
-public final class FactorySpriteCommand implements SingleMultiFactoryCommand<WithSprite> {
+public final class CommandFactorySprite implements SingleMultiFactoryCommand<WithSprite> {
 
 	private IRegex getRegexConcatMultiLine() {
-		return RegexConcat.build(FactorySpriteCommand.class.getName() + "multi", RegexLeaf.start(), //
+		return RegexConcat.build(CommandFactorySprite.class.getName() + "multi", RegexLeaf.start(), //
 				new RegexLeaf("sprite"), //
 				RegexLeaf.spaceOneOrMore(), //
 				new RegexLeaf("\\$?"), //
-				new RegexLeaf("NAME", "([\\p{L}0-9_]+)"), //
+				new RegexLeaf("NAME", "([-.\\p{L}0-9_]+)"), //
 				RegexLeaf.spaceZeroOrMore(), //
 				new RegexOptional(new RegexLeaf("DIM", "\\[(\\d+)x(\\d+)/(?:(\\d+)(z)?|(color))\\]")), //
 				RegexLeaf.spaceZeroOrMore(), //
@@ -67,11 +67,11 @@ public final class FactorySpriteCommand implements SingleMultiFactoryCommand<Wit
 	}
 
 	private IRegex getRegexConcatSingleLine() {
-		return RegexConcat.build(FactorySpriteCommand.class.getName() + "single", RegexLeaf.start(), //
+		return RegexConcat.build(CommandFactorySprite.class.getName() + "single", RegexLeaf.start(), //
 				new RegexLeaf("sprite"), //
 				RegexLeaf.spaceOneOrMore(), //
 				new RegexLeaf("\\$?"), //
-				new RegexLeaf("NAME", "([\\p{L}0-9_]+)"), //
+				new RegexLeaf("NAME", "([-.\\p{L}0-9_]+)"), //
 				RegexLeaf.spaceZeroOrMore(), //
 				new RegexOptional(new RegexLeaf("DIM", "\\[(\\d+)x(\\d+)/(?:(\\d+)(z)|(color))\\]")), //
 				RegexLeaf.spaceOneOrMore(), //
diff --git a/src/net/sourceforge/plantuml/command/UmlDiagramFactory.java b/src/net/sourceforge/plantuml/command/UmlDiagramFactory.java
index b032a8ef4..ca71732b6 100644
--- a/src/net/sourceforge/plantuml/command/UmlDiagramFactory.java
+++ b/src/net/sourceforge/plantuml/command/UmlDiagramFactory.java
@@ -241,7 +241,7 @@ public abstract class UmlDiagramFactory extends PSystemAbstractFactory {
 		cmds.add(new CommandScaleMaxWidthAndHeight());
 		cmds.add(new CommandAffineTransform());
 		cmds.add(new CommandAffineTransformMultiline());
-		final FactorySpriteCommand factorySpriteCommand = new FactorySpriteCommand();
+		final CommandFactorySprite factorySpriteCommand = new CommandFactorySprite();
 		cmds.add(factorySpriteCommand.createMultiLine(false));
 		cmds.add(factorySpriteCommand.createSingleLine());
 		cmds.add(new CommandSpriteFile());
diff --git a/src/net/sourceforge/plantuml/command/note/CommandConstraintOnLinks.java b/src/net/sourceforge/plantuml/command/note/CommandConstraintOnLinks.java
new file mode 100644
index 000000000..95dab07c9
--- /dev/null
+++ b/src/net/sourceforge/plantuml/command/note/CommandConstraintOnLinks.java
@@ -0,0 +1,101 @@
+/* ========================================================================
+ * PlantUML : a free UML diagram generator
+ * ========================================================================
+ *
+ * (C) Copyright 2009-2020, Arnaud Roques
+ *
+ * Project Info:  http://plantuml.com
+ * 
+ * If you like this project or if you find it useful, you can support us at:
+ * 
+ * http://plantuml.com/patreon (only 1$ per month!)
+ * http://plantuml.com/paypal
+ * 
+ * This file is part of PlantUML.
+ *
+ * PlantUML is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * PlantUML distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
+ * USA.
+ *
+ *
+ * Original Author:  Arnaud Roques
+ * 
+ *
+ */
+package net.sourceforge.plantuml.command.note;
+
+import java.util.List;
+
+import net.sourceforge.plantuml.LineLocation;
+import net.sourceforge.plantuml.StringUtils;
+import net.sourceforge.plantuml.Url;
+import net.sourceforge.plantuml.UrlBuilder;
+import net.sourceforge.plantuml.UrlBuilder.ModeUrl;
+import net.sourceforge.plantuml.classdiagram.AbstractEntityDiagram;
+import net.sourceforge.plantuml.command.BlocLines;
+import net.sourceforge.plantuml.command.Command;
+import net.sourceforge.plantuml.command.CommandExecutionResult;
+import net.sourceforge.plantuml.command.CommandMultilines2;
+import net.sourceforge.plantuml.command.CommandPackage;
+import net.sourceforge.plantuml.command.MultilinesStrategy;
+import net.sourceforge.plantuml.command.Position;
+import net.sourceforge.plantuml.command.SingleLineCommand2;
+import net.sourceforge.plantuml.command.regex.IRegex;
+import net.sourceforge.plantuml.command.regex.RegexConcat;
+import net.sourceforge.plantuml.command.regex.RegexLeaf;
+import net.sourceforge.plantuml.command.regex.RegexOptional;
+import net.sourceforge.plantuml.command.regex.RegexResult;
+import net.sourceforge.plantuml.cucadiagram.CucaDiagram;
+import net.sourceforge.plantuml.cucadiagram.Link;
+import net.sourceforge.plantuml.cucadiagram.Stereotag;
+import net.sourceforge.plantuml.graphic.color.ColorParser;
+import net.sourceforge.plantuml.graphic.color.ColorType;
+import net.sourceforge.plantuml.graphic.color.Colors;
+
+public final class CommandConstraintOnLinks extends SingleLineCommand2<CucaDiagram> {
+
+	public CommandConstraintOnLinks() {
+		super(getRegexConcat());
+	}
+
+	private static IRegex getRegexConcat() {
+		return RegexConcat.build(CommandConstraintOnLinks.class.getName(), RegexLeaf.start(), //
+				new RegexLeaf("constraint"), //
+				RegexLeaf.spaceZeroOrMore(), //
+				new RegexLeaf("on"), //
+				RegexLeaf.spaceOneOrMore(), //
+				new RegexLeaf("links"), //
+				RegexLeaf.spaceZeroOrMore(), //
+				color().getRegex(), //
+				RegexLeaf.spaceZeroOrMore(), //
+				new RegexLeaf(":"), //
+				RegexLeaf.spaceZeroOrMore(), //
+				new RegexLeaf("NOTE", "(.*)"), RegexLeaf.end());
+	}
+
+	private static ColorParser color() {
+		return ColorParser.simpleColor(ColorType.BACK);
+	}
+
+	@Override
+	protected CommandExecutionResult executeArg(CucaDiagram diagram, LineLocation location, RegexResult arg) {
+		final List<Link> links = diagram.getTwoLastLinks();
+		if (links == null) {
+			return CommandExecutionResult.error("Cannot put constraint on two last links");
+		}
+		final BlocLines note = BlocLines.getWithNewlines(arg.get("NOTE", 0));
+		return diagram.constraintOnLinks(links.get(0), links.get(1), note.toDisplay());
+	}
+
+}
diff --git a/src/net/sourceforge/plantuml/command/note/FactoryNoteCommand.java b/src/net/sourceforge/plantuml/command/note/CommandFactoryNote.java
similarity index 96%
rename from src/net/sourceforge/plantuml/command/note/FactoryNoteCommand.java
rename to src/net/sourceforge/plantuml/command/note/CommandFactoryNote.java
index 1791f0f74..791a0fe5e 100644
--- a/src/net/sourceforge/plantuml/command/note/FactoryNoteCommand.java
+++ b/src/net/sourceforge/plantuml/command/note/CommandFactoryNote.java
@@ -57,10 +57,10 @@ import net.sourceforge.plantuml.cucadiagram.Stereotag;
 import net.sourceforge.plantuml.graphic.color.ColorParser;
 import net.sourceforge.plantuml.graphic.color.ColorType;
 
-public final class FactoryNoteCommand implements SingleMultiFactoryCommand<AbstractEntityDiagram> {
+public final class CommandFactoryNote implements SingleMultiFactoryCommand<AbstractEntityDiagram> {
 
 	private IRegex getRegexConcatMultiLine() {
-		return RegexConcat.build(FactoryNoteCommand.class.getName() + "multi", RegexLeaf.start(), //
+		return RegexConcat.build(CommandFactoryNote.class.getName() + "multi", RegexLeaf.start(), //
 				new RegexLeaf("note"), //
 				RegexLeaf.spaceOneOrMore(), //
 				new RegexLeaf("as"), //
@@ -75,7 +75,7 @@ public final class FactoryNoteCommand implements SingleMultiFactoryCommand<Abstr
 	}
 
 	private IRegex getRegexConcatSingleLine() {
-		return RegexConcat.build(FactoryNoteCommand.class.getName() + "single", RegexLeaf.start(), //
+		return RegexConcat.build(CommandFactoryNote.class.getName() + "single", RegexLeaf.start(), //
 				new RegexLeaf("note"), //
 				RegexLeaf.spaceOneOrMore(), //
 				new RegexLeaf("[%g]"), //
diff --git a/src/net/sourceforge/plantuml/command/note/FactoryNoteActivityCommand.java b/src/net/sourceforge/plantuml/command/note/CommandFactoryNoteActivity.java
similarity index 97%
rename from src/net/sourceforge/plantuml/command/note/FactoryNoteActivityCommand.java
rename to src/net/sourceforge/plantuml/command/note/CommandFactoryNoteActivity.java
index ac2e109b9..fb6176833 100644
--- a/src/net/sourceforge/plantuml/command/note/FactoryNoteActivityCommand.java
+++ b/src/net/sourceforge/plantuml/command/note/CommandFactoryNoteActivity.java
@@ -65,10 +65,10 @@ import net.sourceforge.plantuml.graphic.color.ColorParser;
 import net.sourceforge.plantuml.graphic.color.ColorType;
 import net.sourceforge.plantuml.utils.UniqueSequence;
 
-public final class FactoryNoteActivityCommand implements SingleMultiFactoryCommand<ActivityDiagram> {
+public final class CommandFactoryNoteActivity implements SingleMultiFactoryCommand<ActivityDiagram> {
 
 	private IRegex getRegexConcatMultiLine() {
-		return RegexConcat.build(FactoryNoteActivityCommand.class.getName() + "multi", RegexLeaf.start(), //
+		return RegexConcat.build(CommandFactoryNoteActivity.class.getName() + "multi", RegexLeaf.start(), //
 				new RegexLeaf("note"), //
 				RegexLeaf.spaceOneOrMore(), //
 				new RegexLeaf("POSITION", "(right|left|top|bottom)"), //
@@ -78,7 +78,7 @@ public final class FactoryNoteActivityCommand implements SingleMultiFactoryComma
 	}
 
 	private IRegex getRegexConcatSingleLine() {
-		return RegexConcat.build(FactoryNoteActivityCommand.class.getName() + "single", RegexLeaf.start(), //
+		return RegexConcat.build(CommandFactoryNoteActivity.class.getName() + "single", RegexLeaf.start(), //
 				new RegexLeaf("note"), //
 				RegexLeaf.spaceOneOrMore(), //
 				new RegexLeaf("POSITION", "(right|left|top|bottom)"), //
diff --git a/src/net/sourceforge/plantuml/command/note/FactoryNoteOnEntityCommand.java b/src/net/sourceforge/plantuml/command/note/CommandFactoryNoteOnEntity.java
similarity index 96%
rename from src/net/sourceforge/plantuml/command/note/FactoryNoteOnEntityCommand.java
rename to src/net/sourceforge/plantuml/command/note/CommandFactoryNoteOnEntity.java
index 12f54bcb8..078b2d202 100644
--- a/src/net/sourceforge/plantuml/command/note/FactoryNoteOnEntityCommand.java
+++ b/src/net/sourceforge/plantuml/command/note/CommandFactoryNoteOnEntity.java
@@ -69,18 +69,18 @@ import net.sourceforge.plantuml.graphic.color.ColorType;
 import net.sourceforge.plantuml.graphic.color.Colors;
 import net.sourceforge.plantuml.utils.UniqueSequence;
 
-public final class FactoryNoteOnEntityCommand implements SingleMultiFactoryCommand<AbstractEntityDiagram> {
+public final class CommandFactoryNoteOnEntity implements SingleMultiFactoryCommand<AbstractEntityDiagram> {
 
 	private final IRegex partialPattern;
 	private final String key;
 
-	public FactoryNoteOnEntityCommand(String key, IRegex partialPattern) {
+	public CommandFactoryNoteOnEntity(String key, IRegex partialPattern) {
 		this.partialPattern = partialPattern;
 		this.key = key;
 	}
 
 	private IRegex getRegexConcatSingleLine(IRegex partialPattern) {
-		return RegexConcat.build(FactoryNoteOnEntityCommand.class.getName() + key + "single", RegexLeaf.start(), //
+		return RegexConcat.build(CommandFactoryNoteOnEntity.class.getName() + key + "single", RegexLeaf.start(), //
 				new RegexLeaf("note"), //
 				RegexLeaf.spaceOneOrMore(), //
 				new RegexLeaf("POSITION", "(right|left|top|bottom)"), //
@@ -109,7 +109,7 @@ public final class FactoryNoteOnEntityCommand implements SingleMultiFactoryComma
 
 	private IRegex getRegexConcatMultiLine(IRegex partialPattern, final boolean withBracket) {
 		if (withBracket) {
-			return RegexConcat.build(FactoryNoteOnEntityCommand.class.getName() + key + "multi" + withBracket,
+			return RegexConcat.build(CommandFactoryNoteOnEntity.class.getName() + key + "multi" + withBracket,
 					RegexLeaf.start(), //
 					new RegexLeaf("note"), //
 					RegexLeaf.spaceOneOrMore(), //
@@ -131,7 +131,7 @@ public final class FactoryNoteOnEntityCommand implements SingleMultiFactoryComma
 					RegexLeaf.end() //
 					);
 		}
-		return RegexConcat.build(FactoryNoteOnEntityCommand.class.getName() + key + "multi" + withBracket,
+		return RegexConcat.build(CommandFactoryNoteOnEntity.class.getName() + key + "multi" + withBracket,
 				RegexLeaf.start(), //
 				new RegexLeaf("note"), //
 				RegexLeaf.spaceOneOrMore(), //
diff --git a/src/net/sourceforge/plantuml/command/note/FactoryNoteOnLinkCommand.java b/src/net/sourceforge/plantuml/command/note/CommandFactoryNoteOnLink.java
similarity index 96%
rename from src/net/sourceforge/plantuml/command/note/FactoryNoteOnLinkCommand.java
rename to src/net/sourceforge/plantuml/command/note/CommandFactoryNoteOnLink.java
index 4023ecfe5..43e1462bf 100644
--- a/src/net/sourceforge/plantuml/command/note/FactoryNoteOnLinkCommand.java
+++ b/src/net/sourceforge/plantuml/command/note/CommandFactoryNoteOnLink.java
@@ -57,10 +57,10 @@ import net.sourceforge.plantuml.graphic.color.ColorParser;
 import net.sourceforge.plantuml.graphic.color.ColorType;
 import net.sourceforge.plantuml.graphic.color.Colors;
 
-public final class FactoryNoteOnLinkCommand implements SingleMultiFactoryCommand<CucaDiagram> {
+public final class CommandFactoryNoteOnLink implements SingleMultiFactoryCommand<CucaDiagram> {
 
 	private IRegex getRegexConcatSingleLine() {
-		return RegexConcat.build(FactoryNoteOnLinkCommand.class.getName() + "single", RegexLeaf.start(), //
+		return RegexConcat.build(CommandFactoryNoteOnLink.class.getName() + "single", RegexLeaf.start(), //
 				new RegexLeaf("note"), //
 				RegexLeaf.spaceOneOrMore(), //
 				new RegexLeaf("POSITION", "(right|left|top|bottom)?"), //
@@ -77,7 +77,7 @@ public final class FactoryNoteOnLinkCommand implements SingleMultiFactoryCommand
 	}
 
 	private IRegex getRegexConcatMultiLine() {
-		return RegexConcat.build(FactoryNoteOnLinkCommand.class.getName() + "multi", RegexLeaf.start(), //
+		return RegexConcat.build(CommandFactoryNoteOnLink.class.getName() + "multi", RegexLeaf.start(), //
 				new RegexLeaf("note"), //
 				RegexLeaf.spaceOneOrMore(), //
 				new RegexLeaf("POSITION", "(right|left|top|bottom)?"), //
diff --git a/src/net/sourceforge/plantuml/command/note/FactoryTipOnEntityCommand.java b/src/net/sourceforge/plantuml/command/note/CommandFactoryTipOnEntity.java
similarity index 96%
rename from src/net/sourceforge/plantuml/command/note/FactoryTipOnEntityCommand.java
rename to src/net/sourceforge/plantuml/command/note/CommandFactoryTipOnEntity.java
index 6cbd56e22..cea206040 100644
--- a/src/net/sourceforge/plantuml/command/note/FactoryTipOnEntityCommand.java
+++ b/src/net/sourceforge/plantuml/command/note/CommandFactoryTipOnEntity.java
@@ -61,19 +61,19 @@ import net.sourceforge.plantuml.cucadiagram.LinkDecor;
 import net.sourceforge.plantuml.cucadiagram.LinkType;
 import net.sourceforge.plantuml.graphic.color.ColorParser;
 
-public final class FactoryTipOnEntityCommand implements SingleMultiFactoryCommand<AbstractEntityDiagram> {
+public final class CommandFactoryTipOnEntity implements SingleMultiFactoryCommand<AbstractEntityDiagram> {
 
 	private final IRegex partialPattern;
 	private final String key;
 
-	public FactoryTipOnEntityCommand(String key, IRegex partialPattern) {
+	public CommandFactoryTipOnEntity(String key, IRegex partialPattern) {
 		this.partialPattern = partialPattern;
 		this.key = key;
 	}
 
 	private RegexConcat getRegexConcatMultiLine(IRegex partialPattern, final boolean withBracket) {
 		if (withBracket) {
-			return RegexConcat.build(FactoryTipOnEntityCommand.class.getName() + key + withBracket, RegexLeaf.start(), //
+			return RegexConcat.build(CommandFactoryTipOnEntity.class.getName() + key + withBracket, RegexLeaf.start(), //
 					new RegexLeaf("note"), //
 					RegexLeaf.spaceOneOrMore(), //
 					new RegexLeaf("POSITION", "(right|left)"), //
@@ -90,7 +90,7 @@ public final class FactoryTipOnEntityCommand implements SingleMultiFactoryComman
 					RegexLeaf.end() //
 					);
 		}
-		return RegexConcat.build(FactoryTipOnEntityCommand.class.getName() + key + withBracket, RegexLeaf.start(), //
+		return RegexConcat.build(CommandFactoryTipOnEntity.class.getName() + key + withBracket, RegexLeaf.start(), //
 				new RegexLeaf("note"), //
 				RegexLeaf.spaceOneOrMore(), //
 				new RegexLeaf("POSITION", "(right|left)"), //
diff --git a/src/net/sourceforge/plantuml/core/DiagramType.java b/src/net/sourceforge/plantuml/core/DiagramType.java
index db4317d06..24bfeddbe 100644
--- a/src/net/sourceforge/plantuml/core/DiagramType.java
+++ b/src/net/sourceforge/plantuml/core/DiagramType.java
@@ -38,13 +38,16 @@ package net.sourceforge.plantuml.core;
 import net.sourceforge.plantuml.utils.StartUtils;
 
 public enum DiagramType {
-	UML, BPM, DITAA, DOT, PROJECT, JCCKIT, SALT, FLOW, CREOLE, JUNGLE, CUTE, MATH, LATEX, DEFINITION, GANTT, NW, MINDMAP, WBS, UNKNOWN;
+	UML, BPM, DITAA, DOT, PROJECT, JCCKIT, SALT, FLOW, CREOLE, JUNGLE, CUTE, MATH, LATEX, DEFINITION, GANTT, NW, MINDMAP, WBS, WIRE, UNKNOWN;
 
 	static public DiagramType getTypeFromArobaseStart(String s) {
 		s = s.toLowerCase();
 		// if (s.startsWith("@startuml2")) {
 		// return UML2;
 		// }
+		if (StartUtils.startsWithSymbolAnd("startwire", s)) {
+			return WIRE;
+		}
 		if (StartUtils.startsWithSymbolAnd("startbpm", s)) {
 			return BPM;
 		}
diff --git a/src/net/sourceforge/plantuml/creole/AtomImg.java b/src/net/sourceforge/plantuml/creole/AtomImg.java
index 29a164dec..7f23d9df3 100644
--- a/src/net/sourceforge/plantuml/creole/AtomImg.java
+++ b/src/net/sourceforge/plantuml/creole/AtomImg.java
@@ -71,11 +71,13 @@ public class AtomImg extends AbstractAtom implements Atom {
 	private final BufferedImage image;
 	private final double scale;
 	private final Url url;
-	
-	private AtomImg(BufferedImage image, double scale, Url url) {
+	private final String rawFileName;
+
+	private AtomImg(BufferedImage image, double scale, Url url, String rawFileName) {
 		this.image = image;
 		this.scale = scale;
 		this.url = url;
+		this.rawFileName = rawFileName;
 	}
 
 	public static Atom createQrcode(String flash, double scale) {
@@ -84,7 +86,7 @@ public class AtomImg extends AbstractAtom implements Atom {
 		if (im == null) {
 			im = new BufferedImage(10, 10, BufferedImage.TYPE_INT_RGB);
 		}
-		return new AtomImg(new UImage(im).scaleNearestNeighbor(scale).getImage(), 1, null);
+		return new AtomImg(new UImage(null, im).scaleNearestNeighbor(scale).getImage(), 1, null, null);
 	}
 
 	public static Atom create(String src, ImgValign valign, int vspace, double scale, Url url) {
@@ -118,7 +120,7 @@ public class AtomImg extends AbstractAtom implements Atom {
 			if (read == null) {
 				return AtomText.create("(Cannot decode: " + f.getCanonicalPath() + ")", fc);
 			}
-			return new AtomImg(FileUtils.ImageIO_read(f), scale, url);
+			return new AtomImg(FileUtils.ImageIO_read(f), scale, url, src);
 		} catch (IOException e) {
 			return AtomText.create("ERROR " + e.toString(), fc);
 		}
@@ -130,7 +132,7 @@ public class AtomImg extends AbstractAtom implements Atom {
 		if (read == null) {
 			return AtomText.create("(Cannot decode: " + source + ")", fc);
 		}
-		return new AtomImg(read, scale, url);
+		return new AtomImg(read, scale, url, null);
 	}
 
 	private static Atom build(String text, final FontConfiguration fc, URL source, double scale, Url url)
@@ -139,7 +141,7 @@ public class AtomImg extends AbstractAtom implements Atom {
 		if (read == null) {
 			return AtomText.create("(Cannot decode: " + text + ")", fc);
 		}
-		return new AtomImg(read, scale, url);
+		return new AtomImg(read, scale, url, source.getPath());
 	}
 
 	// Added by Alain Corbiere
@@ -178,7 +180,7 @@ public class AtomImg extends AbstractAtom implements Atom {
 		if (url != null) {
 			ug.startUrl(url);
 		}
-		ug.draw(new UImage(image).scale(scale));
+		ug.draw(new UImage(rawFileName, image).scale(scale));
 		if (url != null) {
 			ug.closeAction();
 		}
diff --git a/src/net/sourceforge/plantuml/creole/AtomMath.java b/src/net/sourceforge/plantuml/creole/AtomMath.java
index f386ef003..4815d8735 100644
--- a/src/net/sourceforge/plantuml/creole/AtomMath.java
+++ b/src/net/sourceforge/plantuml/creole/AtomMath.java
@@ -99,7 +99,7 @@ public class AtomMath extends AbstractAtom implements Atom {
 			final SvgString svg = math.getSvg(scale, fore, back);
 			ug.draw(new UImageSvg(svg));
 		} else {
-			final UImage image = new UImage(math.getImage(scale, fore, back), math.getFormula());
+			final UImage image = new UImage(null, math.getImage(scale, fore, back), math.getFormula());
 			ug.draw(image);
 		}
 	}
diff --git a/src/net/sourceforge/plantuml/creole/AtomText.java b/src/net/sourceforge/plantuml/creole/AtomText.java
index e9c346d7c..2d1b61d4b 100644
--- a/src/net/sourceforge/plantuml/creole/AtomText.java
+++ b/src/net/sourceforge/plantuml/creole/AtomText.java
@@ -90,13 +90,12 @@ public class AtomText extends AbstractAtom implements Atom {
 	public static Atom create(String text, FontConfiguration fontConfiguration) {
 		return new AtomText(text, fontConfiguration, null, ZERO, ZERO);
 	}
-	
+
 //	public static AtomText createHeading(String text, FontConfiguration fontConfiguration, int order) {
 //		fontConfiguration = FOO(fontConfiguration, order);
 //		return new AtomText(text, fontConfiguration, null, ZERO, ZERO);
 //	}
 
-
 	public static Atom createUrl(Url url, FontConfiguration fontConfiguration, ISkinSimple skinSimple) {
 		fontConfiguration = fontConfiguration.hyperlink();
 		final Display display = Display.getWithNewlines(url.getLabel());
@@ -113,8 +112,8 @@ public class AtomText extends AbstractAtom implements Atom {
 
 	private static Atom createAtomText(final String text, Url url, FontConfiguration fontConfiguration,
 			ISkinSimple skinSimple) {
-		final Pattern p = Pattern.compile(Splitter.openiconPattern + "|" + Splitter.spritePattern2 + "|"
-				+ Splitter.imgPatternNoSrcColon);
+		final Pattern p = Pattern.compile(
+				Splitter.openiconPattern + "|" + Splitter.spritePattern2 + "|" + Splitter.imgPatternNoSrcColon);
 		final Matcher m = p.matcher(text);
 		final List<Atom> result = new ArrayList<Atom>();
 		while (m.find()) {
@@ -176,14 +175,15 @@ public class AtomText extends AbstractAtom implements Atom {
 		return text + " " + fontConfiguration;
 	}
 
-	private AtomText(String text, FontConfiguration style, Url url, DelayedDouble marginLeft, DelayedDouble marginRight) {
+	private AtomText(String text, FontConfiguration style, Url url, DelayedDouble marginLeft,
+			DelayedDouble marginRight) {
 		if (text.contains("" + BackSlash.hiddenNewLine())) {
 			throw new IllegalArgumentException(text);
 		}
 		this.marginLeft = marginLeft;
 		this.marginRight = marginRight;
-		this.text = StringUtils.manageTildeArobaseStart(StringUtils.manageUnicodeNotationUplus(StringUtils
-				.manageAmpDiese(StringUtils.showComparatorCharacters(CharHidder.unhide(text)))));
+		this.text = StringUtils.manageTildeArobaseStart(StringUtils.manageUnicodeNotationUplus(
+				StringUtils.manageAmpDiese(StringUtils.showComparatorCharacters(CharHidder.unhide(text)))));
 		this.fontConfiguration = style;
 		this.url = url;
 	}
@@ -234,42 +234,43 @@ public class AtomText extends AbstractAtom implements Atom {
 	}
 
 	public void drawU(UGraphic ug) {
-		if (ug.matchesProperty("SPECIALTXT")) {
-			ug.draw(this);
-			return;
-		}
 		if (url != null) {
 			ug.startUrl(url);
 		}
-		HtmlColor textColor = fontConfiguration.getColor();
-		FontConfiguration useFontConfiguration = fontConfiguration;
-		if (textColor instanceof HtmlColorAutomatic && ug.getParam().getBackcolor() != null) {
-			textColor = ((HtmlColorSimple) ug.getParam().getBackcolor()).opposite();
-			useFontConfiguration = fontConfiguration.changeColor(textColor);
-		}
-		if (marginLeft != ZERO) {
-			ug = ug.apply(new UTranslate(marginLeft.getDouble(ug.getStringBounder()), 0));
-		}
+		if (ug.matchesProperty("SPECIALTXT")) {
+			ug.draw(this);
+		} else {
+			HtmlColor textColor = fontConfiguration.getColor();
+			FontConfiguration useFontConfiguration = fontConfiguration;
+			if (textColor instanceof HtmlColorAutomatic && ug.getParam().getBackcolor() != null) {
+				textColor = ((HtmlColorSimple) ug.getParam().getBackcolor()).opposite();
+				useFontConfiguration = fontConfiguration.changeColor(textColor);
+			}
+			if (marginLeft != ZERO) {
+				ug = ug.apply(new UTranslate(marginLeft.getDouble(ug.getStringBounder()), 0));
+			}
 
-		final StringTokenizer tokenizer = new StringTokenizer(text, "\t", true);
+			final StringTokenizer tokenizer = new StringTokenizer(text, "\t", true);
 
-		double x = 0;
-		// final int ypos = fontConfiguration.getSpace();
-		final Dimension2D rect = ug.getStringBounder().calculateDimension(fontConfiguration.getFont(), text);
-		final double descent = getDescent();
-		final double ypos = rect.getHeight() - descent;
-		if (tokenizer.hasMoreTokens()) {
-			final double tabSize = getTabSize(ug.getStringBounder());
-			while (tokenizer.hasMoreTokens()) {
-				final String s = tokenizer.nextToken();
-				if (s.equals("\t")) {
-					final double remainder = x % tabSize;
-					x += tabSize - remainder;
-				} else {
-					final UText utext = new UText(s, useFontConfiguration);
-					final Dimension2D dim = ug.getStringBounder().calculateDimension(fontConfiguration.getFont(), s);
-					ug.apply(new UTranslate(x, ypos)).draw(utext);
-					x += dim.getWidth();
+			double x = 0;
+			// final int ypos = fontConfiguration.getSpace();
+			final Dimension2D rect = ug.getStringBounder().calculateDimension(fontConfiguration.getFont(), text);
+			final double descent = getDescent();
+			final double ypos = rect.getHeight() - descent;
+			if (tokenizer.hasMoreTokens()) {
+				final double tabSize = getTabSize(ug.getStringBounder());
+				while (tokenizer.hasMoreTokens()) {
+					final String s = tokenizer.nextToken();
+					if (s.equals("\t")) {
+						final double remainder = x % tabSize;
+						x += tabSize - remainder;
+					} else {
+						final UText utext = new UText(s, useFontConfiguration);
+						final Dimension2D dim = ug.getStringBounder().calculateDimension(fontConfiguration.getFont(),
+								s);
+						ug.apply(new UTranslate(x, ypos)).draw(utext);
+						x += dim.getWidth();
+					}
 				}
 			}
 		}
diff --git a/src/net/sourceforge/plantuml/cucadiagram/Bodier.java b/src/net/sourceforge/plantuml/cucadiagram/Bodier.java
index 911299aa0..c2e881429 100644
--- a/src/net/sourceforge/plantuml/cucadiagram/Bodier.java
+++ b/src/net/sourceforge/plantuml/cucadiagram/Bodier.java
@@ -107,7 +107,7 @@ public class Bodier {
 	private boolean isMethod(String s) {
 		if (type == LeafType.ANNOTATION || type == LeafType.ABSTRACT_CLASS || type == LeafType.CLASS
 				|| type == LeafType.INTERFACE || type == LeafType.ENUM) {
-			return MemberImpl.isMethod(s);
+			return Member.isMethod(s);
 		}
 		return false;
 	}
@@ -123,7 +123,7 @@ public class Bodier {
 				if (s.length() == 0 && methodsToDisplay.size() == 0) {
 					continue;
 				}
-				final Member m = new MemberImpl(s, true, manageModifier);
+				final Member m = new Member(s, true, manageModifier);
 				if (hides == null || hides.contains(m.getVisibilityModifier()) == false) {
 					methodsToDisplay.add(m);
 				}
@@ -151,7 +151,7 @@ public class Bodier {
 				if (s.length() == 0 && fieldsToDisplay.size() == 0) {
 					continue;
 				}
-				final Member m = new MemberImpl(s, false, manageModifier);
+				final Member m = new Member(s, false, manageModifier);
 				if (hides == null || hides.contains(m.getVisibilityModifier()) == false) {
 					fieldsToDisplay.add(m);
 				}
@@ -187,7 +187,7 @@ public class Bodier {
 		}
 		final List<String> result = new ArrayList<String>();
 		for (String s : rawBody) {
-			final Member m = new MemberImpl(s, isMethod(s), manageModifier);
+			final Member m = new Member(s, isMethod(s), manageModifier);
 			if (hides.contains(m.getVisibilityModifier()) == false) {
 				result.add(s);
 			}
diff --git a/src/net/sourceforge/plantuml/cucadiagram/BodyEnhanced.java b/src/net/sourceforge/plantuml/cucadiagram/BodyEnhanced.java
index 2bdf76bfd..a1fef7abd 100644
--- a/src/net/sourceforge/plantuml/cucadiagram/BodyEnhanced.java
+++ b/src/net/sourceforge/plantuml/cucadiagram/BodyEnhanced.java
@@ -170,7 +170,7 @@ public class BodyEnhanced extends AbstractTextBlock implements TextBlock, WithPo
 							align, skinParam, CreoleMode.FULL);
 					blocks.add(bloc);
 				} else {
-					final Member m = new MemberImpl(s, MemberImpl.isMethod(s), manageModifier);
+					final Member m = new Member(s, Member.isMethod(s), manageModifier);
 					members.add(m);
 					if (m.getUrl() != null) {
 						urls.add(m.getUrl());
@@ -179,7 +179,7 @@ public class BodyEnhanced extends AbstractTextBlock implements TextBlock, WithPo
 			}
 		}
 		if (inEllipse && members.size() == 0) {
-			members.add(new MemberImpl("", false, false));
+			members.add(new Member("", false, false));
 		}
 		blocks.add(decorate(stringBounder, new MethodsOrFieldsArea(members, fontParam, skinParam, align, stereotype,
 				entity), separator, title));
diff --git a/src/net/sourceforge/plantuml/cucadiagram/CucaDiagram.java b/src/net/sourceforge/plantuml/cucadiagram/CucaDiagram.java
index 0339e91b8..754c848a9 100644
--- a/src/net/sourceforge/plantuml/cucadiagram/CucaDiagram.java
+++ b/src/net/sourceforge/plantuml/cucadiagram/CucaDiagram.java
@@ -53,6 +53,7 @@ import net.sourceforge.plantuml.Log;
 import net.sourceforge.plantuml.UmlDiagram;
 import net.sourceforge.plantuml.UmlDiagramType;
 import net.sourceforge.plantuml.api.ImageDataSimple;
+import net.sourceforge.plantuml.command.CommandExecutionResult;
 import net.sourceforge.plantuml.core.ImageData;
 import net.sourceforge.plantuml.creole.CreoleMode;
 import net.sourceforge.plantuml.cucadiagram.dot.CucaDiagramTxtMaker;
@@ -69,10 +70,36 @@ import net.sourceforge.plantuml.xmlsc.StateDiagramScxmlMaker;
 
 public abstract class CucaDiagram extends UmlDiagram implements GroupHierarchy, PortionShower {
 
+	static private final boolean G1972 = false;
+
+	// private String namespaceSeparator = ".";
+	// private String namespaceSeparator1 = GO1972 ? "::" : ".";
+	private String namespaceSeparator = null;
+	private boolean namespaceSeparatorHasBeenSet = false;
+
 	public final boolean V1972() {
+		if (getPragma().backToLegacyPackage()) {
+			return false;
+		}
+		if (getPragma().useNewPackage()) {
+			return true;
+		}
+		if (G1972)
+			return true;
 		return false;
 	}
 
+	public final boolean mergeIntricated() {
+		if (getNamespaceSeparator() == null) {
+			return false;
+		}
+		return this.V1972() && this.getUmlDiagramType() == UmlDiagramType.CLASS;
+	}
+
+	public Set<SuperGroup> getAllSuperGroups() {
+		return entityFactory.getAllSuperGroups();
+	}
+
 	private int horizontalPages = 1;
 	private int verticalPages = 1;
 
@@ -105,13 +132,15 @@ public abstract class CucaDiagram extends UmlDiagram implements GroupHierarchy,
 		return this.stacks2.get(stacks2.size() - 1);
 	}
 
-	private String namespaceSeparator = ".";
-
 	final public void setNamespaceSeparator(String namespaceSeparator) {
+		this.namespaceSeparatorHasBeenSet = true;
 		this.namespaceSeparator = namespaceSeparator;
 	}
 
 	final public String getNamespaceSeparator() {
+		if (namespaceSeparatorHasBeenSet == false) {
+			return V1972() ? "::" : ".";
+		}
 		return namespaceSeparator;
 	}
 
@@ -135,6 +164,10 @@ public abstract class CucaDiagram extends UmlDiagram implements GroupHierarchy,
 		return false;
 	}
 
+	final public void setLastEntity(ILeaf foo) {
+		this.lastEntity = foo;
+	}
+
 	final protected ILeaf getOrCreateLeafDefault(Ident idNewLong, Code code, LeafType type, USymbol symbol) {
 		checkNotNull(idNewLong);
 		if (type == null) {
@@ -182,7 +215,7 @@ public abstract class CucaDiagram extends UmlDiagram implements GroupHierarchy,
 	}
 
 	final public Ident buildLeafIdent(String id) {
-		return getLastID().add(id, namespaceSeparator);
+		return getLastID().add(id, getNamespaceSeparator());
 	}
 
 	final public Ident buildLeafIdentSpecial(String id) {
@@ -196,7 +229,7 @@ public abstract class CucaDiagram extends UmlDiagram implements GroupHierarchy,
 	}
 
 	final public Ident buildFullyQualified(String id) {
-		return entityFactory.buildFullyQualified(getLastID(), Ident.empty().add(id, namespaceSeparator));
+		return entityFactory.buildFullyQualified(getLastID(), Ident.empty().add(id, getNamespaceSeparator()));
 	}
 
 	final public Code buildCode(String s) {
@@ -260,7 +293,7 @@ public abstract class CucaDiagram extends UmlDiagram implements GroupHierarchy,
 			}
 			gotoGroupInternalWithNamespace(ident, code, display, code, type, parent);
 		} else if (strategy == NamespaceStrategy.SINGLE) {
-			final Ident newIdLong = buildLeafIdentSpecial(ident.toString(this.namespaceSeparator));
+			final Ident newIdLong = buildLeafIdentSpecial(ident.toString(this.getNamespaceSeparator()));
 			gotoGroupExternal(newIdLong, code, display, null, type, parent);
 			stacks2.add(newIdLong);
 		} else {
@@ -363,7 +396,7 @@ public abstract class CucaDiagram extends UmlDiagram implements GroupHierarchy,
 			return;
 		}
 		final boolean mutation;
-		if (namespaceSeparator == null)
+		if (getNamespaceSeparator() == null)
 			mutation = entityFactory.getLeafVerySmart(idNewLong) != null;
 		else
 			mutation = entityFactory.getLeafStrict(idNewLong) != null;
@@ -491,6 +524,10 @@ public abstract class CucaDiagram extends UmlDiagram implements GroupHierarchy,
 		return entityFactory.getRootGroup();
 	}
 
+	public SuperGroup getRootSuperGroup() {
+		return entityFactory.getRootSuperGroup();
+	}
+
 	public final Collection<ILeaf> getLeafsvalues() {
 		return entityFactory.leafs2();
 	}
@@ -606,6 +643,8 @@ public abstract class CucaDiagram extends UmlDiagram implements GroupHierarchy,
 			throw new UnsupportedOperationException();
 		}
 
+		entityFactory.buildSuperGroups();
+
 		final CucaDiagramFileMaker maker = this.isUseJDot()
 				? new CucaDiagramFileMakerJDot(this, fileFormatOption.getDefaultStringBounder())
 				: new CucaDiagramFileMakerSvek(this);
@@ -807,6 +846,21 @@ public abstract class CucaDiagram extends UmlDiagram implements GroupHierarchy,
 		return null;
 	}
 
+	final public List<Link> getTwoLastLinks() {
+		final List<Link> result = new ArrayList<Link>();
+		final List<Link> links = getLinks();
+		for (int i = links.size() - 1; i >= 0; i--) {
+			final Link link = links.get(i);
+			if (link.getEntity1().getLeafType() != LeafType.NOTE && link.getEntity2().getLeafType() != LeafType.NOTE) {
+				result.add(link);
+				if (result.size() == 2) {
+					return Collections.unmodifiableList(result);
+				}
+			}
+		}
+		return null;
+	}
+
 	private ILeaf lastEntity = null;
 
 	final public ILeaf getLastEntity() {
@@ -854,4 +908,11 @@ public abstract class CucaDiagram extends UmlDiagram implements GroupHierarchy,
 		entityFactory.incRawLayout();
 	}
 
+	public CommandExecutionResult constraintOnLinks(Link link1, Link link2, Display display) {
+		final LinkConstraint linkConstraint = new LinkConstraint(link1, link2, display);
+		link1.setLinkConstraint(linkConstraint);
+		link2.setLinkConstraint(linkConstraint);
+		return CommandExecutionResult.ok();
+	}
+
 }
diff --git a/src/net/sourceforge/plantuml/cucadiagram/GroupHierarchy.java b/src/net/sourceforge/plantuml/cucadiagram/GroupHierarchy.java
index b951020b4..794b1c54d 100644
--- a/src/net/sourceforge/plantuml/cucadiagram/GroupHierarchy.java
+++ b/src/net/sourceforge/plantuml/cucadiagram/GroupHierarchy.java
@@ -36,10 +36,17 @@
 package net.sourceforge.plantuml.cucadiagram;
 
 import java.util.Collection;
+import java.util.Set;
 
 public interface GroupHierarchy {
 
+	public IGroup getRootGroup();
+
+	public SuperGroup getRootSuperGroup();
+
 	public Collection<IGroup> getChildrenGroups(IGroup parent);
-	
+
+	public Set<SuperGroup> getAllSuperGroups();
+
 	public boolean isEmpty(IGroup g);
 }
diff --git a/src/net/sourceforge/plantuml/cucadiagram/Ident.java b/src/net/sourceforge/plantuml/cucadiagram/Ident.java
index 253b1591e..077b023b4 100644
--- a/src/net/sourceforge/plantuml/cucadiagram/Ident.java
+++ b/src/net/sourceforge/plantuml/cucadiagram/Ident.java
@@ -281,4 +281,12 @@ public class Ident implements Code {
 		return parts.size();
 	}
 
+	public Ident getPrefix(int toIndex) {
+		return new Ident(this.parts.subList(0, toIndex));
+	}
+
+	public Ident getSuffix(int fromIndex) {
+		return new Ident(this.parts.subList(fromIndex, this.parts.size()));
+	}
+
 }
diff --git a/src/net/sourceforge/plantuml/cucadiagram/Link.java b/src/net/sourceforge/plantuml/cucadiagram/Link.java
index 90185ae76..ad74b7850 100644
--- a/src/net/sourceforge/plantuml/cucadiagram/Link.java
+++ b/src/net/sourceforge/plantuml/cucadiagram/Link.java
@@ -115,11 +115,10 @@ public class Link extends WithLinkType implements Hideable, Removeable {
 
 	public UComment commentForSvg() {
 		if (type.looksLikeRevertedForSvg()) {
-			return new UComment("reverse link " + getEntity1().getCodeGetName() + " to "
-					+ getEntity2().getCodeGetName());
+			return new UComment(
+					"reverse link " + getEntity1().getCodeGetName() + " to " + getEntity2().getCodeGetName());
 		}
-		return new UComment("link " + getEntity1().getCodeGetName() + " to "
-				+ getEntity2().getCodeGetName());
+		return new UComment("link " + getEntity1().getCodeGetName() + " to " + getEntity2().getCodeGetName());
 	}
 
 	public Link(IEntity cl1, IEntity cl2, LinkType type, Display label, int length, StyleBuilder styleBuilder) {
@@ -196,6 +195,7 @@ public class Link extends WithLinkType implements Hideable, Removeable {
 		result.port1 = this.port2;
 		result.port2 = this.port1;
 		result.url = this.url;
+		result.linkConstraint = this.linkConstraint;
 		return result;
 	}
 
@@ -253,11 +253,11 @@ public class Link extends WithLinkType implements Hideable, Removeable {
 	}
 
 	public EntityPort getEntityPort1(Bibliotekon bibliotekon) {
-		return new EntityPort(bibliotekon.getShapeUid((ILeaf) cl1), port1);
+		return new EntityPort(bibliotekon.getNodeUid((ILeaf) cl1), port1);
 	}
 
 	public EntityPort getEntityPort2(Bibliotekon bibliotekon) {
-		return new EntityPort(bibliotekon.getShapeUid((ILeaf) cl2), port2);
+		return new EntityPort(bibliotekon.getNodeUid((ILeaf) cl2), port2);
 	}
 
 	@Override
@@ -474,7 +474,8 @@ public class Link extends WithLinkType implements Hideable, Removeable {
 
 	public boolean hasEntryPoint() {
 		return (getEntity1().isGroup() == false && ((ILeaf) getEntity1()).getEntityPosition() != EntityPosition.NORMAL)
-				|| (getEntity2().isGroup() == false && ((ILeaf) getEntity2()).getEntityPosition() != EntityPosition.NORMAL);
+				|| (getEntity2().isGroup() == false
+						&& ((ILeaf) getEntity2()).getEntityPosition() != EntityPosition.NORMAL);
 	}
 
 	public boolean hasTwoEntryPointsSameContainer() {
@@ -570,4 +571,14 @@ public class Link extends WithLinkType implements Hideable, Removeable {
 		return umlType;
 	}
 
+	private LinkConstraint linkConstraint;
+
+	public void setLinkConstraint(LinkConstraint linkConstraint) {
+		this.linkConstraint = linkConstraint;
+	}
+
+	public final LinkConstraint getLinkConstraint() {
+		return linkConstraint;
+	}
+
 }
diff --git a/src/net/sourceforge/plantuml/cucadiagram/LinkConstraint.java b/src/net/sourceforge/plantuml/cucadiagram/LinkConstraint.java
new file mode 100644
index 000000000..fc5dba195
--- /dev/null
+++ b/src/net/sourceforge/plantuml/cucadiagram/LinkConstraint.java
@@ -0,0 +1,106 @@
+/* ========================================================================
+ * PlantUML : a free UML diagram generator
+ * ========================================================================
+ *
+ * (C) Copyright 2009-2020, Arnaud Roques
+ *
+ * Project Info:  http://plantuml.com
+ * 
+ * If you like this project or if you find it useful, you can support us at:
+ * 
+ * http://plantuml.com/patreon (only 1$ per month!)
+ * http://plantuml.com/paypal
+ * 
+ * This file is part of PlantUML.
+ *
+ * PlantUML is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * PlantUML distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
+ * USA.
+ *
+ *
+ * Original Author:  Arnaud Roques
+ * 
+ *
+ */
+package net.sourceforge.plantuml.cucadiagram;
+
+import java.awt.geom.Dimension2D;
+import java.awt.geom.Point2D;
+
+import net.sourceforge.plantuml.FontParam;
+import net.sourceforge.plantuml.ISkinParam;
+import net.sourceforge.plantuml.graphic.FontConfiguration;
+import net.sourceforge.plantuml.graphic.HorizontalAlignment;
+import net.sourceforge.plantuml.graphic.HtmlColorUtils;
+import net.sourceforge.plantuml.graphic.TextBlock;
+import net.sourceforge.plantuml.ugraphic.UChangeColor;
+import net.sourceforge.plantuml.ugraphic.UGraphic;
+import net.sourceforge.plantuml.ugraphic.ULine;
+import net.sourceforge.plantuml.ugraphic.UStroke;
+import net.sourceforge.plantuml.ugraphic.UTranslate;
+
+public class LinkConstraint {
+
+	private final Link link1;
+	private final Link link2;
+	private final Display display;
+
+	private double x1;
+	private double y1;
+	private double x2;
+	private double y2;
+
+	public LinkConstraint(Link link1, Link link2, Display display) {
+		this.link1 = link1;
+		this.link2 = link2;
+		this.display = display;
+	}
+
+	public void setPosition(Link link, Point2D pt) {
+		if (link == link1) {
+			x1 = pt.getX();
+			y1 = pt.getY();
+		} else if (link == link2) {
+			x2 = pt.getX();
+			y2 = pt.getY();
+		} else {
+			throw new IllegalArgumentException();
+		}
+	}
+
+	public void drawMe(UGraphic ug, ISkinParam skinParam) {
+		if (x1 == 0 && y1 == 0) {
+			return;
+		}
+		if (x2 == 0 && y2 == 0) {
+			return;
+		}
+		ug = ug.apply(new UChangeColor(HtmlColorUtils.BLACK));
+//		ug.apply(new UTranslate(x1, y1)).draw(new URectangle(10, 10));
+//		ug.apply(new UTranslate(x2, y2)).draw(new URectangle(10, 10));
+
+		final ULine line = new ULine(x2 - x1, y2 - y1);
+		ug = ug.apply(new UStroke(3, 3, 1));
+		ug.apply(new UTranslate(x1, y1)).draw(line);
+
+		final TextBlock label = display.create(new FontConfiguration(skinParam, FontParam.ARROW, null),
+				HorizontalAlignment.CENTER, skinParam);
+		final Dimension2D dimLabel = label.calculateDimension(ug.getStringBounder());
+		final double x = (x1 + x2) / 2 - dimLabel.getWidth() / 2;
+		final double y = (y1 + y2) / 2 - dimLabel.getHeight() / 2;
+		label.drawU(ug.apply(new UTranslate(x, y)));
+
+	}
+
+}
diff --git a/src/net/sourceforge/plantuml/cucadiagram/Member.java b/src/net/sourceforge/plantuml/cucadiagram/Member.java
index 91dbcf015..427502ad9 100644
--- a/src/net/sourceforge/plantuml/cucadiagram/Member.java
+++ b/src/net/sourceforge/plantuml/cucadiagram/Member.java
@@ -35,20 +35,171 @@
  */
 package net.sourceforge.plantuml.cucadiagram;
 
+import net.sourceforge.plantuml.Guillemet;
+import net.sourceforge.plantuml.StringUtils;
 import net.sourceforge.plantuml.Url;
+import net.sourceforge.plantuml.UrlBuilder;
+import net.sourceforge.plantuml.UrlBuilder.ModeUrl;
+import net.sourceforge.plantuml.command.regex.Matcher2;
+import net.sourceforge.plantuml.command.regex.MyPattern;
+import net.sourceforge.plantuml.command.regex.Pattern2;
 import net.sourceforge.plantuml.skin.VisibilityModifier;
 
-public interface Member {
+public class Member {
 
-	public Url getUrl();
+	private final String display;
+	private final boolean staticModifier;
+	private final boolean abstractModifier;
+	private final Url url;
+	private final boolean hasUrl;
 
-	public String getDisplay(boolean withVisibilityChar);
+	private final VisibilityModifier visibilityModifier;
 
-	public boolean hasUrl();
+	@Override
+	public String toString() {
+		return super.toString() + " " + display;
+	}
 
-	public VisibilityModifier getVisibilityModifier();
+	public Member(String tmpDisplay, boolean isMethod, boolean manageModifier) {
+		tmpDisplay = tmpDisplay.replaceAll("(?i)\\{(method|field)\\}\\s*", "");
+		if (manageModifier) {
+			final Pattern2 finalUrl = MyPattern.cmpile("^(.*?)(?:\\[(" + UrlBuilder.getRegexp() + ")\\])?$");
+			final Matcher2 matcher = finalUrl.matcher(tmpDisplay);
+			if (matcher.matches() == false) {
+				throw new IllegalStateException();
+			}
+			tmpDisplay = matcher.group(1);
+			final String urlString = matcher.group(2);
+			if (urlString == null) {
+				this.url = null;
+			} else {
+				this.url = new UrlBuilder(null, ModeUrl.STRICT).getUrl(urlString);
+			}
+		} else {
+			this.url = null;
+		}
+		this.hasUrl = this.url != null;
+		final String lower = StringUtils.goLowerCase(tmpDisplay);
 
-	public boolean isStatic();
+		if (manageModifier) {
+			this.staticModifier = lower.contains("{static}") || lower.contains("{classifier}");
+			this.abstractModifier = lower.contains("{abstract}");
+			String displayClean = tmpDisplay.replaceAll("(?i)\\{(static|classifier|abstract)\\}\\s*", "").trim();
+			if (displayClean.length() == 0) {
+				displayClean = " ";
+			}
 
-	public boolean isAbstract();
+			if (VisibilityModifier.isVisibilityCharacter(displayClean)) {
+				visibilityModifier = VisibilityModifier.getVisibilityModifier(displayClean, isMethod == false);
+				this.display = StringUtils.trin(Guillemet.GUILLEMET.manageGuillemet(displayClean.substring(1)));
+			} else {
+				this.display = Guillemet.GUILLEMET.manageGuillemet(displayClean);
+				visibilityModifier = null;
+			}
+		} else {
+			this.staticModifier = false;
+			this.visibilityModifier = null;
+			this.abstractModifier = false;
+			tmpDisplay = StringUtils.trin(tmpDisplay);
+			this.display = tmpDisplay.length() == 0 ? " " : Guillemet.GUILLEMET.manageGuillemet(StringUtils
+					.trin(tmpDisplay));
+		}
+	}
+
+	public String getDisplay(boolean withVisibilityChar) {
+		if (withVisibilityChar) {
+			return getDisplayWithVisibilityChar();
+		}
+		return getDisplayWithoutVisibilityChar();
+	}
+
+	private String getDisplayWithoutVisibilityChar() {
+		// assert display.length() == 0 || VisibilityModifier.isVisibilityCharacter(display.charAt(0)) == false;
+		return display;
+	}
+
+	private String getDisplayWithVisibilityChar() {
+		if (isPrivate()) {
+			return "-" + display;
+		}
+		if (isPublic()) {
+			return "+" + display;
+		}
+		if (isPackagePrivate()) {
+			return "~" + display;
+		}
+		if (isProtected()) {
+			return "#" + display;
+		}
+		if (isIEMandatory()) {
+			return "*" + display;
+		}
+		return display;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		final Member other = (Member) obj;
+		return this.display.equals(other.display);
+	}
+
+	@Override
+	public int hashCode() {
+		return display.hashCode();
+	}
+
+	public final boolean isStatic() {
+		return staticModifier;
+	}
+
+	public final boolean isAbstract() {
+		return abstractModifier;
+	}
+
+	private boolean isPrivate() {
+		return visibilityModifier == VisibilityModifier.PRIVATE_FIELD
+				|| visibilityModifier == VisibilityModifier.PRIVATE_METHOD;
+	}
+
+	private boolean isProtected() {
+		return visibilityModifier == VisibilityModifier.PROTECTED_FIELD
+				|| visibilityModifier == VisibilityModifier.PROTECTED_METHOD;
+	}
+
+	private boolean isPublic() {
+		return visibilityModifier == VisibilityModifier.PUBLIC_FIELD
+				|| visibilityModifier == VisibilityModifier.PUBLIC_METHOD;
+	}
+
+	private boolean isPackagePrivate() {
+		return visibilityModifier == VisibilityModifier.PACKAGE_PRIVATE_FIELD
+				|| visibilityModifier == VisibilityModifier.PACKAGE_PRIVATE_METHOD;
+	}
+
+	private boolean isIEMandatory() {
+		return visibilityModifier == VisibilityModifier.IE_MANDATORY;
+	}
+
+	public final VisibilityModifier getVisibilityModifier() {
+		return visibilityModifier;
+	}
+
+	public final Url getUrl() {
+		return url;
+	}
+
+	public boolean hasUrl() {
+		return hasUrl;
+	}
+
+	public static boolean isMethod(String s) {
+		// s = UrlBuilder.purgeUrl(s);
+		if (s.contains("{method}")) {
+			return true;
+		}
+		if (s.contains("{field}")) {
+			return false;
+		}
+		return s.contains("(") || s.contains(")");
+	}
 }
diff --git a/src/net/sourceforge/plantuml/cucadiagram/MemberImpl.java b/src/net/sourceforge/plantuml/cucadiagram/MemberImpl.java
deleted file mode 100644
index 3c5b94a28..000000000
--- a/src/net/sourceforge/plantuml/cucadiagram/MemberImpl.java
+++ /dev/null
@@ -1,205 +0,0 @@
-/* ========================================================================
- * PlantUML : a free UML diagram generator
- * ========================================================================
- *
- * (C) Copyright 2009-2020, Arnaud Roques
- *
- * Project Info:  http://plantuml.com
- * 
- * If you like this project or if you find it useful, you can support us at:
- * 
- * http://plantuml.com/patreon (only 1$ per month!)
- * http://plantuml.com/paypal
- * 
- * This file is part of PlantUML.
- *
- * PlantUML is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * PlantUML distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
- * License for more details.
- *
- * You should have received a copy of the GNU General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
- * USA.
- *
- *
- * Original Author:  Arnaud Roques
- * 
- *
- */
-package net.sourceforge.plantuml.cucadiagram;
-
-import net.sourceforge.plantuml.Guillemet;
-import net.sourceforge.plantuml.StringUtils;
-import net.sourceforge.plantuml.Url;
-import net.sourceforge.plantuml.UrlBuilder;
-import net.sourceforge.plantuml.UrlBuilder.ModeUrl;
-import net.sourceforge.plantuml.command.regex.Matcher2;
-import net.sourceforge.plantuml.command.regex.MyPattern;
-import net.sourceforge.plantuml.command.regex.Pattern2;
-import net.sourceforge.plantuml.skin.VisibilityModifier;
-
-public class MemberImpl implements Member {
-
-	private final String display;
-	private final boolean staticModifier;
-	private final boolean abstractModifier;
-	private final Url url;
-	private final boolean hasUrl;
-
-	private final VisibilityModifier visibilityModifier;
-
-	@Override
-	public String toString() {
-		return super.toString() + " " + display;
-	}
-
-	public MemberImpl(String tmpDisplay, boolean isMethod, boolean manageModifier) {
-		tmpDisplay = tmpDisplay.replaceAll("(?i)\\{(method|field)\\}\\s*", "");
-		if (manageModifier) {
-			final Pattern2 finalUrl = MyPattern.cmpile("^(.*?)(?:\\[(" + UrlBuilder.getRegexp() + ")\\])?$");
-			final Matcher2 matcher = finalUrl.matcher(tmpDisplay);
-			if (matcher.matches() == false) {
-				throw new IllegalStateException();
-			}
-			tmpDisplay = matcher.group(1);
-			final String urlString = matcher.group(2);
-			if (urlString == null) {
-				this.url = null;
-			} else {
-				this.url = new UrlBuilder(null, ModeUrl.STRICT).getUrl(urlString);
-			}
-		} else {
-			this.url = null;
-		}
-		this.hasUrl = this.url != null;
-		final String lower = StringUtils.goLowerCase(tmpDisplay);
-
-		if (manageModifier) {
-			this.staticModifier = lower.contains("{static}") || lower.contains("{classifier}");
-			this.abstractModifier = lower.contains("{abstract}");
-			String displayClean = tmpDisplay.replaceAll("(?i)\\{(static|classifier|abstract)\\}\\s*", "").trim();
-			if (displayClean.length() == 0) {
-				displayClean = " ";
-			}
-
-			if (VisibilityModifier.isVisibilityCharacter(displayClean)) {
-				visibilityModifier = VisibilityModifier.getVisibilityModifier(displayClean, isMethod == false);
-				this.display = StringUtils.trin(Guillemet.GUILLEMET.manageGuillemet(displayClean.substring(1)));
-			} else {
-				this.display = Guillemet.GUILLEMET.manageGuillemet(displayClean);
-				visibilityModifier = null;
-			}
-		} else {
-			this.staticModifier = false;
-			this.visibilityModifier = null;
-			this.abstractModifier = false;
-			tmpDisplay = StringUtils.trin(tmpDisplay);
-			this.display = tmpDisplay.length() == 0 ? " " : Guillemet.GUILLEMET.manageGuillemet(StringUtils
-					.trin(tmpDisplay));
-		}
-	}
-
-	public String getDisplay(boolean withVisibilityChar) {
-		if (withVisibilityChar) {
-			return getDisplayWithVisibilityChar();
-		}
-		return getDisplayWithoutVisibilityChar();
-	}
-
-	private String getDisplayWithoutVisibilityChar() {
-		// assert display.length() == 0 || VisibilityModifier.isVisibilityCharacter(display.charAt(0)) == false;
-		return display;
-	}
-
-	private String getDisplayWithVisibilityChar() {
-		if (isPrivate()) {
-			return "-" + display;
-		}
-		if (isPublic()) {
-			return "+" + display;
-		}
-		if (isPackagePrivate()) {
-			return "~" + display;
-		}
-		if (isProtected()) {
-			return "#" + display;
-		}
-		if (isIEMandatory()) {
-			return "*" + display;
-		}
-		return display;
-	}
-
-	@Override
-	public boolean equals(Object obj) {
-		final MemberImpl other = (MemberImpl) obj;
-		return this.display.equals(other.display);
-	}
-
-	@Override
-	public int hashCode() {
-		return display.hashCode();
-	}
-
-	public final boolean isStatic() {
-		return staticModifier;
-	}
-
-	public final boolean isAbstract() {
-		return abstractModifier;
-	}
-
-	private boolean isPrivate() {
-		return visibilityModifier == VisibilityModifier.PRIVATE_FIELD
-				|| visibilityModifier == VisibilityModifier.PRIVATE_METHOD;
-	}
-
-	private boolean isProtected() {
-		return visibilityModifier == VisibilityModifier.PROTECTED_FIELD
-				|| visibilityModifier == VisibilityModifier.PROTECTED_METHOD;
-	}
-
-	private boolean isPublic() {
-		return visibilityModifier == VisibilityModifier.PUBLIC_FIELD
-				|| visibilityModifier == VisibilityModifier.PUBLIC_METHOD;
-	}
-
-	private boolean isPackagePrivate() {
-		return visibilityModifier == VisibilityModifier.PACKAGE_PRIVATE_FIELD
-				|| visibilityModifier == VisibilityModifier.PACKAGE_PRIVATE_METHOD;
-	}
-
-	private boolean isIEMandatory() {
-		return visibilityModifier == VisibilityModifier.IE_MANDATORY;
-	}
-
-	public final VisibilityModifier getVisibilityModifier() {
-		return visibilityModifier;
-	}
-
-	public final Url getUrl() {
-		return url;
-	}
-
-	public boolean hasUrl() {
-		return hasUrl;
-	}
-
-	public static boolean isMethod(String s) {
-		// s = UrlBuilder.purgeUrl(s);
-		if (s.contains("{method}")) {
-			return true;
-		}
-		if (s.contains("{field}")) {
-			return false;
-		}
-		return s.contains("(") || s.contains(")");
-	}
-}
diff --git a/src/net/sourceforge/plantuml/project3/Instant.java b/src/net/sourceforge/plantuml/cucadiagram/SuperGroup.java
similarity index 80%
rename from src/net/sourceforge/plantuml/project3/Instant.java
rename to src/net/sourceforge/plantuml/cucadiagram/SuperGroup.java
index 280ce5198..20e69a0c1 100644
--- a/src/net/sourceforge/plantuml/project3/Instant.java
+++ b/src/net/sourceforge/plantuml/cucadiagram/SuperGroup.java
@@ -33,14 +33,21 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.cucadiagram;
 
-public interface Instant extends Value, Comparable<Instant> {
+import java.util.HashSet;
+import java.util.Set;
 
-	public Instant increment();
+public class SuperGroup {
 
-	public Instant decrement();
+	private final Set<IGroup> groups = new HashSet<IGroup>();
 
-	public String toShortString();
+	public SuperGroup(IGroup g) {
+		this.groups.add(g);
+	}
+
+	public IGroup getFirstGroup() {
+		return groups.iterator().next();
+	}
 
 }
diff --git a/src/net/sourceforge/plantuml/cucadiagram/dot/DotData.java b/src/net/sourceforge/plantuml/cucadiagram/dot/DotData.java
index 45baa7f9e..803ba1e53 100644
--- a/src/net/sourceforge/plantuml/cucadiagram/dot/DotData.java
+++ b/src/net/sourceforge/plantuml/cucadiagram/dot/DotData.java
@@ -75,6 +75,10 @@ final public class DotData implements PortionShower {
 	private final ColorMapper colorMapper;
 	private final EntityFactory entityFactory;
 
+	public EntityFactory getEntityFactory() {
+		return entityFactory;
+	}
+
 	public DotData(IGroup topParent, List<Link> links, Collection<ILeaf> leafs, UmlDiagramType umlDiagramType,
 			ISkinParam skinParam, GroupHierarchy groupHierarchy, PortionShower portionShower, ColorMapper colorMapper,
 			EntityFactory entityFactory, boolean isHideEmptyDescriptionForState, DotMode dotMode,
diff --git a/src/net/sourceforge/plantuml/cucadiagram/entity/EntityFactory.java b/src/net/sourceforge/plantuml/cucadiagram/entity/EntityFactory.java
index 4e65ca09b..be3c760fc 100644
--- a/src/net/sourceforge/plantuml/cucadiagram/entity/EntityFactory.java
+++ b/src/net/sourceforge/plantuml/cucadiagram/entity/EntityFactory.java
@@ -38,13 +38,17 @@ package net.sourceforge.plantuml.cucadiagram.entity;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
 
-import net.sourceforge.plantuml.OptionFlags;
+import net.sourceforge.plantuml.ColorParam;
+import net.sourceforge.plantuml.ISkinParam;
+import net.sourceforge.plantuml.creole.CreoleMode;
 import net.sourceforge.plantuml.cucadiagram.Bodier;
 import net.sourceforge.plantuml.cucadiagram.Code;
 import net.sourceforge.plantuml.cucadiagram.CucaDiagram;
@@ -58,6 +62,10 @@ import net.sourceforge.plantuml.cucadiagram.ILeaf;
 import net.sourceforge.plantuml.cucadiagram.Ident;
 import net.sourceforge.plantuml.cucadiagram.LeafType;
 import net.sourceforge.plantuml.cucadiagram.Link;
+import net.sourceforge.plantuml.cucadiagram.SuperGroup;
+import net.sourceforge.plantuml.graphic.HtmlColor;
+import net.sourceforge.plantuml.graphic.USymbol;
+import net.sourceforge.plantuml.graphic.color.ColorType;
 import net.sourceforge.plantuml.skin.VisibilityModifier;
 
 public final class EntityFactory {
@@ -73,14 +81,104 @@ public final class EntityFactory {
 	private int rawLayout;
 
 	private final IGroup rootGroup = new GroupRoot(this);
+	private final SuperGroup rootSuperGroup = new SuperGroup(rootGroup);
+	
 	private final List<HideOrShow2> hides2;
 	private final List<HideOrShow2> removed;
 	/* private */ final public CucaDiagram namespaceSeparator;
+	// private final boolean mergeIntricated;
+	private Map<IGroup, ILeaf> emptyGroupsAsNode = new HashMap<IGroup, ILeaf>();
+
+	public ILeaf getLeafForEmptyGroup(IGroup g) {
+		return emptyGroupsAsNode.get(g);
+	}
+
+	public SuperGroup getRootSuperGroup() {
+		return rootSuperGroup;
+	}
+
+	private Set<SuperGroup> superGroups = null;
+	final Map<IGroup, SuperGroup> groupToSuper = new LinkedHashMap<IGroup, SuperGroup>();
+
+	public Set<SuperGroup> getAllSuperGroups() {
+		return Collections.unmodifiableSet(superGroups);
+	}
+
+	public void buildSuperGroups() {
+		superGroups = new HashSet<SuperGroup>();
+		for (IGroup g : groups2.values()) {
+			final SuperGroup sg = new SuperGroup(g);
+			superGroups.add(sg);
+			groupToSuper.put(g, sg);
+		}
+	}
+
+	public ILeaf createLeafForEmptyGroup(IGroup g, ISkinParam skinParam) {
+		final ILeaf folder = this.createLeaf(g.getIdent(), g.getCode(), g.getDisplay(), LeafType.EMPTY_PACKAGE,
+				g.getParentContainer(), null, this.namespaceSeparator.getNamespaceSeparator());
+		((EntityImpl) folder).setOriginalGroup(g);
+		final USymbol symbol = g.getUSymbol();
+		folder.setUSymbol(symbol);
+		folder.setStereotype(g.getStereotype());
+		if (g.getUrl99() != null) {
+			folder.addUrl(g.getUrl99());
+		}
+		if (g.getColors(skinParam).getColor(ColorType.BACK) == null) {
+			final ColorParam param = symbol == null ? ColorParam.packageBackground : symbol.getColorParamBack();
+			final HtmlColor c1 = skinParam.getHtmlColor(param, g.getStereotype(), false);
+			folder.setSpecificColorTOBEREMOVED(ColorType.BACK, c1 == null ? skinParam.getBackgroundColor() : c1);
+		} else {
+			folder.setSpecificColorTOBEREMOVED(ColorType.BACK, g.getColors(skinParam).getColor(ColorType.BACK));
+		}
+		emptyGroupsAsNode.put(g, folder);
+		return folder;
+	}
+
+	public Display getIntricatedDisplay(Ident ident) {
+		final Set<Ident> known = new HashSet<Ident>(groups2.keySet());
+		known.removeAll(hiddenBecauseOfIntrication);
+		String sep = namespaceSeparator.getNamespaceSeparator();
+		if (sep == null) {
+			sep = ".";
+		}
+		for (int check = ident.size() - 1; check > 0; check--) {
+			if (known.contains(ident.getPrefix(check))) {
+				// if (hiddenBecauseOfIntrication.contains(ident.getPrefix(check)) == false) {
+				return Display.getWithNewlines(ident.getSuffix(check).toString(sep))
+						.withCreoleMode(CreoleMode.SIMPLE_LINE);
+			}
+		}
+		return Display.getWithNewlines(ident.toString(sep)).withCreoleMode(CreoleMode.SIMPLE_LINE);
+	}
+
+	private final Collection<Ident> hiddenBecauseOfIntrication = new ArrayList<Ident>();
+
+	public IGroup isIntricated(IGroup parent) {
+		final int leafs = parent.getLeafsDirect().size();
+		final Collection<IGroup> children = parent.getChildren();
+		if (leafs == 0 && children.size() == 1) {
+			final IGroup g = children.iterator().next();
+			if (g.getLeafsDirect().size() == 0 && g.getChildren().size() == 0
+					&& g.getGroupType() == GroupType.PACKAGE) {
+				return null;
+			}
+			for (Link link : this.getLinks()) {
+				if (link.contains(parent)) {
+					return null;
+				}
+			}
+			((EntityImpl) g).setIntricated(true);
+			hiddenBecauseOfIntrication.add(parent.getIdent());
+			return g;
+		}
+		return null;
+	}
 
 	public EntityFactory(List<HideOrShow2> hides2, List<HideOrShow2> removed, CucaDiagram namespaceSeparator) {
 		this.hides2 = hides2;
 		this.removed = removed;
 		this.namespaceSeparator = namespaceSeparator;
+		// this.mergeIntricated = namespaceSeparator.mergeIntricated();
 
 		// if (OptionFlags.V1972(namespaceSeparator)) {
 		// this.leafsByCode = null;
@@ -400,9 +498,10 @@ public final class EntityFactory {
 			if (result != null) {
 				return result;
 			}
-			System.err.println("getParentContainer::groups2=" + groups2);
-			result = createGroup(parent, parent, Display.getWithNewlines(parent.getName()), null, GroupType.PACKAGE,
-					null, Collections.<VisibilityModifier>emptySet(), namespaceSeparator.getNamespaceSeparator());
+//			System.err.println("getParentContainer::groups2=" + groups2);
+			final Display display = Display.getWithNewlines(parent.getName());
+			result = createGroup(parent, parent, display, null, GroupType.PACKAGE, null,
+					Collections.<VisibilityModifier>emptySet(), namespaceSeparator.getNamespaceSeparator());
 			addGroup(result);
 			return result;
 		}
@@ -412,4 +511,5 @@ public final class EntityFactory {
 		return parentContainer;
 	}
 
+
 }
diff --git a/src/net/sourceforge/plantuml/cucadiagram/entity/EntityImpl.java b/src/net/sourceforge/plantuml/cucadiagram/entity/EntityImpl.java
index 8eb85b7f1..019e5cbf7 100644
--- a/src/net/sourceforge/plantuml/cucadiagram/entity/EntityImpl.java
+++ b/src/net/sourceforge/plantuml/cucadiagram/entity/EntityImpl.java
@@ -45,12 +45,15 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
+import javax.swing.text.html.HTMLDocument.HTMLReader.IsindexAction;
+
 import net.sourceforge.plantuml.FontParam;
 import net.sourceforge.plantuml.Guillemet;
 import net.sourceforge.plantuml.ISkinParam;
 import net.sourceforge.plantuml.OptionFlags;
 import net.sourceforge.plantuml.StringUtils;
 import net.sourceforge.plantuml.Url;
+import net.sourceforge.plantuml.creole.CreoleMode;
 import net.sourceforge.plantuml.cucadiagram.Bodier;
 import net.sourceforge.plantuml.cucadiagram.Code;
 import net.sourceforge.plantuml.cucadiagram.Display;
@@ -79,7 +82,7 @@ import net.sourceforge.plantuml.svek.SingleStrategy;
 import net.sourceforge.plantuml.ugraphic.UFont;
 import net.sourceforge.plantuml.utils.UniqueSequence;
 
-final class EntityImpl implements ILeaf, IGroup {
+final public class EntityImpl implements ILeaf, IGroup {
 
 	private final EntityFactory entityFactory;
 
@@ -232,6 +235,9 @@ final class EntityImpl implements ILeaf, IGroup {
 	}
 
 	public Display getDisplay() {
+		if (intricated) {
+			return entityFactory.getIntricatedDisplay(ident);
+		}
 		return display;
 	}
 
@@ -261,7 +267,8 @@ final class EntityImpl implements ILeaf, IGroup {
 
 	@Override
 	public String toString() {
-		// return super.toString() + code + " " + display + "(" + leafType + ")[" + groupType + "]  " + xposition + " "
+		// return super.toString() + code + " " + display + "(" + leafType + ")[" +
+		// groupType + "] " + xposition + " "
 		// + getUid();
 		if (entityFactory.namespaceSeparator.V1972())
 			return getUid() + " " + ident + " " + display + "(" + leafType + ")[" + groupType + "]";
@@ -435,46 +442,51 @@ final class EntityImpl implements ILeaf, IGroup {
 		if (dest.isGroup() == false) {
 			throw new UnsupportedOperationException();
 		}
-		System.err.println("moveEntitiesTo1972::before1::groups2=" + entityFactory.groups2());
+		// System.err.println("moveEntitiesTo1972::before1::groups2=" +
+		// entityFactory.groups2());
 		final Ident firstIdent = getIdent();
 		final Ident destIdent = dest.getIdent();
-		System.err.println("moveEntitiesTo1972::this=" + firstIdent);
-		System.err.println("moveEntitiesTo1972::dest=" + destIdent);
+		// System.err.println("moveEntitiesTo1972::this=" + firstIdent);
+		// System.err.println("moveEntitiesTo1972::dest=" + destIdent);
 		if (destIdent.startsWith(firstIdent) == false) {
 			throw new UnsupportedOperationException();
 		}
-		System.err.println("moveEntitiesTo1972::before2::groups2=" + entityFactory.groups2());
+		// System.err.println("moveEntitiesTo1972::before2::groups2=" +
+		// entityFactory.groups2());
 		for (ILeaf ent : new ArrayList<ILeaf>(entityFactory.leafs2())) {
 			Ident ident = ent.getIdent();
 			if (ident.equals(firstIdent) == false && ident.startsWith(firstIdent)
 					&& ident.startsWith(destIdent) == false) {
-				System.err.print("moving leaf ident1=" + ident);
+				// System.err.print("moving leaf ident1=" + ident);
 				entityFactory.leafs2.remove(ident);
 				ident = ident.move(firstIdent, destIdent);
-				System.err.println(" to ident2=" + ident);
+				// System.err.println(" to ident2=" + ident);
 				((EntityImpl) ent).ident = ident;
 				((EntityImpl) ent).code = ident;
 				entityFactory.leafs2.put(ident, ent);
 			}
 		}
-		System.err.println("moveEntitiesTo1972::before3::groups2=" + entityFactory.groups2());
+		// System.err.println("moveEntitiesTo1972::before3::groups2=" +
+		// entityFactory.groups2());
 		for (IGroup ent : new ArrayList<IGroup>(entityFactory.groups2())) {
 			Ident ident = ent.getIdent();
-			System.err.println("found=" + ident + " " + ident.startsWith(firstIdent) + " "
-					+ ident.startsWith(destIdent));
+			// System.err.println("found=" + ident + " " + ident.startsWith(firstIdent) + "
+			// "
+			// + ident.startsWith(destIdent));
 			if (ident.equals(firstIdent) == false && ident.startsWith(firstIdent)
 					&& ident.startsWith(destIdent) == false) {
-				System.err.print("moving gr ident1=" + ident);
+				// System.err.print("moving gr ident1=" + ident);
 				entityFactory.groups2.remove(ident);
 				ident = ident.move(firstIdent, destIdent);
-				System.err.println(" to ident2=" + ident);
+				// System.err.println(" to ident2=" + ident);
 				((EntityImpl) ent).ident = ident;
 				((EntityImpl) ent).code = ident;
 				entityFactory.groups2.put(ident, ent);
-				System.err.println("-->groups2=" + entityFactory.groups2());
+				// System.err.println("-->groups2=" + entityFactory.groups2());
 			}
 		}
-		System.err.println("moveEntitiesTo1972::after::groups2=" + entityFactory.groups2());
+		// System.err.println("moveEntitiesTo1972::after::groups2=" +
+		// entityFactory.groups2());
 		// for (IGroup g : dest.getChildren()) {
 		// // ((EntityImpl) g).parentContainer = dest;
 		// throw new IllegalStateException();
@@ -757,4 +769,21 @@ final class EntityImpl implements ILeaf, IGroup {
 		return legend;
 	}
 
+	private boolean intricated;
+
+	public void setIntricated(boolean intricated) {
+		this.intricated = intricated;
+
+	}
+
+	private IGroup originalGroup;
+
+	public void setOriginalGroup(IGroup originalGroup) {
+		this.originalGroup = originalGroup;
+	}
+
+	public IGroup getOriginalGroup() {
+		return originalGroup;
+	}
+
 }
diff --git a/src/net/sourceforge/plantuml/descdiagram/DescriptionDiagramFactory.java b/src/net/sourceforge/plantuml/descdiagram/DescriptionDiagramFactory.java
index 3fb4d72a0..098e63917 100644
--- a/src/net/sourceforge/plantuml/descdiagram/DescriptionDiagramFactory.java
+++ b/src/net/sourceforge/plantuml/descdiagram/DescriptionDiagramFactory.java
@@ -49,9 +49,9 @@ import net.sourceforge.plantuml.command.CommandFootboxIgnored;
 import net.sourceforge.plantuml.command.CommandPage;
 import net.sourceforge.plantuml.command.CommandRankDir;
 import net.sourceforge.plantuml.command.UmlDiagramFactory;
-import net.sourceforge.plantuml.command.note.FactoryNoteCommand;
-import net.sourceforge.plantuml.command.note.FactoryNoteOnEntityCommand;
-import net.sourceforge.plantuml.command.note.FactoryNoteOnLinkCommand;
+import net.sourceforge.plantuml.command.note.CommandFactoryNote;
+import net.sourceforge.plantuml.command.note.CommandFactoryNoteOnEntity;
+import net.sourceforge.plantuml.command.note.CommandFactoryNoteOnLink;
 import net.sourceforge.plantuml.command.regex.RegexLeaf;
 import net.sourceforge.plantuml.command.regex.RegexOr;
 import net.sourceforge.plantuml.descdiagram.command.CommandArchimate;
@@ -92,10 +92,10 @@ public class DescriptionDiagramFactory extends UmlDiagramFactory {
 		//
 		cmds.add(new CommandPackageWithUSymbol());
 		cmds.add(new CommandEndPackage());
-		final FactoryNoteCommand factoryNoteCommand = new FactoryNoteCommand();
+		final CommandFactoryNote factoryNoteCommand = new CommandFactoryNote();
 		cmds.add(factoryNoteCommand.createMultiLine(false));
 
-		final FactoryNoteOnEntityCommand factoryNoteOnEntityCommand = new FactoryNoteOnEntityCommand("desc",
+		final CommandFactoryNoteOnEntity factoryNoteOnEntityCommand = new CommandFactoryNoteOnEntity("desc",
 				new RegexOr("ENTITY", //
 						new RegexLeaf("[\\p{L}0-9_.]+"), //
 						new RegexLeaf("\\(\\)[%s]*[\\p{L}0-9_.]+"), //
@@ -117,7 +117,7 @@ public class DescriptionDiagramFactory extends UmlDiagramFactory {
 		cmds.add(factoryNoteOnEntityCommand.createMultiLine(false));
 		cmds.add(factoryNoteCommand.createMultiLine(false));
 
-		final FactoryNoteOnLinkCommand factoryNoteOnLinkCommand = new FactoryNoteOnLinkCommand();
+		final CommandFactoryNoteOnLink factoryNoteOnLinkCommand = new CommandFactoryNoteOnLink();
 		cmds.add(factoryNoteOnLinkCommand.createSingleLine());
 		cmds.add(factoryNoteOnLinkCommand.createMultiLine(false));
 
diff --git a/src/net/sourceforge/plantuml/descdiagram/command/CommandCreateElementParenthesis.java b/src/net/sourceforge/plantuml/descdiagram/command/CommandCreateElementParenthesis.java
new file mode 100644
index 000000000..ba251cd1f
--- /dev/null
+++ b/src/net/sourceforge/plantuml/descdiagram/command/CommandCreateElementParenthesis.java
@@ -0,0 +1,242 @@
+/* ========================================================================
+ * PlantUML : a free UML diagram generator
+ * ========================================================================
+ *
+ * (C) Copyright 2009-2020, Arnaud Roques
+ *
+ * Project Info:  http://plantuml.com
+ * 
+ * If you like this project or if you find it useful, you can support us at:
+ * 
+ * http://plantuml.com/patreon (only 1$ per month!)
+ * http://plantuml.com/paypal
+ * 
+ * This file is part of PlantUML.
+ *
+ * PlantUML is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * PlantUML distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
+ * USA.
+ *
+ *
+ * Original Author:  Arnaud Roques
+ * 
+ *
+ */
+package net.sourceforge.plantuml.descdiagram.command;
+
+import net.sourceforge.plantuml.FontParam;
+import net.sourceforge.plantuml.LineLocation;
+import net.sourceforge.plantuml.OptionFlags;
+import net.sourceforge.plantuml.StringUtils;
+import net.sourceforge.plantuml.Url;
+import net.sourceforge.plantuml.UrlBuilder;
+import net.sourceforge.plantuml.UrlBuilder.ModeUrl;
+import net.sourceforge.plantuml.classdiagram.AbstractEntityDiagram;
+import net.sourceforge.plantuml.classdiagram.ClassDiagram;
+import net.sourceforge.plantuml.command.CommandExecutionResult;
+import net.sourceforge.plantuml.command.SingleLineCommand2;
+import net.sourceforge.plantuml.command.regex.IRegex;
+import net.sourceforge.plantuml.command.regex.RegexConcat;
+import net.sourceforge.plantuml.command.regex.RegexLeaf;
+import net.sourceforge.plantuml.command.regex.RegexOptional;
+import net.sourceforge.plantuml.command.regex.RegexOr;
+import net.sourceforge.plantuml.command.regex.RegexResult;
+import net.sourceforge.plantuml.cucadiagram.Code;
+import net.sourceforge.plantuml.cucadiagram.Display;
+import net.sourceforge.plantuml.cucadiagram.IEntity;
+import net.sourceforge.plantuml.cucadiagram.ILeaf;
+import net.sourceforge.plantuml.cucadiagram.Ident;
+import net.sourceforge.plantuml.cucadiagram.LeafType;
+import net.sourceforge.plantuml.cucadiagram.Stereotype;
+import net.sourceforge.plantuml.descdiagram.DescriptionDiagram;
+import net.sourceforge.plantuml.graphic.HtmlColor;
+import net.sourceforge.plantuml.graphic.USymbol;
+import net.sourceforge.plantuml.graphic.color.ColorParser;
+import net.sourceforge.plantuml.graphic.color.ColorType;
+import net.sourceforge.plantuml.graphic.color.Colors;
+
+public class CommandCreateElementParenthesis extends SingleLineCommand2<ClassDiagram> {
+
+	public CommandCreateElementParenthesis() {
+		super(getRegexConcat());
+	}
+
+	private static IRegex getRegexConcat() {
+		return RegexConcat.build(CommandCreateElementParenthesis.class.getName(), RegexLeaf.start(), //
+				new RegexLeaf("\\(\\)[%s]+"), //
+				color2().getRegex(), //
+				RegexLeaf.spaceZeroOrMore(), //
+				new RegexOr(//
+						new RegexLeaf("CODE1", CommandCreateElementFull.CODE_WITH_QUOTE), //
+						new RegexConcat(//
+								new RegexLeaf("DISPLAY2", CommandCreateElementFull.DISPLAY), //
+								new RegexOptional( //
+										new RegexConcat( //
+												RegexLeaf.spaceOneOrMore(), //
+												new RegexLeaf("STEREOTYPE2", "(\\<\\<.+\\>\\>)")//
+										)), //
+								RegexLeaf.spaceZeroOrMore(), //
+								new RegexLeaf("as"), //
+								RegexLeaf.spaceOneOrMore(), //
+								new RegexLeaf("CODE2", CommandCreateElementFull.CODE)), //
+						new RegexConcat(//
+								new RegexLeaf("CODE3", CommandCreateElementFull.CODE), //
+								new RegexOptional( //
+										new RegexConcat( //
+												RegexLeaf.spaceOneOrMore(), //
+												new RegexLeaf("STEREOTYPE3", "(\\<\\<.+\\>\\>)") //
+										)), //
+								RegexLeaf.spaceOneOrMore(), //
+								new RegexLeaf("as"), //
+								RegexLeaf.spaceZeroOrMore(), //
+								new RegexLeaf("DISPLAY3", CommandCreateElementFull.DISPLAY)), //
+						new RegexConcat(//
+								new RegexLeaf("DISPLAY4", CommandCreateElementFull.DISPLAY_WITHOUT_QUOTE), //
+								new RegexOptional( //
+										new RegexConcat( //
+												RegexLeaf.spaceOneOrMore(), //
+												new RegexLeaf("STEREOTYPE4", "(\\<\\<.+\\>\\>)") //
+										)), //
+								RegexLeaf.spaceZeroOrMore(), //
+								new RegexLeaf("as"), //
+								RegexLeaf.spaceOneOrMore(), //
+								new RegexLeaf("CODE4", CommandCreateElementFull.CODE)) //
+				), //
+				new RegexOptional( //
+						new RegexConcat( //
+								RegexLeaf.spaceZeroOrMore(), //
+								new RegexLeaf("STEREOTYPE", "(\\<\\<.+\\>\\>)") //
+						)), //
+				RegexLeaf.spaceZeroOrMore(), //
+				new RegexLeaf("URL", "(" + UrlBuilder.getRegexp() + ")?"), //
+				RegexLeaf.spaceZeroOrMore(), //
+				color().getRegex(), RegexLeaf.end());
+	}
+
+	private static ColorParser color() {
+		return ColorParser.simpleColor(ColorType.BACK);
+	}
+
+	private static ColorParser color2() {
+		return ColorParser.simpleColor(ColorType.BACK, "COLOR2");
+	}
+
+//	private static final String CODE_CORE = "[\\p{L}0-9_.]+|\\(\\)[%s]*[\\p{L}0-9_.]+|\\(\\)[%s]*[%g][^%g]+[%g]|:[^:]+:|\\([^()]+\\)|\\[[^\\[\\]]+\\]";
+//	public static final String CODE = "(" + CODE_CORE + ")";
+//	public static final String CODE_WITH_QUOTE = "(" + CODE_CORE + "|[%g].+?[%g])";
+//
+//	private static final String DISPLAY_CORE = "[%g].+?[%g]|:[^:]+:|\\([^()]+\\)|\\[[^\\[\\]]+\\]";
+//	public static final String DISPLAY = "(" + DISPLAY_CORE + ")";
+//	public static final String DISPLAY_WITHOUT_QUOTE = "(" + DISPLAY_CORE + "|[\\p{L}0-9_.]+)";
+
+	@Override
+	final protected boolean isForbidden(CharSequence line) {
+		if (line.toString().matches("^[\\p{L}0-9_.]+$")) {
+			return true;
+		}
+		return false;
+	}
+
+	@Override
+	protected CommandExecutionResult executeArg(ClassDiagram diagram, LineLocation location, RegexResult arg) {
+		String codeRaw = arg.getLazzy("CODE", 0);
+		final String displayRaw = arg.getLazzy("DISPLAY", 0);
+		final String symbol = "interface";
+		final LeafType type;
+		final USymbol usymbol;
+
+		type = LeafType.DESCRIPTION;
+		usymbol = USymbol.getFromString(symbol, diagram.getSkinParam());
+
+		final String idShort = StringUtils.eventuallyRemoveStartingAndEndingDoubleQuote(codeRaw);
+		final Ident ident = diagram.buildLeafIdent(idShort);
+		final Code code = diagram.V1972() ? ident : diagram.buildCode(idShort);
+		if (!diagram.V1972() && diagram.isGroup(code)) {
+			return CommandExecutionResult.error("This element (" + code.getName() + ") is already defined");
+		}
+		if (diagram.V1972() && diagram.isGroupStrict(ident)) {
+			return CommandExecutionResult.error("This element (" + ident.getName() + ") is already defined");
+		}
+		String display = displayRaw;
+		if (display == null) {
+			display = code.getName();
+		}
+		display = StringUtils.eventuallyRemoveStartingAndEndingDoubleQuote(display);
+		final String stereotype = arg.getLazzy("STEREOTYPE", 0);
+		if (existsWithBadType3(diagram, code, ident, type, usymbol)) {
+			return CommandExecutionResult.error("This element (" + code.getName() + ") is already defined");
+		}
+		final IEntity entity = diagram.getOrCreateLeaf(ident, code, type, usymbol);
+		entity.setDisplay(Display.getWithNewlines(display));
+		entity.setUSymbol(usymbol);
+		if (stereotype != null) {
+			entity.setStereotype(new Stereotype(stereotype, diagram.getSkinParam().getCircledCharacterRadius(),
+					diagram.getSkinParam().getFont(null, false, FontParam.CIRCLED_CHARACTER),
+					diagram.getSkinParam().getIHtmlColorSet()));
+		}
+
+		final String urlString = arg.get("URL", 0);
+		if (urlString != null) {
+			final UrlBuilder urlBuilder = new UrlBuilder(diagram.getSkinParam().getValue("topurl"), ModeUrl.STRICT);
+			final Url url = urlBuilder.getUrl(urlString);
+			entity.addUrl(url);
+		}
+
+		Colors colors = color().getColor(arg, diagram.getSkinParam().getIHtmlColorSet());
+
+		final HtmlColor lineColor = diagram.getSkinParam().getIHtmlColorSet().getColorIfValid(arg.get("LINECOLOR", 1));
+		if (lineColor != null) {
+			colors = colors.add(ColorType.LINE, lineColor);
+		}
+		entity.setColors(colors);
+
+		// entity.setSpecificColorTOBEREMOVED(ColorType.BACK,
+		// diagram.getSkinParam().getIHtmlColorSet().getColorIfValid(arg.get("COLOR",
+		// 0)));
+		return CommandExecutionResult.ok();
+	}
+
+	public static boolean existsWithBadType3(AbstractEntityDiagram diagram, Code code, Ident ident, LeafType type,
+			USymbol usymbol) {
+		if (diagram.V1972()) {
+			if (diagram.leafExistSmart(ident) == false) {
+				return false;
+			}
+			final ILeaf other = diagram.getLeafSmart(ident);
+			if (other.getLeafType() != type) {
+				return true;
+			}
+			if (usymbol != null && other.getUSymbol() != usymbol) {
+				return true;
+			}
+			return false;
+		} else {
+			if (diagram.leafExist(code) == false) {
+				return false;
+			}
+			final ILeaf other = diagram.getLeaf(code);
+			if (other.getLeafType() != type) {
+				return true;
+			}
+			if (usymbol != null && other.getUSymbol() != usymbol) {
+				return true;
+			}
+			return false;
+		}
+	}
+
+	private char getCharEncoding(final String codeRaw) {
+		return codeRaw != null && codeRaw.length() > 2 ? codeRaw.charAt(0) : 0;
+	}
+}
diff --git a/src/net/sourceforge/plantuml/donors/PSystemDonors.java b/src/net/sourceforge/plantuml/donors/PSystemDonors.java
index 15e8628b5..36f2fb985 100644
--- a/src/net/sourceforge/plantuml/donors/PSystemDonors.java
+++ b/src/net/sourceforge/plantuml/donors/PSystemDonors.java
@@ -71,24 +71,24 @@ public class PSystemDonors extends AbstractPSystem {
 	private static final int COLS = 6;
 	private static final int FREE_LINES = 6;
 
-	public static final String DONORS = "6_C902mFU3XMJbc44wzsvvsjcZxOY0eHBCyJYiF08fxk1iGVuDxfSR-H_YAwqhrlcX5jsPhYFACe5WGz"
-			+ "tYRN3DlSsmEncfYlaZlpHhymBmn9Q4C2N8Sx8ZvLXfGWbehklqpSdcuT5-9WXmgX75BSio7JYHWNmTFc"
-			+ "zIrbijRaGSBf_cywBUuU2c6MPYkSZqQe_GEd6AjhBdS_Yheqwk8kLQrkXHhJgOjZk2eqogi7lhPGCYey"
-			+ "DCvtG8jPCTb3f4JhnX6YHcU6aPgqpiaJUMh_KI-WJUToJOcS2UKnIYIwRYkTsNVG_OBabn0AkM2Bm08g"
-			+ "7BuJj7Ljn4X2CcWOwibeRBbconCt377enBxyer7KkATaUXhQ965Ne2JgQnoGz4eZMiJiz6P09SzeCQD9"
-			+ "KbVcJNXy7PQcQ1qqt0iMOPQq2Qi9v4te2P_mLPTCpl5ILF2cfoIFPflGKDasuDxCwDjSInBbVxSnS-Qx"
-			+ "HxLvIcUhgut_sHnorvHLosz66-qSmRs_BQHWSR50VW2zD2SgoPzbtrX8GhYtqWiPdFklNAC8d3qo5HR_"
-			+ "4LjQe__MbMniHIoF6mjyN5zNcfB1bbo5N8Q-fbNABPqlb9IcnkSXZvQEpoJMbCpgdQUFKJAtyl-xOILc"
-			+ "Li90LwQORRfUK3HMiPgUR4m4fq7tXjm4o0ke3fDRULWznA_p0Pct9ICHkGt9AEJhB-MGYnGciAO7Iim0"
-			+ "5ayfxI69U212JJlGsr1fHTeYzDjDUjgMZ8Nxf3yyxfePepoYaneduO-5mE669JLlfhkqxz4JU8ulpwlk"
-			+ "8tMi0bpBlJTSfFeP3AmhBHY1r1tHsFUoIyAXWH9Px2Ll3V4zmneN3ps-u4mM7M96c-EGvQ5oEncjz1R3"
-			+ "koP3tocJpTkhlyWRIBm8wo4T6OtW_M7oI2qkaOHqi1m_Mm1Yvk8LkrBWcaBLBJidc-iKfhglFeHAyEDy"
-			+ "0gFoBJp3MElkHN46DCgS4_DUMVFHU0qfFXPGFOS8MjA9CaTAKQQjPMJCMeEUWzR1zbFT5lJR-5R3gaBn"
-			+ "RH7TiTW0Q7t6DAFtS0Cy4Ifg5Rpaf330Nxz-JG_6EiJujPVDY8DaSqaCeGAQqtznMfflnz0IrblqVzks"
-			+ "lQsSF_zUHtPm6esxhog8deB0_rsKd8EchlEf-c_WWoJYW9MOjN4IAo-DtWWa9JYE3rPHJqqpwdToHhaj"
-			+ "jUYTnuSg_NQgn84vDoo_QAKeXoznohQtXbEbP3_-nk8aXuP_YYfhChagfJKnefhOsL6Sfx7lKOgxhbf1"
-			+ "17FW4e6zN_zqckOXO_QBbJaC1qD-FY9T3k6kXRMOriPfWVtYhEyjRLqouN_92ByKQzhEwc6wSVnRsWNA"
-			+ "lXr9mdWKi7PAd2Ua1lyb0m00";
+	public static final String DONORS = "6qOA02mFU3XMJbc44wzsfuUeUc2VR6qyRpkVqESmi8ZJNK3y1jWxFUutqbz4LxhtJJEYBLjJd6SKnOAW"
+			+ "nzlacc6RUtiWLXFpLN9dtkWNveKX2Mr8WEjmHyIdof0IVEycQbqbNzfRkOJbCk0By7OGIqNtNoPkJxUE"
+			+ "2t6mGuLG3YdksVytfxRkNbM58LrMhTFH5Ih0gi0ZOdS_QdDfrCPtKvlUX09qAd45DrMLy7hZfYBDuAxZ"
+			+ "N3gqw1GZpI8bNZUE43SwC_JKfF1ByaH6vZ_g1RJfNAufaJEXF4Q9HDTjVqVs7PG_BNb1H48ks2ImW4gE"
+			+ "m0cANXinFY8oQEZgIMDihcRC4oyCAUZ4lloZKTIufsHw6bfyH5s1ahYlSG3Iatgqu6nXDA2i9rkCQ9Ag"
+			+ "C_CbtByEorDwpJJS3nPHbhG9hYd8cz0JFk6hBfcSuwMeuIy_JVB8rmGQBbi6t9iEVOjhaqIvdMsQkVDf"
+			+ "KssUqjdgQkD_DeVSDMNLyfjHH2D7SEzl2obON5e0FmAzD2SgoPzb7rd8Xd1jfHVITQvUkHeHBjcGgM9T"
+			+ "_eWjh-d_wE9ocn7Bus2HpvwlCqt9G4jTYLo6lj3AvhRE5qfAK-FpoAEbupFXcgpPlUFqFihcDF-_RYip"
+			+ "un80dPbY5dJze1YifjNmP0CHc1QwFUGcG5v0TPZSoiFg4R_E1mHlAYCHXGrAB-Hzi-MGYnGciBmxIdG0"
+			+ "9ayfxU4Zve48HUs2daQLUgGiGh_TfATjsPYuJ_fWVzPW3FAOpjOu2Nyi18umpAf5ixvR-m8z1LUvNEPL"
+			+ "Ty_Fgm3M5juBBbBz90DwfGKZ8hL6DFQzx99mQA0mYxFahI6-PrXZvQ7x1vpMeatCQ3CVCWsI6ybgfRSO"
+			+ "tZKP_kOXgzcnMkGvi4V4PaWpenMyFveyKejBV4Hqi9mWMm0Ivk8LU_72dIjLkPoSRAvJcEgnMWiLyhla"
+			+ "4rYgmCBJWxNo7i0piaUnSpZlbJKVZTUGuda1bNAOe2wTA7Ea56NQMY33rghrRBGEjf_gM31lurivAmw5"
+			+ "jqrqrhX1mHGsdHAuqOYHA2SQ5NhPay80V_twbNfoKmSPl_QooJYriY6MGgMSByiNfw3nTWoj-rlqVzks"
+			+ "lQsSF_zUDvkSUkFjAmNGSv0S_nxAhAEBajNzDFWRR9WCSC1AAjeuuSf3Isy5aXRinpk3gFFjZEQTd2ot"
+			+ "f1QzSlvGsT_zoIm7LtF3YejwYdBu4gTiUsi45M_x_ZS6MpaC_4LKcPFXwgzUBICAnFOsuZoDVOzIt5cL"
+			+ "11BCZii4pdxrqscQXupPHoYp7Yu2-Vc8T3c4XnRKqhGuJWHMnHdUUwTroOJ_9JU9kEgdh2tLGq_ZL6vg"
+			+ "5sZuTYMA6qQ1uI9cny3uTfEJ5RZvfeTwgBOVwVGN4oSkAeRTtdDs0W00";
 
 	/*
 	 * Special thanks to our sponsors and donors:
@@ -110,8 +110,8 @@ public class PSystemDonors extends AbstractPSystem {
 		final List<TextBlock> cols = getCols(getDonors(), COLS, FREE_LINES);
 		return new UDrawable() {
 			public void drawU(UGraphic ug) {
-				final TextBlockBackcolored header = GraphicStrings.createBlackOnWhite(Arrays
-						.asList("<b>Special thanks to our sponsors and donors !"));
+				final TextBlockBackcolored header = GraphicStrings
+						.createBlackOnWhite(Arrays.asList("<b>Special thanks to our sponsors and donors !"));
 				header.drawU(ug);
 				final StringBounder stringBounder = ug.getStringBounder();
 				ug = ug.apply(new UTranslate(0, header.calculateDimension(stringBounder).getHeight()));
@@ -143,7 +143,8 @@ public class PSystemDonors extends AbstractPSystem {
 
 	private List<String> getDonors() throws IOException {
 		final List<String> lines = new ArrayList<String>();
-		final Transcoder t = new TranscoderImpl(new AsciiEncoder(), new StringCompressorNone(), new CompressionBrotli());
+		final Transcoder t = new TranscoderImpl(new AsciiEncoder(), new StringCompressorNone(),
+				new CompressionBrotli());
 		final String s = t.decode(DONORS).replace('*', '.');
 		final StringTokenizer st = new StringTokenizer(s, BackSlash.NEWLINE);
 		while (st.hasMoreTokens()) {
diff --git a/src/net/sourceforge/plantuml/graphic/QuoteUtils.java b/src/net/sourceforge/plantuml/graphic/QuoteUtils.java
index c70d29477..1e39528fd 100644
--- a/src/net/sourceforge/plantuml/graphic/QuoteUtils.java
+++ b/src/net/sourceforge/plantuml/graphic/QuoteUtils.java
@@ -42,298 +42,209 @@ import java.util.List;
 
 public class QuoteUtils {
 
-	static private final List<String> quotes = Arrays
-			.asList("Ur'f qrnq, Wvz.",
-					"Ol Tenogune'f unzzre, ol gur fbaf bs Jbeina, lbh funyy or niratrq.",
-					"Ebnqf? Jurer jr'er tbvat, jr qba'g arrq ebnqf.",
-					"Gur gvzr vf bhg bs wbvag.",
-					"P'rfg phevrhk purm yrf znevaf pr orfbva qr snver qrf cuenfrf.",
-					"V'z gnyxvat nobhg gur bgure Crgre, gur bar ba gur bgure fvqr.",
-					"Znl gur Sbepr or jvgu lbh!",
-					"Arire tvir hc, arire fheeraqre...",
-					"Unfgn yn ivfgn, onol.",
-					"Url, Qbp, jr orggre onpx hc. Jr qba'g unir rabhtu ebnq gb trg hc gb 88.",
-					"Terrgvatf, Cebsrffbe Snyxra. Funyy jr cynl n tnzr?",
-					"V pna'g punatr gur ynj bs culfvpf!",
-					"N fgenatr tnzr. Gur bayl jvaavat zbir vf abg gb cynl.",
-					"V'z gur Tngrxrrcre, ner lbh gur Xrlznfgre?",
-					"V nz gur Znfgre Pbageby Cebtenz. Ab bar Hfre jebgr zr.",
-					"Yvsr? Qba'g gnyx gb zr nobhg yvsr.",
-					"V nyjnlf gubhtug fbzrguvat jnf shaqnzragnyyl jebat jvgu gur havirefr.",
-					"N ebobg znl abg vawher n uhzna orvat be, guebhtu vanpgvba, nyybj n uhzna orvat gb pbzr gb unez.",
-					"Fheeraqre znl or bhe bayl bcgvba.",
-					"Fvk ol avar. Sbegl gjb.",
-					"Vg'f yvsr, Wvz, ohg abg nf jr xabj vg.",
-					"Qba'g Cnavp!",
-					"Jung qb lbh zrna? Na Nsevpna be Rhebcrna fjnyybj?",
-					"V arrq lbhe obbgf lbhe pybgurf naq lbhe zbgbeplpyr",
-					"Lbh sbetbg gb fnl cyrnfr...",
-					"Lbh unir qvrq bs qlfragrel.",
-					"Jbhyqa'g lbh cersre n avpr tnzr bs purff?",
-					"Jura lbh unir ryvzvangrq gur vzcbffvoyr, jungrire erznvaf, ubjrire vzcebonoyr, zhfg or gur gehgu.",
-					"V xabj abj jul lbh pel. Ohg vg'f fbzrguvat V pna arire qb.",
-					"Erfvfgnapr vf shgvyr. Lbh jvyy or nffvzvyngrq.",
-					"Nalguvat qvssrerag vf tbbq.",
-					"Penpxrq ol Nyqb Erfrg naq Ynherag Ehrvy.",
-					"V'z obgu. V'z n pryroevgl va na rzretrapl.",
-					"Qb lbh xabj guvf terng terng cbyvfu npgbe, Wbfrcu Ghen?",
-					"Gb vasvavgl naq orlbaq!",
-					"Fcnpr: gur svany sebagvre...",
-					"Fhe zba ovyyrg, grarm, l n rpevg Fnvag-Ynmner, p'rfg zrf lrhk bh dhbv ?",
-					"Gur obl vf vzcbegnag. Ur unf gb yvir.",
-					"Bapr hcba n gvzr va n tnynkl sne, sne njnl...",
-					"Naq lbh xabj gurer'f n ybat ybat jnl nurnq bs lbh...",
-					"Na nyyretl gb bkltra? Ryz oyvtug?",
-					"Ohg nybef lbh ner Serapu!",
-					"A'nv-wr qbap gnag irph dhr cbhe prggr vasnzvr?",
-					"Fbzrguvat vf ebggra va gur Fgngr bs Qraznex.",
-					"Url, jung qb lbh jnag? Zvenpyrf?",
-					"1.21 tvtnjnggf! 1.21 tvtnjnggf. Terng Fpbgg! ",
-					"Jung gur uryy vf n tvtnjngg?",
-					"V arrq n inpngvba.",
-					"Ba qrienvg wnznvf dhvggre Zbagnhona.",
-					"Zl sbepr vf n cyngsbez gung lbh pna pyvzo ba...",
-					"Gurer'f fbzrguvat jrveq, naq vg qba'g ybbx tbbq...",
-					"Rg evra ienvzrag ar punatr znvf gbhg rfg qvssrerag",
-					"Ornz zr hc, Fpbggl.",
-					"Gurer vf ab fcbba.",
-					"Sbyybj gur juvgr enoovg.",
-					"Arire fraq n uhzna gb qb n znpuvar'f wbo.",
-					"Theh zrqvgngvba. Cerff yrsg zbhfr ohggba gb pbagvahr.",
-					"V qba'g guvax jr'er va Xnafnf nalzber.",
-					"Yhxr, V nz lbhe sngure.",
-					"Oybbq, Fjrng naq Grnef",
-					"Ubhfgba, jr unir n ceboyrz.",
-					"Xrlobneq snvyher, cerff nal xrl gb pbagvahr",
-					"Ovt zvfgnxr!",
-					"Ubj znal HZY qrfvtaref qbrf vg gnxr gb punatr n yvtugohyo ?",
-					"Qb lbh yvxr zbivrf nobhg tynqvngbef ?",
-					"Gur fcvevg bs yrneavat vf n ynfgvat sebagvre.",
-					"Vg vf phevbhf sbe fnvybef guvf arrq sbe znxvat fragraprf.",
-					"Ubcvat sbe gur orfg, ohg rkcrpgvat gur jbefg",
-					"Gur jvyy gb tb ba jura V'z uheg qrrc vafvqr.",
-					"Vs vg oyrrqf, jr pna xvyy vg.",
-					"Ubhfgba, V unir n onq srryvat nobhg guvf zvffvba.",
-					"Znzn nyjnlf fnvq yvsr jnf yvxr n obk bs pubpbyngrf. Lbh arire xabj jung lbh'er tbaan trg.",
-					"Ol gur jnl, vf gurer nalbar ba obneq jub xabjf ubj gb syl n cynar?",
-					"Qnir, guvf pbairefngvba pna freir ab checbfr nalzber. Tbbqolr.",
-					"Vg pna bayl or nggevohgnoyr gb uhzna reebe.",
-					"Ybbxf yvxr V cvpxrq gur jebat jrrx gb dhvg fzbxvat.",
-					"Lbh uhznaf npg fb fgenatr. Rirelguvat lbh perngr vf hfrq gb qrfgebl.",
-					"Jurer qvq lbh yrnea ubj gb artbgvngr yvxr gung?",
-					"Fve, ner lbh pynffvsvrq nf uhzna?",
-					"Jr'er abg tbaan znxr vg, ner jr?",
-					"Vg'f va lbhe angher gb qrfgebl lbhefryirf.",
-					"Gur zber pbagnpg V unir jvgu uhznaf, gur zber V yrnea.",
-					"Jbhyq vg fnir lbh n ybg bs gvzr vs V whfg tnir hc naq jrag znq abj?",
-					"Ernyvgl vf serdhragyl vanpphengr.",
-					"Qba'g oryvrir nalguvat lbh ernq ba gur arg. Rkprcg guvf. Jryy, vapyhqvat guvf, V fhccbfr.",
-					"N phc bs grn jbhyq erfgber zl abeznyvgl.",
-					"Nalguvat gung guvaxf ybtvpnyyl pna or sbbyrq ol fbzrguvat ryfr gung guvaxf ng yrnfg nf ybtvpnyyl nf vg qbrf.",
-					"Va na vasvavgr Havirefr nalguvat pna unccra.",
-					"Fbzrgvzrf vs lbh erprvirq na nafjre, gur dhrfgvba zvtug or gnxra njnl.",
-					"Cyrnfr pnyy zr Rqqvr vs vg jvyy uryc lbh gb erynk.",
-					"V qba'g oryvrir vg. Cebir vg gb zr naq V fgvyy jba'g oryvrir vg.",
-					"Gbgnyyl znq, hggre abafrafr. Ohg jr'yy qb vg orpnhfr vg'f oevyyvnag abafrafr.",
-					"Guvf fragrapr vf abg gehr.",
-					"V jbhyq engure qvr fgnaqvat guna yvir ba zl xarrf.",
-					"Lbh ner orvat jngpurq.",
-					"Qvq lbh srrq gurz nsgre zvqavtug?",
-					"Ubj qb lbh rkcynva fpubby gb uvture vagryyvtrapr?",
-					"Crbcyr fbzrgvzrf znxr zvfgnxrf.",
-					"Ybbx, V qba'g unir gvzr sbe n pbairefngvba evtug abj.",
-					"Nyy ceboyrzf va pbzchgre fpvrapr pna or fbyirq ol nabgure yriry bs vaqverpgvba",
-					"...rkprcg sbe gur ceboyrz bs gbb znal yriryf bs vaqverpgvba",
-					"V xabj orpnhfr V ohvyg vg",
-					"Rira gur fznyyrfg crefba pna punatr gur pbhefr bs gur shgher.",
-					"Vs lbh ner n sevraq, lbh fcrnx gur cnffjbeq, naq gur qbbef jvyy bcra.",
-					"Lbh Funyy Abg Cnff",
-					"73.6% Bs Nyy Fgngvfgvpf Ner Znqr Hc",
-					"Jr pna arvgure pbasvez abe qral gung guvf vf penfuvat",
-					"Jura gur orngvat bs lbhe urneg rpubrf gur orngvat bs gur qehzf",
-					"Arire gehfg n pbzchgre lbh pna'g guebj bhg n jvaqbj",
-					"Lrnu, V'z pnyz. V'z n pnyz crefba. Vf gurer fbzr ernfba V fubhyqa'g or pnyz?",
-					"Rirelobql whfg fgnl pnyz. Gur fvghngvba vf haqre pbageby.",
-					"Uvccl, lbh guvax rirelguvat vf n pbafcvenpl.",
-					"Gurfr thlf ner nobhg nf zhpu sha nf n gnk nhqvg.",
-					"Gurer vf fbzrguvat qbja gurer! Fbzrguvat abg hf.",
-					"V fnj n tyvzcfr bs zl shgher naq rirelguvat'f punatrq sbe zr abj.",
-					"Va fcnpr ab bar pna urne lbh fpernz",
-					"V pna'g yvr gb lbh nobhg lbhe punaprf, ohg... lbh unir zl flzcnguvrf.",
-					"Gurer vf na rkcynangvba sbe guvf, lbh xabj.",
-					"V'z nsenvq V unir fbzr onq arjf.",
-					"Qb zr n snibhe. Qvfpbaarpg zr. V pbhyq or erjbexrq, ohg V'yy arire or gbc bs gur yvar ntnva.",
-					"Gnxr vg rnfl, qba'g chfu gur yvggyr ohggba ba gur wblfgvpx!",
-					"V'z n irel cevingr crefba.",
-					"Gb fphycg na ryrcunag sebz n ovt oybpx bs zneoyr, whfg xabpx njnl nyy gur ovgf gung qba'g ybbx yvxr na ryrcunag.",
-					"Jub fnvq lbh pbhyq gnyx gb zr? Unir V tbg fbzrguvat ba zl snpr ?",
-					"Jr'ir orra guebhtu jbefg",
-					"Havgrq jr fgnaq",
-					"Jr funyy arire fheeraqre",
-					"Nofbyhgr ubarfgl vfa'g nyjnlf gur zbfg qvcybzngvp abe gur fnsrfg sbez bs pbzzhavpngvba jvgu rzbgvbany orvatf.",
-					"Vg'f... pbzcyvpngrq.",
-					"Qb abg bcra hagvy 1985",
-					"V fgvyy zrff hc ohg V'yy whfg fgneg ntnva",
-					"V jba'g tvir hc, ab V jba'g tvir va; Gvyy V ernpu gur raq; Naq gura V'yy fgneg ntnva",
-					"V jnaan gel rira gubhtu V pbhyq snvy",
-					"Fbzrgvzrf jr pbzr ynfg ohg jr qvq bhe orfg",
-					"Vs lbh frr fbzrguvat, fnl fbzrguvat",
-					"Va gurbel gurer vf ab qvssrerapr orgjrra gurbel naq cenpgvpr. Ohg, va cenpgvpr, gurer vf.",
-					"Vs V pnaabg oevat lbh pbzsbeg gura ng yrnfg V oevat lbh ubcr",
-					"Jr nyy zhfg yrnea sebz fznyy zvfsbeghar, pbhag gur oyrffvatf gung ner erny",
-					"Cercner Guerr Frnyrq Rairybcrf...",
-					"Lbh xabj gung guvat lbh whfg qvq? Qba'g qb gung",
-					"Vg gbbx zr n ybat gvzr gb haqrefgnaq gung vs lbh jnag gb qb guvf wbo jryy lbh unir gb fgnl qrgnpurq.",
-					"Qb lbh yvxr lbhe zbeavat grn jrnx be fgebat ?",
-					"Jvagre vf pbzvat",
-					"Jung sbbyf gurfr zbegnyf or!",
-					"Fbzrguvat jvpxrq guvf jnl pbzrf.",
-					"V guvax V trg vg, jung jnf vg? Cbxre Avtug? Onpurybe Cnegl?",
-					"Vg'f nyevtug gb or fpnerq. Erzrzore, gurer vf ab pbhentr jvgubhg srne.",
-					"Guebhtu ernqvarff naq qvfpvcyvar jr ner znfgref bs bhe sngr.",
-					"Jvgu terng cbjre pbzrf terng erfcbafvovyvgl",
-					"Vs n znpuvar pna yrnea gur inyhr bs uhzna yvsr, znlor jr pna gbb ?",
-					"Bayl tbvat sbejneq 'pnhfr jr pna'g svaq erirefr.",
-					"Jr'er abg tbaan fvg va fvyrapr, jr'er abg tbaan yvir jvgu srne",
-					"Oba, qnaf qvk zvahgrf wr abhf pbafvqrer pbzzr qrsvavgvirzrag creqhf.",
-					"Pn fren fherzrag ovra dhnaq pn fren svav.",
-					"Vg'f gur ynfg cvrpr bs gur chmmyr ohg lbh whfg pna'g znxr vg svg",
-					"Qbpgbe fnlf lbh'er pherq ohg lbh fgvyy srry gur cnva",
-					"Vf negvsvpvny vagryyvtrapr gur rknpg bccbfvgr bs angheny fghcvqvgl ?",
-					"Sbeprzrag, pn qrcraq, pn qrcnffr...",
-					"Gurer'f orra n cnggrea bs vafhobeqvangr orunivbe erpragyl.",
-					"Ab. Jr ner abg na rssrpgvir grnz.",
-					"Bhe wbo vf abg gb erzrzore... erzrzore?",
-					"Guvf vf zvffvba pbageby. Ubj ner lbh nyy qbvat guvf ybiryl zbeavat?",
-					"Vs lbh pbhyq frr lbhe jubyr yvsr ynvq bhg va sebag bs lbh, jbhyq lbh punatr guvatf?",
-					"Vf guvf n aba-mreb-fhz tnzr?",
-					"Abj gung'f n cebcre vagebqhpgvba.",
-					"Rirelguvat unf punatrq naq vg jba'g fgbc punatvat nalgvzr fbba.",
-					"Jung znxrf lbh qvssrerag znxrf lbh qnatrebhf",
-					"Qviretrapr vf rkgerzryl qnatrebhf",
-					"V'z Qviretrag. Naq V pna'g or pbagebyyrq",
-					"Znl gur bqqf or rire va lbhe snibe",
-					"Ab WninFpevcg senzrjbexf jrer perngrq qhevat gur jevgvat bs guvf zrffntr.",
-					"P'rfg cerffr-cherr dhv g'nf vagreebtr ?",
-					"Ybbx, nygreangvir snpgf ner abg snpgf. Gurl'er snyfrubbqf",
-					"Guvf vf abg n penfu, guvf vf zber bs na nygreangvir erfhyg.",
-					"Lbh yrnearq gb cebtenz va SBEGENA qvqa'g lbh?",
-					"Guvf oht vf n srngher nf qrfpevorq ol gur znexrgvat qrcnegzrag.",
-					"Abg rirelobql haqrefgnaqf gur uhzbe bs cebtenzzref.",
-					"Vs lbh yvir na beqvanel yvsr, nyy lbh'yy unir ner beqvanel fgbevrf.",
-					"Pbzr jvgu zr vs lbh jnag gb yvir",
-					"Gh y'nf gebhir bh pryhv-yn ?",
-					"Qb lbh ernyyl guvax lbh unir n punapr ntnvafg hf, Ze. Pbjobl?",
-					"Nggragvba, jubrire lbh ner, guvf punaary vf erfreirq sbe rzretrapl pnyyf bayl.",
-					"Qbrf vg fbhaq yvxr V'z beqrevat n cvmmn? ",
-					"Jr'er tbaan arrq fbzr zber SOV thlf, V thrff.",
-					"Trg ernql sbe ehfu ubhe",
-					"V unir gb jnea lbh, V'ir urneq eryngvbafuvcf onfrq ba vagrafr rkcrevraprf arire jbex.",
-					"Nalguvat ryfr ohg gur onfrzrag gung'yy xrrc guvf ryringbe sebz snyyvat?",
-					"Vf guvf grfgvat jurgure V'z n ercyvpnag be n yrfovna, Ze. Qrpxneq? ",
-					"V'ir qbar... dhrfgvbanoyr guvatf",
-					"Jbhyq lbh... yvxr gb or hctenqrq?",
-					"Snhg erpbaanvger... p'rfg qh oehgny!",
-					"Fv ba oevpbynvg cyhf fbhirag, ba nhenvg zbvaf yn grgr nhk orgvfrf.",
-					"Wr invf yhv snver har beqbaanapr, rg har frirer...",
-					"Znvf vy pbaanvg cnf Enbhy, pr zrp! vy in nibve ha erirvy cravoyr.",
-					"W'nv ibhyh rger qvcybzngr n pnhfr qr ibhf gbhf, rivgre dhr yr fnat pbhyr.",
-					"Vtabenapr oevatf punbf, abg xabjyrqtr.",
-					"Yrneavat vf nyjnlf n cnvashy cebprff.",
-					"V'z fbeel, ner lbh sebz gur cnfg ?",
-					"Unir lbh gevrq gheavat vg bss naq ba ntnva ?",
-					"Vs lbh qba'g xabj jurer lbh ner tbvat nal ebnq pna gnxr lbh gurer",
-					"Xrrc pnyz naq cerff Pgey-Nyg-Qry",
-					"Vs lbh glcr Tbbtyr vagb Tbbtyr, lbh pna oernx gur Vagrearg.",
-					"V unir cneg bs n cyna.",
-					"V'z cerggl fher gur nafjre vf: V nz Tebbg.",
-					"Nalguvat gung pna cbffvoyl tb jebat, qbrf",
-					"Cyhf pn engr, cyhf ba n qr punapr dhr pn znepur",
-					"Fb V thrff gur sbeghar gryyre'f evtug...",
-					"Jura rirelguvat'f tbar jebat fbzrubj...",
-					"V qba'g jnag gb yvir ba guvf cynarg nalzber",
-					"Oba, p'rfg y'urher bh yrf fbhiravef fr enzrarag...",
-					"Fhpprff pbafvfgf bs tbvat sebz snvyher gb snvyher jvgubhg ybff bs raguhfvnfz",
-					"Vs lbh'er tbvat guebhtu uryy, xrrc tbvat",
-					"Jre xnzcsg, xnaa ireyvrera. Jre avpug xnzcsg, ung fpuba ireybera.",
-					"P'rfg nh cvrq qh zhe dhr y'ba ibvg yr zvrhk yr zhe.",
-					"Jr xabj ubj uhznaf jbex. Gurl ner nyy fb cerqvpgnoyr.",
-					"Pyrneyl, lbh unir arire zrg n jbzna.",
-					"Gur qbtznf bs gur dhvrg cnfg ner vanqrdhngr gb gur fgbezl cerfrag",
-					"Ab jnl gb cerirag guvf, fnlf bayl angvba jurer guvf erthyneyl unccraf",
-					"L'n dhrydhrf dhrfgvbaf dhv erfgrag fbhf fvyrapr...",
-					"Vs gurer vf ab fbyhgvba, gurer vf ab ceboyrz",
-					"V unir zrzbevrf, ohg V pna'g gryy vs gurl'er erny.",
-					"L n pbzzr ha tbhg nzre ra abhf",
-					"L n qrf fvyraprf dhv qvfrag ornhpbhc",
-					"V frr lbh'ir unq fbzr qvfpvcyvanel ceboyrzf va gur cnfg.",
-					"Cercnengvba vf gur xrl gb fhpprffshy, vapbafcvphbhf gvzr geniry.",
-					"Vg'f arire gbb yngr gb or jub lbh zvtug unir orra.",
-					"Rg cbhe nyyre purm Zvpxrl vyf ibag fherzrag cnf pbafgehver har tner gbhf yrf 100 zrgerf",
-					"Nyy lbhe onfr ner orybat gb hf",
-					"Znqr ba Rnegu ol uhznaf",
-					"Jvaaref Qba'g Hfr Qehtf",
-					"Lbh xabj jung fhecevfrq zr gur zbfg? Vg jnfa'g zrrgvat gurz. Vg jnf zrrgvat lbh.",
-					"Va jne gurer ner ab jvaaref, bayl jvqbjf",
-					"Vs lbh guvax guvf Havirefr vf onq, lbh fubhyq frr fbzr bs gur bguref",
-					"Cnp-Zna'f n onq thl?",
-					"Zl ernyvgl vf whfg qvssrerag guna lbhef",
-					"L'ra n dh'bag rffnlr, vyf bag rh qrf ceboyrzrf",
-					"Gb ree vf uhzna, ohg gb ernyyl sbhy guvatf hc erdhverf n pbzchgre.",
-					"Vs lbh oryvrir rirelguvat lbh ernq, lbh orggre abg ernq",
-					"Gurer vf ab ceboyrz fb onq lbh pna'g znxr vg jbefr",
-					"Pn p'rfg qh ybheq... Ha gehp qr znynqr.",
-					"V qb abg guvax, gung V guvax.. V guvax.",
-					"Gurer ner cynprf ybjre guna gur onfrzrag",
-					"Gurer ner 10 glcrf bs crbcyr: gubfr jub haqrefgnaq ovanel, naq gubfr jub qba'g.",
-					"Cyrnfr zvaq gur tnc orgjrra gur genva naq gur cyngsbez",
-					"Nfuvzbgb av tb-puhv xhqnfnv",
-					"Vs lbh'er erprvivat guvf genafzvffvba, znxr ab nggrzcg gb pbzr gb vgf cbvag bs bevtva.",
-					"Obl, qb V ungr orvat evtug nyy gur gvzr!",
-					"Jub funirf gur oneore jub funirf nyy gur zra jub qba'g funir gurzfryirf?",
-					"V haqrefgnaq uhzna rzbgvbaf, nygubhtu V qb abg srry gurz zlfrys.",
-					"Lbh qvqa'g fnl gur zntvp jbeq!",
-					"Gurer vf ab ceboyrz gung n ynpx bs fbyhgvba jba'g svanyyl erfbyir.",
-					"V unir n inthr srryvat bs vzcraqvat zvfsbeghar.",
-					"Jura lbh'er va gebhoyr, pbashfr rirelguvat",
-					"Jung gur uryy vf Cebwrpg Ryebaq?",
-					"Qba'g qvt hc gur ovt obk bs cyhgbavhz, Znex",
-					"Uryc vf bayl 140 zvyyvba zvyrf njnl.",
-					"P unf gur fcrrq naq rssvpvrapl bs nffrzoyl ynathntr pbzovarq jvgu ernqnovyvgl bs nffrzoyl ynathntr",
-					"Crey vf gur bayl ynathntr gung ybbxf gur fnzr orsber naq nsgre EFN rapelcgvba",
-					"Gur zber vg snvyf, gur zber yvxryl vg vf gung vg jvyy jbex",
-					"V ubcr V qvqa'g gnxr hc gbb zhpu bs lbhe gvzr", "Lbh'er tbaan arrq n ovttre obng",
-					"Dhnaq ibhf rgrf rzorgrf, rzoebhvyyrm gbhg", "Gurer nva'g ab phevat jung'f jebat jvgu gung guvat",
-					"Vs lbh cevpx hf, qb jr abg oyrrq?", "V qvq lbhe wbo bapr - V jnf tbbq ng vg.",
-					"Vyf cbheenvrag snver har fryrpgvba nh fgnaqneq...", "Gung'f ab jnl gb gerng n sevraq.",
-					"Ubjrire ornhgvshy gur fgengrtl, lbh fubhyq bppnfvbanyyl ybbx ng gur erfhygf",
-					"Qba'g svk vg vs vg'f abg oebxra",
-					"Fhqqrayl V'z gnxvat fhttrfgvbaf sebz fbzr fgebat-nez zna jvgu na VD bs zvahf 50.",
-					"Fur ehvaf rirelguvat fur'f va. Fur ehvaf guvatf fur'f abg rira va.",
-					"Byvir, V guvax lbh fubhyq xabj guvf: lbh'er n ubeevoyr npgerff.",
-					"Lbh'er yngr! Qb lbh unir ab pbaprcg bs gvzr ?",
-					"P'zba zna, yrg'f qb fbzrguvat gung ernyyl pbbxf.",
-					"Nh sbaq, znvagranag, yrf qvcybzngrf ceraqenvrag cyhgbg yr cnf fhe yrf ubzzrf q'npgvba.",
-					"Whfg zragnyyl gubhtu... ner lbh bxnl ?", "Jr xabj fur'f bxnl orpnhfr fur'f oybaq",
-					"Gh oyhssrf Znegbav", "Lbh thlf ner trggvat cnvq?",
-					"Gurer'f n erq guvatl zbivat gbjneqf gur terra guvatl... V guvax jr'er gur terra guvatl",
-					"Nyy flfgrzf ner shapgvbavat Pbzznaqre", "V erzrzore gung fbhaq! Gung'f n onq fbhaq!",
-					"V xabj vg'f uneq sbe lbh gb haqrefgnaq rira fubeg fragraprf",
-					"Gur havirefr vf shyy bs vagryyvtrag yvsr. Vg'f whfg orra gbb vagryyvtrag gb pbzr urer.",
-					"Uryyb, jbhyq lbh yvxr gb urne n GPC wbxr ?",
-					"Rirel snvel gnyr arrqf n tbbq byq-snfuvbarq ivyynva",
-					"Fvapr gurer'f ab pbapyhfvba gb gur cnenqbk gurbel, gurer'f ab jnl gb cebir gurer'f ab cnenqbk.",
-					"Gnxr zr guebhtu gur qnexarff gb gur oernx bs gur qnl",
-					"Vg znxrf gur gehgu rira zber vapbzcerurafvoyr orpnhfr rirelguvat vf arj",
-					"V qba'g xabj ubj ohg V fhqqrayl ybfr pbageby", "Gurer ner zbzragf jura V guvax V'z tbvat penml",
-					"Jbhyq gung vg jrer fb fvzcyr", "Pnyy gung n xavsr? Guvf vf n xavsr.",
-					"Fhe y'bprna qh grzcf dhv tyvffr...", "Snvyher serr bcrengvbaf erdhver rkcrevrapr jvgu snvyher.",
-					"Pngnfgebcur vf nyjnlf whfg nebhaq gur pbeare.",
-					"Orjner bs ohtf va gur nobir pbqr; V unir bayl cebirq vg pbeerpg, abg gevrq vg",
-					"V'z abg n erny cebtenzzre. V guebj gbtrgure guvatf hagvy vg jbexf gura V zbir ba",
-					"#qrsvar DHRFGVBA ((oo)||!(oo))", "Pbzchgref ner hfryrff. Gurl pna bayl tvir lbh nafjref",
-					"Gur Vagrearg? Vf gung guvat fgvyy nebhaq?",
-					"Va beqre gb haqrefgnaq erphefvba, bar zhfg svefg haqrefgnaq erphefvba");
+	static private final List<String> quotes = Arrays.asList("Ur'f qrnq, Wvz.",
+			"Ol Tenogune'f unzzre, ol gur fbaf bs Jbeina, lbh funyy or niratrq.",
+			"Ebnqf? Jurer jr'er tbvat, jr qba'g arrq ebnqf.", "Gur gvzr vf bhg bs wbvag.",
+			"P'rfg phevrhk purm yrf znevaf pr orfbva qr snver qrf cuenfrf.",
+			"V'z gnyxvat nobhg gur bgure Crgre, gur bar ba gur bgure fvqr.", "Znl gur Sbepr or jvgu lbh!",
+			"Arire tvir hc, arire fheeraqre...", "Unfgn yn ivfgn, onol.",
+			"Url, Qbp, jr orggre onpx hc. Jr qba'g unir rabhtu ebnq gb trg hc gb 88.",
+			"Terrgvatf, Cebsrffbe Snyxra. Funyy jr cynl n tnzr?", "V pna'g punatr gur ynj bs culfvpf!",
+			"N fgenatr tnzr. Gur bayl jvaavat zbir vf abg gb cynl.", "V'z gur Tngrxrrcre, ner lbh gur Xrlznfgre?",
+			"V nz gur Znfgre Pbageby Cebtenz. Ab bar Hfre jebgr zr.", "Yvsr? Qba'g gnyx gb zr nobhg yvsr.",
+			"V nyjnlf gubhtug fbzrguvat jnf shaqnzragnyyl jebat jvgu gur havirefr.",
+			"N ebobg znl abg vawher n uhzna orvat be, guebhtu vanpgvba, nyybj n uhzna orvat gb pbzr gb unez.",
+			"Fheeraqre znl or bhe bayl bcgvba.", "Fvk ol avar. Sbegl gjb.", "Vg'f yvsr, Wvz, ohg abg nf jr xabj vg.",
+			"Qba'g Cnavp!", "Jung qb lbh zrna? Na Nsevpna be Rhebcrna fjnyybj?",
+			"V arrq lbhe obbgf lbhe pybgurf naq lbhe zbgbeplpyr", "Lbh sbetbg gb fnl cyrnfr...",
+			"Lbh unir qvrq bs qlfragrel.", "Jbhyqa'g lbh cersre n avpr tnzr bs purff?",
+			"Jura lbh unir ryvzvangrq gur vzcbffvoyr, jungrire erznvaf, ubjrire vzcebonoyr, zhfg or gur gehgu.",
+			"V xabj abj jul lbh pel. Ohg vg'f fbzrguvat V pna arire qb.",
+			"Erfvfgnapr vf shgvyr. Lbh jvyy or nffvzvyngrq.", "Nalguvat qvssrerag vf tbbq.",
+			"Penpxrq ol Nyqb Erfrg naq Ynherag Ehrvy.", "V'z obgu. V'z n pryroevgl va na rzretrapl.",
+			"Qb lbh xabj guvf terng terng cbyvfu npgbe, Wbfrcu Ghen?", "Gb vasvavgl naq orlbaq!",
+			"Fcnpr: gur svany sebagvre...", "Fhe zba ovyyrg, grarm, l n rpevg Fnvag-Ynmner, p'rfg zrf lrhk bh dhbv ?",
+			"Gur obl vf vzcbegnag. Ur unf gb yvir.", "Bapr hcba n gvzr va n tnynkl sne, sne njnl...",
+			"Naq lbh xabj gurer'f n ybat ybat jnl nurnq bs lbh...", "Na nyyretl gb bkltra? Ryz oyvtug?",
+			"Ohg nybef lbh ner Serapu!", "A'nv-wr qbap gnag irph dhr cbhe prggr vasnzvr?",
+			"Fbzrguvat vf ebggra va gur Fgngr bs Qraznex.", "Url, jung qb lbh jnag? Zvenpyrf?",
+			"1.21 tvtnjnggf! 1.21 tvtnjnggf. Terng Fpbgg! ", "Jung gur uryy vf n tvtnjngg?", "V arrq n inpngvba.",
+			"Ba qrienvg wnznvf dhvggre Zbagnhona.", "Zl sbepr vf n cyngsbez gung lbh pna pyvzo ba...",
+			"Gurer'f fbzrguvat jrveq, naq vg qba'g ybbx tbbq...", "Rg evra ienvzrag ar punatr znvf gbhg rfg qvssrerag",
+			"Ornz zr hc, Fpbggl.", "Gurer vf ab fcbba.", "Sbyybj gur juvgr enoovg.",
+			"Arire fraq n uhzna gb qb n znpuvar'f wbo.", "Theh zrqvgngvba. Cerff yrsg zbhfr ohggba gb pbagvahr.",
+			"V qba'g guvax jr'er va Xnafnf nalzber.", "Yhxr, V nz lbhe sngure.", "Oybbq, Fjrng naq Grnef",
+			"Ubhfgba, jr unir n ceboyrz.", "Xrlobneq snvyher, cerff nal xrl gb pbagvahr", "Ovt zvfgnxr!",
+			"Ubj znal HZY qrfvtaref qbrf vg gnxr gb punatr n yvtugohyo ?", "Qb lbh yvxr zbivrf nobhg tynqvngbef ?",
+			"Gur fcvevg bs yrneavat vf n ynfgvat sebagvre.",
+			"Vg vf phevbhf sbe fnvybef guvf arrq sbe znxvat fragraprf.", "Ubcvat sbe gur orfg, ohg rkcrpgvat gur jbefg",
+			"Gur jvyy gb tb ba jura V'z uheg qrrc vafvqr.", "Vs vg oyrrqf, jr pna xvyy vg.",
+			"Ubhfgba, V unir n onq srryvat nobhg guvf zvffvba.",
+			"Znzn nyjnlf fnvq yvsr jnf yvxr n obk bs pubpbyngrf. Lbh arire xabj jung lbh'er tbaan trg.",
+			"Ol gur jnl, vf gurer nalbar ba obneq jub xabjf ubj gb syl n cynar?",
+			"Qnir, guvf pbairefngvba pna freir ab checbfr nalzber. Tbbqolr.",
+			"Vg pna bayl or nggevohgnoyr gb uhzna reebe.", "Ybbxf yvxr V cvpxrq gur jebat jrrx gb dhvg fzbxvat.",
+			"Lbh uhznaf npg fb fgenatr. Rirelguvat lbh perngr vf hfrq gb qrfgebl.",
+			"Jurer qvq lbh yrnea ubj gb artbgvngr yvxr gung?", "Fve, ner lbh pynffvsvrq nf uhzna?",
+			"Jr'er abg tbaan znxr vg, ner jr?", "Vg'f va lbhe angher gb qrfgebl lbhefryirf.",
+			"Gur zber pbagnpg V unir jvgu uhznaf, gur zber V yrnea.",
+			"Jbhyq vg fnir lbh n ybg bs gvzr vs V whfg tnir hc naq jrag znq abj?", "Ernyvgl vf serdhragyl vanpphengr.",
+			"Qba'g oryvrir nalguvat lbh ernq ba gur arg. Rkprcg guvf. Jryy, vapyhqvat guvf, V fhccbfr.",
+			"N phc bs grn jbhyq erfgber zl abeznyvgl.",
+			"Nalguvat gung guvaxf ybtvpnyyl pna or sbbyrq ol fbzrguvat ryfr gung guvaxf ng yrnfg nf ybtvpnyyl nf vg qbrf.",
+			"Va na vasvavgr Havirefr nalguvat pna unccra.",
+			"Fbzrgvzrf vs lbh erprvirq na nafjre, gur dhrfgvba zvtug or gnxra njnl.",
+			"Cyrnfr pnyy zr Rqqvr vs vg jvyy uryc lbh gb erynk.",
+			"V qba'g oryvrir vg. Cebir vg gb zr naq V fgvyy jba'g oryvrir vg.",
+			"Gbgnyyl znq, hggre abafrafr. Ohg jr'yy qb vg orpnhfr vg'f oevyyvnag abafrafr.",
+			"Guvf fragrapr vf abg gehr.", "V jbhyq engure qvr fgnaqvat guna yvir ba zl xarrf.",
+			"Lbh ner orvat jngpurq.", "Qvq lbh srrq gurz nsgre zvqavtug?",
+			"Ubj qb lbh rkcynva fpubby gb uvture vagryyvtrapr?", "Crbcyr fbzrgvzrf znxr zvfgnxrf.",
+			"Ybbx, V qba'g unir gvzr sbe n pbairefngvba evtug abj.",
+			"Nyy ceboyrzf va pbzchgre fpvrapr pna or fbyirq ol nabgure yriry bs vaqverpgvba",
+			"...rkprcg sbe gur ceboyrz bs gbb znal yriryf bs vaqverpgvba", "V xabj orpnhfr V ohvyg vg",
+			"Rira gur fznyyrfg crefba pna punatr gur pbhefr bs gur shgher.",
+			"Vs lbh ner n sevraq, lbh fcrnx gur cnffjbeq, naq gur qbbef jvyy bcra.", "Lbh Funyy Abg Cnff",
+			"73.6% Bs Nyy Fgngvfgvpf Ner Znqr Hc", "Jr pna arvgure pbasvez abe qral gung guvf vf penfuvat",
+			"Jura gur orngvat bs lbhe urneg rpubrf gur orngvat bs gur qehzf",
+			"Arire gehfg n pbzchgre lbh pna'g guebj bhg n jvaqbj",
+			"Lrnu, V'z pnyz. V'z n pnyz crefba. Vf gurer fbzr ernfba V fubhyqa'g or pnyz?",
+			"Rirelobql whfg fgnl pnyz. Gur fvghngvba vf haqre pbageby.", "Uvccl, lbh guvax rirelguvat vf n pbafcvenpl.",
+			"Gurfr thlf ner nobhg nf zhpu sha nf n gnk nhqvg.", "Gurer vf fbzrguvat qbja gurer! Fbzrguvat abg hf.",
+			"V fnj n tyvzcfr bs zl shgher naq rirelguvat'f punatrq sbe zr abj.", "Va fcnpr ab bar pna urne lbh fpernz",
+			"V pna'g yvr gb lbh nobhg lbhe punaprf, ohg... lbh unir zl flzcnguvrf.",
+			"Gurer vf na rkcynangvba sbe guvf, lbh xabj.", "V'z nsenvq V unir fbzr onq arjf.",
+			"Qb zr n snibhe. Qvfpbaarpg zr. V pbhyq or erjbexrq, ohg V'yy arire or gbc bs gur yvar ntnva.",
+			"Gnxr vg rnfl, qba'g chfu gur yvggyr ohggba ba gur wblfgvpx!", "V'z n irel cevingr crefba.",
+			"Gb fphycg na ryrcunag sebz n ovt oybpx bs zneoyr, whfg xabpx njnl nyy gur ovgf gung qba'g ybbx yvxr na ryrcunag.",
+			"Jub fnvq lbh pbhyq gnyx gb zr? Unir V tbg fbzrguvat ba zl snpr ?", "Jr'ir orra guebhtu jbefg",
+			"Havgrq jr fgnaq", "Jr funyy arire fheeraqre",
+			"Nofbyhgr ubarfgl vfa'g nyjnlf gur zbfg qvcybzngvp abe gur fnsrfg sbez bs pbzzhavpngvba jvgu rzbgvbany orvatf.",
+			"Vg'f... pbzcyvpngrq.", "Qb abg bcra hagvy 1985", "V fgvyy zrff hc ohg V'yy whfg fgneg ntnva",
+			"V jba'g tvir hc, ab V jba'g tvir va; Gvyy V ernpu gur raq; Naq gura V'yy fgneg ntnva",
+			"V jnaan gel rira gubhtu V pbhyq snvy", "Fbzrgvzrf jr pbzr ynfg ohg jr qvq bhe orfg",
+			"Vs lbh frr fbzrguvat, fnl fbzrguvat",
+			"Va gurbel gurer vf ab qvssrerapr orgjrra gurbel naq cenpgvpr. Ohg, va cenpgvpr, gurer vf.",
+			"Vs V pnaabg oevat lbh pbzsbeg gura ng yrnfg V oevat lbh ubcr",
+			"Jr nyy zhfg yrnea sebz fznyy zvfsbeghar, pbhag gur oyrffvatf gung ner erny",
+			"Cercner Guerr Frnyrq Rairybcrf...", "Lbh xabj gung guvat lbh whfg qvq? Qba'g qb gung",
+			"Vg gbbx zr n ybat gvzr gb haqrefgnaq gung vs lbh jnag gb qb guvf wbo jryy lbh unir gb fgnl qrgnpurq.",
+			"Qb lbh yvxr lbhe zbeavat grn jrnx be fgebat ?", "Jvagre vf pbzvat", "Jung sbbyf gurfr zbegnyf or!",
+			"Fbzrguvat jvpxrq guvf jnl pbzrf.", "V guvax V trg vg, jung jnf vg? Cbxre Avtug? Onpurybe Cnegl?",
+			"Vg'f nyevtug gb or fpnerq. Erzrzore, gurer vf ab pbhentr jvgubhg srne.",
+			"Guebhtu ernqvarff naq qvfpvcyvar jr ner znfgref bs bhe sngr.",
+			"Jvgu terng cbjre pbzrf terng erfcbafvovyvgl",
+			"Vs n znpuvar pna yrnea gur inyhr bs uhzna yvsr, znlor jr pna gbb ?",
+			"Bayl tbvat sbejneq 'pnhfr jr pna'g svaq erirefr.",
+			"Jr'er abg tbaan fvg va fvyrapr, jr'er abg tbaan yvir jvgu srne",
+			"Oba, qnaf qvk zvahgrf wr abhf pbafvqrer pbzzr qrsvavgvirzrag creqhf.",
+			"Pn fren fherzrag ovra dhnaq pn fren svav.",
+			"Vg'f gur ynfg cvrpr bs gur chmmyr ohg lbh whfg pna'g znxr vg svg",
+			"Qbpgbe fnlf lbh'er pherq ohg lbh fgvyy srry gur cnva",
+			"Vf negvsvpvny vagryyvtrapr gur rknpg bccbfvgr bs angheny fghcvqvgl ?",
+			"Sbeprzrag, pn qrcraq, pn qrcnffr...", "Gurer'f orra n cnggrea bs vafhobeqvangr orunivbe erpragyl.",
+			"Ab. Jr ner abg na rssrpgvir grnz.", "Bhe wbo vf abg gb erzrzore... erzrzore?",
+			"Guvf vf zvffvba pbageby. Ubj ner lbh nyy qbvat guvf ybiryl zbeavat?",
+			"Vs lbh pbhyq frr lbhe jubyr yvsr ynvq bhg va sebag bs lbh, jbhyq lbh punatr guvatf?",
+			"Vf guvf n aba-mreb-fhz tnzr?", "Abj gung'f n cebcre vagebqhpgvba.",
+			"Rirelguvat unf punatrq naq vg jba'g fgbc punatvat nalgvzr fbba.",
+			"Jung znxrf lbh qvssrerag znxrf lbh qnatrebhf", "Qviretrapr vf rkgerzryl qnatrebhf",
+			"V'z Qviretrag. Naq V pna'g or pbagebyyrq", "Znl gur bqqf or rire va lbhe snibe",
+			"Ab WninFpevcg senzrjbexf jrer perngrq qhevat gur jevgvat bs guvf zrffntr.",
+			"P'rfg cerffr-cherr dhv g'nf vagreebtr ?", "Ybbx, nygreangvir snpgf ner abg snpgf. Gurl'er snyfrubbqf",
+			"Guvf vf abg n penfu, guvf vf zber bs na nygreangvir erfhyg.",
+			"Lbh yrnearq gb cebtenz va SBEGENA qvqa'g lbh?",
+			"Guvf oht vf n srngher nf qrfpevorq ol gur znexrgvat qrcnegzrag.",
+			"Abg rirelobql haqrefgnaqf gur uhzbe bs cebtenzzref.",
+			"Vs lbh yvir na beqvanel yvsr, nyy lbh'yy unir ner beqvanel fgbevrf.", "Pbzr jvgu zr vs lbh jnag gb yvir",
+			"Gh y'nf gebhir bh pryhv-yn ?", "Qb lbh ernyyl guvax lbh unir n punapr ntnvafg hf, Ze. Pbjobl?",
+			"Nggragvba, jubrire lbh ner, guvf punaary vf erfreirq sbe rzretrapl pnyyf bayl.",
+			"Qbrf vg fbhaq yvxr V'z beqrevat n cvmmn? ", "Jr'er tbaan arrq fbzr zber SOV thlf, V thrff.",
+			"Trg ernql sbe ehfu ubhe",
+			"V unir gb jnea lbh, V'ir urneq eryngvbafuvcf onfrq ba vagrafr rkcrevraprf arire jbex.",
+			"Nalguvat ryfr ohg gur onfrzrag gung'yy xrrc guvf ryringbe sebz snyyvat?",
+			"Vf guvf grfgvat jurgure V'z n ercyvpnag be n yrfovna, Ze. Qrpxneq? ", "V'ir qbar... dhrfgvbanoyr guvatf",
+			"Jbhyq lbh... yvxr gb or hctenqrq?", "Snhg erpbaanvger... p'rfg qh oehgny!",
+			"Fv ba oevpbynvg cyhf fbhirag, ba nhenvg zbvaf yn grgr nhk orgvfrf.",
+			"Wr invf yhv snver har beqbaanapr, rg har frirer...",
+			"Znvf vy pbaanvg cnf Enbhy, pr zrp! vy in nibve ha erirvy cravoyr.",
+			"W'nv ibhyh rger qvcybzngr n pnhfr qr ibhf gbhf, rivgre dhr yr fnat pbhyr.",
+			"Vtabenapr oevatf punbf, abg xabjyrqtr.", "Yrneavat vf nyjnlf n cnvashy cebprff.",
+			"V'z fbeel, ner lbh sebz gur cnfg ?", "Unir lbh gevrq gheavat vg bss naq ba ntnva ?",
+			"Vs lbh qba'g xabj jurer lbh ner tbvat nal ebnq pna gnxr lbh gurer", "Xrrc pnyz naq cerff Pgey-Nyg-Qry",
+			"Vs lbh glcr Tbbtyr vagb Tbbtyr, lbh pna oernx gur Vagrearg.", "V unir cneg bs n cyna.",
+			"V'z cerggl fher gur nafjre vf: V nz Tebbg.", "Nalguvat gung pna cbffvoyl tb jebat, qbrf",
+			"Cyhf pn engr, cyhf ba n qr punapr dhr pn znepur", "Fb V thrff gur sbeghar gryyre'f evtug...",
+			"Jura rirelguvat'f tbar jebat fbzrubj...", "V qba'g jnag gb yvir ba guvf cynarg nalzber",
+			"Oba, p'rfg y'urher bh yrf fbhiravef fr enzrarag...",
+			"Fhpprff pbafvfgf bs tbvat sebz snvyher gb snvyher jvgubhg ybff bs raguhfvnfz",
+			"Vs lbh'er tbvat guebhtu uryy, xrrc tbvat",
+			"Jre xnzcsg, xnaa ireyvrera. Jre avpug xnzcsg, ung fpuba ireybera.",
+			"P'rfg nh cvrq qh zhe dhr y'ba ibvg yr zvrhk yr zhe.",
+			"Jr xabj ubj uhznaf jbex. Gurl ner nyy fb cerqvpgnoyr.", "Pyrneyl, lbh unir arire zrg n jbzna.",
+			"Gur qbtznf bs gur dhvrg cnfg ner vanqrdhngr gb gur fgbezl cerfrag",
+			"Ab jnl gb cerirag guvf, fnlf bayl angvba jurer guvf erthyneyl unccraf",
+			"L'n dhrydhrf dhrfgvbaf dhv erfgrag fbhf fvyrapr...", "Vs gurer vf ab fbyhgvba, gurer vf ab ceboyrz",
+			"V unir zrzbevrf, ohg V pna'g gryy vs gurl'er erny.", "L n pbzzr ha tbhg nzre ra abhf",
+			"L n qrf fvyraprf dhv qvfrag ornhpbhc", "V frr lbh'ir unq fbzr qvfpvcyvanel ceboyrzf va gur cnfg.",
+			"Cercnengvba vf gur xrl gb fhpprffshy, vapbafcvphbhf gvzr geniry.",
+			"Vg'f arire gbb yngr gb or jub lbh zvtug unir orra.",
+			"Rg cbhe nyyre purm Zvpxrl vyf ibag fherzrag cnf pbafgehver har tner gbhf yrf 100 zrgerf",
+			"Nyy lbhe onfr ner orybat gb hf", "Znqr ba Rnegu ol uhznaf", "Jvaaref Qba'g Hfr Qehtf",
+			"Lbh xabj jung fhecevfrq zr gur zbfg? Vg jnfa'g zrrgvat gurz. Vg jnf zrrgvat lbh.",
+			"Va jne gurer ner ab jvaaref, bayl jvqbjf",
+			"Vs lbh guvax guvf Havirefr vf onq, lbh fubhyq frr fbzr bs gur bguref", "Cnp-Zna'f n onq thl?",
+			"Zl ernyvgl vf whfg qvssrerag guna lbhef", "L'ra n dh'bag rffnlr, vyf bag rh qrf ceboyrzrf",
+			"Gb ree vf uhzna, ohg gb ernyyl sbhy guvatf hc erdhverf n pbzchgre.",
+			"Vs lbh oryvrir rirelguvat lbh ernq, lbh orggre abg ernq",
+			"Gurer vf ab ceboyrz fb onq lbh pna'g znxr vg jbefr", "Pn p'rfg qh ybheq... Ha gehp qr znynqr.",
+			"V qb abg guvax, gung V guvax.. V guvax.", "Gurer ner cynprf ybjre guna gur onfrzrag",
+			"Gurer ner 10 glcrf bs crbcyr: gubfr jub haqrefgnaq ovanel, naq gubfr jub qba'g.",
+			"Cyrnfr zvaq gur tnc orgjrra gur genva naq gur cyngsbez", "Nfuvzbgb av tb-puhv xhqnfnv",
+			"Vs lbh'er erprvivat guvf genafzvffvba, znxr ab nggrzcg gb pbzr gb vgf cbvag bs bevtva.",
+			"Obl, qb V ungr orvat evtug nyy gur gvzr!",
+			"Jub funirf gur oneore jub funirf nyy gur zra jub qba'g funir gurzfryirf?",
+			"V haqrefgnaq uhzna rzbgvbaf, nygubhtu V qb abg srry gurz zlfrys.", "Lbh qvqa'g fnl gur zntvp jbeq!",
+			"Gurer vf ab ceboyrz gung n ynpx bs fbyhgvba jba'g svanyyl erfbyir.",
+			"V unir n inthr srryvat bs vzcraqvat zvfsbeghar.", "Jura lbh'er va gebhoyr, pbashfr rirelguvat",
+			"Jung gur uryy vf Cebwrpg Ryebaq?", "Qba'g qvt hc gur ovt obk bs cyhgbavhz, Znex",
+			"Uryc vf bayl 140 zvyyvba zvyrf njnl.",
+			"P unf gur fcrrq naq rssvpvrapl bs nffrzoyl ynathntr pbzovarq jvgu ernqnovyvgl bs nffrzoyl ynathntr",
+			"Crey vf gur bayl ynathntr gung ybbxf gur fnzr orsber naq nsgre EFN rapelcgvba",
+			"Gur zber vg snvyf, gur zber yvxryl vg vf gung vg jvyy jbex",
+			"V ubcr V qvqa'g gnxr hc gbb zhpu bs lbhe gvzr", "Lbh'er tbaan arrq n ovttre obng",
+			"Dhnaq ibhf rgrf rzorgrf, rzoebhvyyrm gbhg", "Gurer nva'g ab phevat jung'f jebat jvgu gung guvat",
+			"Vs lbh cevpx hf, qb jr abg oyrrq?", "V qvq lbhe wbo bapr - V jnf tbbq ng vg.",
+			"Vyf cbheenvrag snver har fryrpgvba nh fgnaqneq...", "Gung'f ab jnl gb gerng n sevraq.",
+			"Ubjrire ornhgvshy gur fgengrtl, lbh fubhyq bppnfvbanyyl ybbx ng gur erfhygf",
+			"Qba'g svk vg vs vg'f abg oebxra",
+			"Fhqqrayl V'z gnxvat fhttrfgvbaf sebz fbzr fgebat-nez zna jvgu na VD bs zvahf 50.",
+			"Fur ehvaf rirelguvat fur'f va. Fur ehvaf guvatf fur'f abg rira va.",
+			"Byvir, V guvax lbh fubhyq xabj guvf: lbh'er n ubeevoyr npgerff.",
+			"Lbh'er yngr! Qb lbh unir ab pbaprcg bs gvzr ?", "P'zba zna, yrg'f qb fbzrguvat gung ernyyl pbbxf.",
+			"Nh sbaq, znvagranag, yrf qvcybzngrf ceraqenvrag cyhgbg yr cnf fhe yrf ubzzrf q'npgvba.",
+			"Whfg zragnyyl gubhtu... ner lbh bxnl ?", "Jr xabj fur'f bxnl orpnhfr fur'f oybaq", "Gh oyhssrf Znegbav",
+			"Lbh thlf ner trggvat cnvq?",
+			"Gurer'f n erq guvatl zbivat gbjneqf gur terra guvatl... V guvax jr'er gur terra guvatl",
+			"Nyy flfgrzf ner shapgvbavat Pbzznaqre", "V erzrzore gung fbhaq! Gung'f n onq fbhaq!",
+			"V xabj vg'f uneq sbe lbh gb haqrefgnaq rira fubeg fragraprf",
+			"Gur havirefr vf shyy bs vagryyvtrag yvsr. Vg'f whfg orra gbb vagryyvtrag gb pbzr urer.",
+			"Uryyb, jbhyq lbh yvxr gb urne n GPC wbxr ?", "Rirel snvel gnyr arrqf n tbbq byq-snfuvbarq ivyynva",
+			"Fvapr gurer'f ab pbapyhfvba gb gur cnenqbk gurbel, gurer'f ab jnl gb cebir gurer'f ab cnenqbk.",
+			"Gnxr zr guebhtu gur qnexarff gb gur oernx bs gur qnl",
+			"Vg znxrf gur gehgu rira zber vapbzcerurafvoyr orpnhfr rirelguvat vf arj",
+			"V qba'g xabj ubj ohg V fhqqrayl ybfr pbageby", "Gurer ner zbzragf jura V guvax V'z tbvat penml",
+			"Jbhyq gung vg jrer fb fvzcyr", "Pnyy gung n xavsr? Guvf vf n xavsr.", "Fhe y'bprna qh grzcf dhv tyvffr...",
+			"Snvyher serr bcrengvbaf erdhver rkcrevrapr jvgu snvyher.", "Pngnfgebcur vf nyjnlf whfg nebhaq gur pbeare.",
+			"Orjner bs ohtf va gur nobir pbqr; V unir bayl cebirq vg pbeerpg, abg gevrq vg",
+			"V'z abg n erny cebtenzzre. V guebj gbtrgure guvatf hagvy vg jbexf gura V zbir ba",
+			"#qrsvar DHRFGVBA ((oo)||!(oo))", "Pbzchgref ner hfryrff. Gurl pna bayl tvir lbh nafjref",
+			"Gur Vagrearg? Vf gung guvat fgvyy nebhaq?",
+			"Va beqre gb haqrefgnaq erphefvba, bar zhfg svefg haqrefgnaq erphefvba",
+			"Obhagl Uhagvat vf n pbzcyvpngrq cebsrffvba.",
+			"Znahsnpghere'f cebgbpby qvpgngrf V pna abg or pncgherq. V zhfg frys-qrfgehpg.", "Snvyher vf abg na bcgvba",
+			"Rirelguvat snvyf nyy gur gvzr", "Gur orfg jnl gb nibvq snvyher vf gb snvy pbafgnagyl",
+			"Cerzngher bcgvzvmngvba vf gur ebbg bs nyy rivy", "Chgnva, w'ra nv zneer q'nibve gbhwbhef envfba");
 
 	private QuoteUtils() {
 	}
diff --git a/src/net/sourceforge/plantuml/graphic/TextBlockUtils.java b/src/net/sourceforge/plantuml/graphic/TextBlockUtils.java
index 8297b19d5..e905f01cb 100644
--- a/src/net/sourceforge/plantuml/graphic/TextBlockUtils.java
+++ b/src/net/sourceforge/plantuml/graphic/TextBlockUtils.java
@@ -106,7 +106,8 @@ public class TextBlockUtils {
 		return new TextBlockMarged(textBlock, marginX1, marginX2, marginY1, marginY2);
 	}
 
-	public static TextBlock withMinWidth(TextBlock textBlock, double minWidth, HorizontalAlignment horizontalAlignment) {
+	public static TextBlock withMinWidth(TextBlock textBlock, double minWidth,
+			HorizontalAlignment horizontalAlignment) {
 		return new TextBlockMinWidth(textBlock, minWidth, horizontalAlignment);
 	}
 
@@ -125,6 +126,10 @@ public class TextBlockUtils {
 		return new PositionableImpl(pt, textBlock.calculateDimension(stringBounder));
 	}
 
+	public static Positionable asPositionable(Dimension2D dim, StringBounder stringBounder, Point2D pt) {
+		return new PositionableImpl(pt, dim);
+	}
+
 	public static TextBlock mergeLR(TextBlock b1, TextBlock b2, VerticalAlignment verticallAlignment) {
 		return new TextBlockHorizontal(b1, b2, verticallAlignment);
 	}
@@ -133,7 +138,8 @@ public class TextBlockUtils {
 		return new TextBlockVertical2(b1, b2, horizontalAlignment);
 	}
 
-	// public static TextBlockBackcolored mergeColoredTB(TextBlockBackcolored b1, TextBlockBackcolored b2,
+	// public static TextBlockBackcolored mergeColoredTB(TextBlockBackcolored b1,
+	// TextBlockBackcolored b2,
 	// HorizontalAlignment horizontalAlignment) {
 	// return addBackcolor(mergeTB(b1, b2, horizontalAlignment), b1.getBackcolor());
 	// }
diff --git a/src/net/sourceforge/plantuml/jdot/CucaDiagramFileMakerJDot.java b/src/net/sourceforge/plantuml/jdot/CucaDiagramFileMakerJDot.java
index 19595dfb2..c5fde0062 100644
--- a/src/net/sourceforge/plantuml/jdot/CucaDiagramFileMakerJDot.java
+++ b/src/net/sourceforge/plantuml/jdot/CucaDiagramFileMakerJDot.java
@@ -83,6 +83,7 @@ import net.sourceforge.plantuml.cucadiagram.Link;
 import net.sourceforge.plantuml.cucadiagram.Member;
 import net.sourceforge.plantuml.cucadiagram.MethodsOrFieldsArea;
 import net.sourceforge.plantuml.cucadiagram.Stereotype;
+import net.sourceforge.plantuml.cucadiagram.entity.EntityFactory;
 import net.sourceforge.plantuml.graphic.FontConfiguration;
 import net.sourceforge.plantuml.graphic.HorizontalAlignment;
 import net.sourceforge.plantuml.graphic.HtmlColor;
@@ -102,7 +103,7 @@ import net.sourceforge.plantuml.svek.DotStringFactory;
 import net.sourceforge.plantuml.svek.GeneralImageBuilder;
 import net.sourceforge.plantuml.svek.GraphvizCrash;
 import net.sourceforge.plantuml.svek.IEntityImage;
-import net.sourceforge.plantuml.svek.Shape;
+import net.sourceforge.plantuml.svek.Node;
 import net.sourceforge.plantuml.ugraphic.ImageBuilder;
 import net.sourceforge.plantuml.ugraphic.UGraphic;
 import net.sourceforge.plantuml.ugraphic.UStroke;
@@ -121,7 +122,6 @@ public class CucaDiagramFileMakerJDot implements CucaDiagramFileMaker {
 	private final Map<ILeaf, ST_Agnode_s> nodes = new LinkedHashMap<ILeaf, ST_Agnode_s>();
 	private final Map<Link, ST_Agedge_s> edges = new LinkedHashMap<Link, ST_Agedge_s>();
 	private final Map<IGroup, ST_Agraph_s> clusters = new LinkedHashMap<IGroup, ST_Agraph_s>();
-	private Map<IGroup, ILeaf> emptyGroups = new HashMap<IGroup, ILeaf>();
 
 	private final DotStringFactory dotStringFactory;
 
@@ -141,11 +141,11 @@ public class CucaDiagramFileMakerJDot implements CucaDiagramFileMaker {
 
 			for (Map.Entry<ILeaf, ST_Agnode_s> ent : nodes.entrySet()) {
 				final ILeaf leaf = ent.getKey();
-				final ST_Agnode_s node = ent.getValue();
-				final Point2D corner = getCorner(node);
+				final ST_Agnode_s agnode = ent.getValue();
+				final Point2D corner = getCorner(agnode);
 
-				final Shape shape = dotStringFactory.getBibliotekon().getShape(leaf);
-				final IEntityImage image = shape.getImage();
+				final Node node = dotStringFactory.getBibliotekon().getNode(leaf);
+				final IEntityImage image = node.getImage();
 				image.drawU(ug.apply(new UTranslate(corner)));
 			}
 
@@ -199,7 +199,8 @@ public class CucaDiagramFileMakerJDot implements CucaDiagramFileMaker {
 		final Cluster cluster = dotStringFactory.getBibliotekon().getCluster(group);
 		cluster.setPosition(llx, lly, urx, ury);
 		JUtils.LOG2("cluster=" + cluster);
-		// ug.apply(new UTranslate(llx, lly)).apply(new UChangeColor(HtmlColorUtils.BLUE))
+		// ug.apply(new UTranslate(llx, lly)).apply(new
+		// UChangeColor(HtmlColorUtils.BLUE))
 		// .draw(new URectangle(urx - llx, ury - lly));
 		cluster.drawU(ug, new UStroke(1.5), diagram.getUmlDiagramType(), diagram.getSkinParam());
 	}
@@ -210,21 +211,9 @@ public class CucaDiagramFileMakerJDot implements CucaDiagramFileMaker {
 				continue;
 			}
 			if (diagram.isEmpty(g) && g.getGroupType() == GroupType.PACKAGE) {
-				final ILeaf folder = diagram.getEntityFactory().createLeaf(g.getIdent(), g.getCode(), g.getDisplay(),
-						LeafType.EMPTY_PACKAGE, g.getParentContainer(), null, diagram.getNamespaceSeparator());
-				emptyGroups.put(g, folder);
-				final USymbol symbol = g.getUSymbol();
-				folder.setUSymbol(symbol);
-				folder.setStereotype(g.getStereotype());
-				if (g.getColors(diagram.getSkinParam()).getColor(ColorType.BACK) == null) {
-					final ColorParam param = symbol == null ? ColorParam.packageBackground : symbol.getColorParamBack();
-					final HtmlColor c1 = diagram.getSkinParam().getHtmlColor(param, g.getStereotype(), false);
-					folder.setSpecificColorTOBEREMOVED(ColorType.BACK, c1 == null ? diagram.getSkinParam()
-							.getBackgroundColor() : c1);
-				} else {
-					folder.setSpecificColorTOBEREMOVED(ColorType.BACK,
-							g.getColors(diagram.getSkinParam()).getColor(ColorType.BACK));
-				}
+				final ISkinParam skinParam = diagram.getSkinParam();
+				final EntityFactory entityFactory = diagram.getEntityFactory();
+				final ILeaf folder = entityFactory.createLeafForEmptyGroup(g, skinParam);
 				printEntityNew(folder);
 			} else {
 				printGroup(g);
@@ -261,10 +250,11 @@ public class CucaDiagramFileMakerJDot implements CucaDiagramFileMaker {
 			final int suppWidthBecauseOfShape = uSymbol == null ? 0 : uSymbol.suppWidthBecauseOfShape();
 
 			titleAndAttributeWidth = (int) Math.max(dimLabel.getWidth(), attributeWidth) + suppWidthBecauseOfShape;
-			titleAndAttributeHeight = (int) (dimLabel.getHeight() + attributeHeight + marginForFields + suppHeightBecauseOfShape);
+			titleAndAttributeHeight = (int) (dimLabel.getHeight() + attributeHeight + marginForFields
+					+ suppHeightBecauseOfShape);
 		}
 
-		dotStringFactory.openCluster(g, titleAndAttributeWidth, titleAndAttributeHeight, title, stereo);
+		dotStringFactory.openCluster(titleAndAttributeWidth, titleAndAttributeHeight, title, stereo, g);
 		this.printEntities(g.getLeafsDirect());
 
 		printGroups(g);
@@ -291,16 +281,16 @@ public class CucaDiagramFileMakerJDot implements CucaDiagramFileMaker {
 	}
 
 	private void exportEntity(ST_Agraph_s g, ILeaf leaf) {
-		final Shape shape = dotStringFactory.getBibliotekon().getShape(leaf);
+		final Node node = dotStringFactory.getBibliotekon().getNode(leaf);
 		// System.err.println("exportEntity " + leaf);
-		final ST_Agnode_s node = agnode(g, new CString(shape.getUid()), true);
-		agsafeset(node, new CString("shape"), new CString("box"), new CString(""));
-		final String width = "" + (shape.getWidth() / 72);
-		final String height = "" + (shape.getHeight() / 72);
-		agsafeset(node, new CString("width"), new CString(width), new CString(""));
-		agsafeset(node, new CString("height"), new CString(height), new CString(""));
+		final ST_Agnode_s agnode = agnode(g, new CString(node.getUid()), true);
+		agsafeset(agnode, new CString("shape"), new CString("box"), new CString(""));
+		final String width = "" + (node.getWidth() / 72);
+		final String height = "" + (node.getHeight() / 72);
+		agsafeset(agnode, new CString("width"), new CString(width), new CString(""));
+		agsafeset(agnode, new CString("height"), new CString(height), new CString(""));
 		// System.err.println("NODE " + leaf.getUid() + " " + width + " " + height);
-		nodes.put(leaf, node);
+		nodes.put(leaf, agnode);
 	}
 
 	private void printEntity(ILeaf ent) {
@@ -308,12 +298,8 @@ public class CucaDiagramFileMakerJDot implements CucaDiagramFileMaker {
 			throw new IllegalStateException();
 		}
 		final IEntityImage image = printEntityInternal(ent);
-		final Dimension2D dim = image.calculateDimension(stringBounder);
-		final Shape shape = new Shape(image, image.getShapeType(), dim.getWidth(), dim.getHeight(),
-				dotStringFactory.getColorSequence(), ent.isTop(), image.getShield(stringBounder),
-				ent.getEntityPosition());
-		dotStringFactory.addShape(shape);
-		getBibliotekon().putShape(ent, shape);
+		final Node node = getBibliotekon().createNode(ent, image, dotStringFactory.getColorSequence(), stringBounder);
+		dotStringFactory.addNode(node);
 	}
 
 	private TextBlock getTitleBlock(IGroup g) {
@@ -361,15 +347,15 @@ public class CucaDiagramFileMakerJDot implements CucaDiagramFileMaker {
 	}
 
 	private void printCluster(ST_Agraph_s g, Cluster cluster) {
-		for (Shape shape : cluster.getShapes()) {
-			final ST_Agnode_s node = agnode(g, new CString(shape.getUid()), true);
-			agsafeset(node, new CString("shape"), new CString("box"), new CString(""));
-			final String width = "" + (shape.getWidth() / 72);
-			final String height = "" + (shape.getHeight() / 72);
-			agsafeset(node, new CString("width"), new CString(width), new CString(""));
-			agsafeset(node, new CString("height"), new CString(height), new CString(""));
-			final ILeaf leaf = dotStringFactory.getBibliotekon().getLeaf(shape);
-			nodes.put(leaf, node);
+		for (Node node : cluster.getNodes()) {
+			final ST_Agnode_s agnode = agnode(g, new CString(node.getUid()), true);
+			agsafeset(agnode, new CString("shape"), new CString("box"), new CString(""));
+			final String width = "" + (node.getWidth() / 72);
+			final String height = "" + (node.getHeight() / 72);
+			agsafeset(agnode, new CString("width"), new CString(width), new CString(""));
+			agsafeset(agnode, new CString("height"), new CString(height), new CString(""));
+			final ILeaf leaf = dotStringFactory.getBibliotekon().getLeaf(node);
+			nodes.put(leaf, agnode);
 		}
 
 	}
@@ -411,7 +397,8 @@ public class CucaDiagramFileMakerJDot implements CucaDiagramFileMaker {
 			// agsafeset(node, new CString("height"), new CString(height), new CString(""));
 			// nodes.put(leaf, node);
 			// // System.err
-			// // .println("NODE " + leaf.getUid() + " [shape=box, width=" + width + ", height=" + height + "]");
+			// // .println("NODE " + leaf.getUid() + " [shape=box, width=" + width + ",
+			// height=" + height + "]");
 			// }
 			//
 			for (Link link : diagram.getLinks()) {
@@ -460,7 +447,8 @@ public class CucaDiagramFileMakerJDot implements CucaDiagramFileMaker {
 				continue;
 			}
 			if (diagram.isEmpty(g) && g.getGroupType() == GroupType.PACKAGE) {
-				final ILeaf folder = emptyGroups.get(g);
+				final EntityFactory entityFactory = diagram.getEntityFactory();
+				final ILeaf folder = entityFactory.getLeafForEmptyGroup(g);
 				exportEntity(graph, folder);
 			} else {
 				exportGroup(graph, g);
@@ -476,8 +464,8 @@ public class CucaDiagramFileMakerJDot implements CucaDiagramFileMaker {
 		if (cluster.isLabel()) {
 			final double width = cluster.getTitleAndAttributeWidth();
 			final double height = cluster.getTitleAndAttributeHeight() - 5;
-			agsafeset(cluster1, new CString("label"),
-					Macro.createHackInitDimensionFromLabel((int) width, (int) height), new CString(""));
+			agsafeset(cluster1, new CString("label"), Macro.createHackInitDimensionFromLabel((int) width, (int) height),
+					new CString(""));
 		}
 		this.exportEntities(cluster1, group.getLeafsDirect());
 		this.clusters.put(group, cluster1);
@@ -517,9 +505,9 @@ public class CucaDiagramFileMakerJDot implements CucaDiagramFileMaker {
 		if (n != null) {
 			return n;
 		}
-		final String id = getBibliotekon().getShapeUid((ILeaf) entity);
+		final String id = getBibliotekon().getNodeUid((ILeaf) entity);
 		for (Map.Entry<ILeaf, ST_Agnode_s> ent : nodes.entrySet()) {
-			if (id.equals(getBibliotekon().getShapeUid(ent.getKey()))) {
+			if (id.equals(getBibliotekon().getNodeUid(ent.getKey()))) {
 				return ent.getValue();
 			}
 		}
@@ -543,10 +531,12 @@ public class CucaDiagramFileMakerJDot implements CucaDiagramFileMaker {
 
 		int length = link.getLength();
 		// System.err.println("length=" + length);
-		// if (/* pragma.horizontalLineBetweenDifferentPackageAllowed() || */link.isInvis() || length != 1) {
+		// if (/* pragma.horizontalLineBetweenDifferentPackageAllowed() ||
+		// */link.isInvis() || length != 1) {
 		agsafeset(e, new CString("minlen"), new CString("" + (length - 1)), new CString(""));
 		// }
-		// System.err.print("EDGE " + link.getEntity1().getUid() + "->" + link.getEntity2().getUid() + " minlen="
+		// System.err.print("EDGE " + link.getEntity1().getUid() + "->" +
+		// link.getEntity2().getUid() + " minlen="
 		// + (length - 1) + " ");
 
 		final TextBlock label = getLabel(link);
@@ -600,12 +590,8 @@ public class CucaDiagramFileMakerJDot implements CucaDiagramFileMaker {
 			throw new IllegalStateException();
 		}
 		final IEntityImage image = printEntityInternal(ent);
-		final Dimension2D dim = image.calculateDimension(stringBounder);
-		final Shape shape = new Shape(image, image.getShapeType(), dim.getWidth(), dim.getHeight(),
-				dotStringFactory.getColorSequence(), ent.isTop(), image.getShield(stringBounder),
-				ent.getEntityPosition());
+		final Node shape = getBibliotekon().createNode(ent, image, dotStringFactory.getColorSequence(), stringBounder);
 		// dotStringFactory.addShape(shape);
-		getBibliotekon().putShape(ent, shape);
 	}
 
 	private Bibliotekon getBibliotekon() {
diff --git a/src/net/sourceforge/plantuml/objectdiagram/ObjectDiagramFactory.java b/src/net/sourceforge/plantuml/objectdiagram/ObjectDiagramFactory.java
index c01efadd7..e0ccf25a6 100644
--- a/src/net/sourceforge/plantuml/objectdiagram/ObjectDiagramFactory.java
+++ b/src/net/sourceforge/plantuml/objectdiagram/ObjectDiagramFactory.java
@@ -49,9 +49,9 @@ import net.sourceforge.plantuml.command.CommandPackage;
 import net.sourceforge.plantuml.command.CommandPage;
 import net.sourceforge.plantuml.command.CommandRankDir;
 import net.sourceforge.plantuml.command.UmlDiagramFactory;
-import net.sourceforge.plantuml.command.note.FactoryNoteCommand;
-import net.sourceforge.plantuml.command.note.FactoryNoteOnEntityCommand;
-import net.sourceforge.plantuml.command.note.FactoryNoteOnLinkCommand;
+import net.sourceforge.plantuml.command.note.CommandFactoryNote;
+import net.sourceforge.plantuml.command.note.CommandFactoryNoteOnEntity;
+import net.sourceforge.plantuml.command.note.CommandFactoryNoteOnLink;
 import net.sourceforge.plantuml.command.regex.RegexLeaf;
 import net.sourceforge.plantuml.objectdiagram.command.CommandAddData;
 import net.sourceforge.plantuml.objectdiagram.command.CommandCreateEntityObject;
@@ -78,7 +78,7 @@ public class ObjectDiagramFactory extends UmlDiagramFactory {
 		cmds.add(new CommandLinkClass(UmlDiagramType.OBJECT));
 		//
 		cmds.add(new CommandCreateEntityObject());
-		final FactoryNoteCommand factoryNoteCommand = new FactoryNoteCommand();
+		final CommandFactoryNote factoryNoteCommand = new CommandFactoryNote();
 
 		cmds.add(factoryNoteCommand.createSingleLine());
 		cmds.add(new CommandPackage());
@@ -88,7 +88,7 @@ public class ObjectDiagramFactory extends UmlDiagramFactory {
 		// addCommand(new CommandStereotype());
 		//
 		// addCommand(new CommandImport());
-		final FactoryNoteOnEntityCommand factoryNoteOnEntityCommand = new FactoryNoteOnEntityCommand("object",
+		final CommandFactoryNoteOnEntity factoryNoteOnEntityCommand = new CommandFactoryNoteOnEntity("object",
 				new RegexLeaf("ENTITY", "([\\p{L}0-9_.]+|[%g][^%g]+[%g])"));
 		cmds.add(factoryNoteOnEntityCommand.createSingleLine());
 
@@ -99,7 +99,7 @@ public class ObjectDiagramFactory extends UmlDiagramFactory {
 		cmds.add(factoryNoteOnEntityCommand.createMultiLine(false));
 		cmds.add(new CommandCreateEntityObjectMultilines());
 
-		final FactoryNoteOnLinkCommand factoryNoteOnLinkCommand = new FactoryNoteOnLinkCommand();
+		final CommandFactoryNoteOnLink factoryNoteOnLinkCommand = new CommandFactoryNoteOnLink();
 		cmds.add(factoryNoteOnLinkCommand.createSingleLine());
 		cmds.add(factoryNoteOnLinkCommand.createMultiLine(false));
 
diff --git a/src/net/sourceforge/plantuml/posimo/DotPath.java b/src/net/sourceforge/plantuml/posimo/DotPath.java
index 88442609e..49016688d 100644
--- a/src/net/sourceforge/plantuml/posimo/DotPath.java
+++ b/src/net/sourceforge/plantuml/posimo/DotPath.java
@@ -45,9 +45,11 @@ import java.awt.geom.Rectangle2D;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 import net.sourceforge.plantuml.EnsureVisible;
 import net.sourceforge.plantuml.asciiart.BasicCharArea;
@@ -168,6 +170,29 @@ public class DotPath implements UShape, Moveable {
 		return beziers.get(0).getP1();
 	}
 
+	public Set<Point2D> sample() {
+		final Set<Point2D> result = new HashSet<Point2D>();
+		for (CubicCurve2D.Double bez : beziers) {
+			sample(bez, result);
+		}
+		return Collections.unmodifiableSet(result);
+	}
+
+	private static void sample(CubicCurve2D bez, Set<Point2D> result) {
+		final Point2D p1 = bez.getCtrlP1();
+		final Point2D p2 = bez.getCtrlP2();
+		if (bez.getFlatnessSq() > 0.5 || p1.distance(p2) > 4) {
+			final CubicCurve2D.Double left = new CubicCurve2D.Double();
+			final CubicCurve2D.Double right = new CubicCurve2D.Double();
+			bez.subdivide(left, right);
+			sample(left, result);
+			sample(right, result);
+		} else {
+			result.add(p1);
+			result.add(p2);
+		}
+	}
+
 	public PointAndAngle getMiddle() {
 		Point2D result = null;
 		double angle = 0;
@@ -361,8 +386,8 @@ public class DotPath implements UShape, Moveable {
 	public void draw(Graphics2D g2d, double x, double y) {
 		final GeneralPath p = new GeneralPath();
 		for (CubicCurve2D.Double bez : beziers) {
-			bez = new CubicCurve2D.Double(x + bez.x1, y + bez.y1, x + bez.ctrlx1, y + bez.ctrly1, x + bez.ctrlx2, y
-					+ bez.ctrly2, x + bez.x2, y + bez.y2);
+			bez = new CubicCurve2D.Double(x + bez.x1, y + bez.y1, x + bez.ctrlx1, y + bez.ctrly1, x + bez.ctrlx2,
+					y + bez.ctrly2, x + bez.x2, y + bez.y2);
 			p.append(bez, true);
 		}
 		g2d.draw(p);
@@ -379,8 +404,8 @@ public class DotPath implements UShape, Moveable {
 	public void drawOk(EpsGraphics eps, double x, double y) {
 		// boolean first = true;
 		for (CubicCurve2D.Double bez : beziers) {
-			bez = new CubicCurve2D.Double(x + bez.x1, y + bez.y1, x + bez.ctrlx1, y + bez.ctrly1, x + bez.ctrlx2, y
-					+ bez.ctrly2, x + bez.x2, y + bez.y2);
+			bez = new CubicCurve2D.Double(x + bez.x1, y + bez.y1, x + bez.ctrlx1, y + bez.ctrly1, x + bez.ctrlx2,
+					y + bez.ctrly2, x + bez.x2, y + bez.y2);
 			eps.epsLine(bez.x1, bez.y1, bez.x2, bez.y2);
 		}
 	}
@@ -390,8 +415,8 @@ public class DotPath implements UShape, Moveable {
 		final boolean dashed = false;
 		boolean first = true;
 		for (CubicCurve2D.Double bez : beziers) {
-			bez = new CubicCurve2D.Double(x + bez.x1, y + bez.y1, x + bez.ctrlx1, y + bez.ctrly1, x + bez.ctrlx2, y
-					+ bez.ctrly2, x + bez.x2, y + bez.y2);
+			bez = new CubicCurve2D.Double(x + bez.x1, y + bez.y1, x + bez.ctrlx1, y + bez.ctrly1, x + bez.ctrlx2,
+					y + bez.ctrly2, x + bez.x2, y + bez.y2);
 			if (first) {
 				eps.movetoNoMacro(bez.x1, bez.y1);
 				first = dashed;
@@ -504,14 +529,14 @@ public class DotPath implements UShape, Moveable {
 				area.drawHLine('-', (int) (bez.y1 / pixelYPerChar), (int) (bez.x1 / pixelXPerChar),
 						(int) (bez.x2 / pixelXPerChar));
 			} /*
-			 * else { throw new UnsupportedOperationException("bez=" + toString(bez)); }
-			 */
+				 * else { throw new UnsupportedOperationException("bez=" + toString(bez)); }
+				 */
 		}
 	}
 
 	static String toString(CubicCurve2D.Double c) {
-		return "(" + c.x1 + "," + c.y1 + ") " + "(" + c.ctrlx1 + "," + c.ctrly1 + ") " + "(" + c.ctrlx2 + ","
-				+ c.ctrly2 + ") " + "(" + c.x2 + "," + c.y2 + ") ";
+		return "(" + c.x1 + "," + c.y1 + ") " + "(" + c.ctrlx1 + "," + c.ctrly1 + ") " + "(" + c.ctrlx2 + "," + c.ctrly2
+				+ ") " + "(" + c.x2 + "," + c.y2 + ") ";
 
 	}
 
@@ -526,8 +551,8 @@ public class DotPath implements UShape, Moveable {
 	}
 
 	public static CubicCurve2D.Double reverse(CubicCurve2D curv) {
-		return new CubicCurve2D.Double(curv.getX2(), curv.getY2(), curv.getCtrlX2(), curv.getCtrlY2(),
-				curv.getCtrlX1(), curv.getCtrlY1(), curv.getX1(), curv.getY1());
+		return new CubicCurve2D.Double(curv.getX2(), curv.getY2(), curv.getCtrlX2(), curv.getCtrlY2(), curv.getCtrlX1(),
+				curv.getCtrlY1(), curv.getX1(), curv.getY1());
 	}
 
 	public DotPath reverse() {
diff --git a/src/net/sourceforge/plantuml/preproc/Stdlib.java b/src/net/sourceforge/plantuml/preproc/Stdlib.java
index b29047a22..1332fc3aa 100644
--- a/src/net/sourceforge/plantuml/preproc/Stdlib.java
+++ b/src/net/sourceforge/plantuml/preproc/Stdlib.java
@@ -56,7 +56,7 @@ public class Stdlib {
 		}
 	}
 
-	private static Stdlib retrieve(final String name) throws IOException {
+	public static Stdlib retrieve(final String name) throws IOException {
 		Stdlib result = all.get(name);
 		if (result == null) {
 			final DataInputStream dataStream = getDataStream(name);
@@ -222,7 +222,7 @@ public class Stdlib {
 	public static void extractStdLib() throws IOException {
 		for (String name : getAll()) {
 			final Stdlib folder = Stdlib.retrieve(name);
-			folder.extractMeFull(new File("stdlib", name));
+			folder.extractMeFull();
 		}
 	}
 
@@ -237,7 +237,7 @@ public class Stdlib {
 		return Collections.unmodifiableCollection(result);
 	}
 
-	private void extractMeFull(File dir) throws IOException {
+	private void extractMeFull() throws IOException {
 		final DataInputStream dataStream = getDataStream();
 		if (dataStream == null) {
 			return;
@@ -280,6 +280,46 @@ public class Stdlib {
 		}
 	}
 
+	public List<String> extractAllSprites() throws IOException {
+		final List<String> result = new ArrayList<String>();
+		final DataInputStream dataStream = getDataStream();
+		if (dataStream == null) {
+			return Collections.unmodifiableList(result);
+		}
+		dataStream.readUTF();
+		final InputStream spriteStream = getSpriteStream();
+		try {
+			while (true) {
+				final String filename = dataStream.readUTF();
+				if (filename.equals(SEPARATOR)) {
+					return Collections.unmodifiableList(result);
+				}
+				while (true) {
+					final String s = dataStream.readUTF();
+					if (s.equals(SEPARATOR)) {
+						break;
+					}
+					if (isSpriteLine(s)) {
+						final Matcher m = sizePattern.matcher(s);
+						final boolean ok = m.find();
+						if (ok == false) {
+							throw new IOException(s);
+						}
+						final int width = Integer.parseInt(m.group(1));
+						final int height = Integer.parseInt(m.group(2));
+						final String sprite = readSprite(width, height, spriteStream);
+						if (s.contains("_LARGE") == false) {
+							result.add(s + "\n" + sprite + "}");
+						}
+					}
+				}
+			}
+		} finally {
+			dataStream.close();
+			spriteStream.close();
+		}
+	}
+
 	public static void addInfoVersion(List<String> strings, boolean details) {
 		try {
 			for (String name : getAll()) {
diff --git a/src/net/sourceforge/plantuml/project3/Arrows.java b/src/net/sourceforge/plantuml/project/Arrows.java
similarity index 98%
rename from src/net/sourceforge/plantuml/project3/Arrows.java
rename to src/net/sourceforge/plantuml/project/Arrows.java
index afa770a6c..521087deb 100644
--- a/src/net/sourceforge/plantuml/project3/Arrows.java
+++ b/src/net/sourceforge/plantuml/project/Arrows.java
@@ -33,7 +33,7 @@
  *
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project;
 
 import net.sourceforge.plantuml.Direction;
 import net.sourceforge.plantuml.ugraphic.UPolygon;
diff --git a/src/net/sourceforge/plantuml/project3/Resources.java b/src/net/sourceforge/plantuml/project/Completion.java
similarity index 76%
rename from src/net/sourceforge/plantuml/project3/Resources.java
rename to src/net/sourceforge/plantuml/project/Completion.java
index d1159e667..7158898de 100644
--- a/src/net/sourceforge/plantuml/project3/Resources.java
+++ b/src/net/sourceforge/plantuml/project/Completion.java
@@ -33,21 +33,20 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project;
 
-import java.util.LinkedHashSet;
-import java.util.Set;
+import net.sourceforge.plantuml.project.lang.Complement;
 
-public class Resources /*implements LoadPlanable*/ {
+public class Completion implements Complement {
 
-	private final Set<Resource> all = new LinkedHashSet<Resource>();
+	private final int completion;
 
-//	public int getLoadAt(Instant instant) {
-//		int result = 0;
-//		for (Resource res : all) {
-//			result += res.getLoadAt(instant);
-//		}
-//		return result;
-//	}
+	public Completion(int completion) {
+		this.completion = completion;
+	}
+
+	public final int getCompletion() {
+		return completion;
+	}
 
 }
diff --git a/src/net/sourceforge/plantuml/project3/ConstantPlan.java b/src/net/sourceforge/plantuml/project/ConstantPlan.java
similarity index 92%
rename from src/net/sourceforge/plantuml/project3/ConstantPlan.java
rename to src/net/sourceforge/plantuml/project/ConstantPlan.java
index 078ef64dd..0703b2efb 100644
--- a/src/net/sourceforge/plantuml/project3/ConstantPlan.java
+++ b/src/net/sourceforge/plantuml/project/ConstantPlan.java
@@ -33,7 +33,9 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project;
+
+import net.sourceforge.plantuml.project.core.Wink;
 
 public class ConstantPlan implements LoadPlanable {
 
@@ -51,7 +53,7 @@ public class ConstantPlan implements LoadPlanable {
 		return new ConstantPlan(load);
 	}
 
-	public int getLoadAt(Instant instant) {
+	public int getLoadAt(Wink instant) {
 		return loadPerInstant;
 
 	}
diff --git a/src/net/sourceforge/plantuml/project3/DayAsDate.java b/src/net/sourceforge/plantuml/project/DayAsDate.java
similarity index 74%
rename from src/net/sourceforge/plantuml/project3/DayAsDate.java
rename to src/net/sourceforge/plantuml/project/DayAsDate.java
index 0a1d0ffc9..a9b0b97e3 100644
--- a/src/net/sourceforge/plantuml/project3/DayAsDate.java
+++ b/src/net/sourceforge/plantuml/project/DayAsDate.java
@@ -33,10 +33,15 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project;
 
 import java.util.Date;
 
+import net.sourceforge.plantuml.project.core.Month;
+import net.sourceforge.plantuml.project.core.Wink;
+import net.sourceforge.plantuml.project.lang.Complement;
+import net.sourceforge.plantuml.project.lang.Subject;
+
 public class DayAsDate implements Complement, Comparable<DayAsDate>, Subject {
 
 	private final int year;
@@ -65,6 +70,10 @@ public class DayAsDate implements Complement, Comparable<DayAsDate>, Subject {
 		this.month = month;
 	}
 
+	public int getYear() {
+		return year;
+	}
+
 	private int internalNumber() {
 		return year * 100 * 100 + month.ordinal() * 100 + dayOfMonth;
 	}
@@ -120,7 +129,7 @@ public class DayAsDate implements Complement, Comparable<DayAsDate>, Subject {
 		return DayOfWeek.fromH(h);
 	}
 
-	public InstantDay asInstantDay(DayAsDate reference) {
+	public Wink asInstantDay(DayAsDate reference) {
 		// if (this.compareTo(reference) < 0) {
 		// throw new IllegalArgumentException();
 		// }
@@ -130,7 +139,39 @@ public class DayAsDate implements Complement, Comparable<DayAsDate>, Subject {
 			current = current.next();
 			cmp++;
 		}
-		return new InstantDay(cmp);
+		return new Wink(cmp);
+	}
+
+	// http://www.proesite.com/timex/wkcalc.htm
+	public int ISO_WN() {
+		final int y = year;
+		int m = month.ordinal() + 1;
+		int d = dayOfMonth;
+		int dow = DOW(y, m, d);
+		int dow0101 = DOW(y, 1, 1);
+
+		if (m == 1 && 3 < dow0101 && dow0101 < 7 - (d - 1)) {
+			// days before week 1 of the current year have the same week number as
+			// the last day of the last week of the previous year
+
+			dow = dow0101 - 1;
+			dow0101 = DOW(y - 1, 1, 1);
+			m = 12;
+			d = 31;
+		} else if (m == 12 && 30 - (d - 1) < DOW(y + 1, 1, 1) && DOW(y + 1, 1, 1) < 4) {
+			// days after the last week of the current year have the same week number as
+			// the first day of the next year, (i.e. 1)
+
+			return 1;
+		}
+
+		return (DOW(y, 1, 1) < 4) ? 1 : 0 + 4 * (m - 1) + (2 * (m - 1) + (d - 1) + dow0101 - dow + 6) * 36 / 256;
+
+	}
+
+	private int DOW(int y, int m, int d) {
+		// TODO Auto-generated method stub
+		return 0;
 	}
 
 	public int compareTo(DayAsDate other) {
diff --git a/src/net/sourceforge/plantuml/project3/DayOfWeek.java b/src/net/sourceforge/plantuml/project/DayOfWeek.java
similarity index 93%
rename from src/net/sourceforge/plantuml/project3/DayOfWeek.java
rename to src/net/sourceforge/plantuml/project/DayOfWeek.java
index 54af9a75b..37bcdb160 100644
--- a/src/net/sourceforge/plantuml/project3/DayOfWeek.java
+++ b/src/net/sourceforge/plantuml/project/DayOfWeek.java
@@ -33,9 +33,11 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project;
 
 import net.sourceforge.plantuml.StringUtils;
+import net.sourceforge.plantuml.project.lang.Complement;
+import net.sourceforge.plantuml.project.lang.Subject;
 
 public enum DayOfWeek implements Subject, Complement {
 
diff --git a/src/net/sourceforge/plantuml/project3/DaysAsDates.java b/src/net/sourceforge/plantuml/project/DaysAsDates.java
similarity index 93%
rename from src/net/sourceforge/plantuml/project3/DaysAsDates.java
rename to src/net/sourceforge/plantuml/project/DaysAsDates.java
index abf96514c..612b68c1e 100644
--- a/src/net/sourceforge/plantuml/project3/DaysAsDates.java
+++ b/src/net/sourceforge/plantuml/project/DaysAsDates.java
@@ -33,10 +33,13 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project;
 
 import java.util.Iterator;
 
+import net.sourceforge.plantuml.project.lang.Complement;
+import net.sourceforge.plantuml.project.lang.Subject;
+
 public class DaysAsDates implements Subject, Complement, Iterable<DayAsDate> {
 
 	private final DayAsDate date1;
diff --git a/src/net/sourceforge/plantuml/project3/Failable.java b/src/net/sourceforge/plantuml/project/Failable.java
similarity index 97%
rename from src/net/sourceforge/plantuml/project3/Failable.java
rename to src/net/sourceforge/plantuml/project/Failable.java
index 1263f66b6..482f1cdb2 100644
--- a/src/net/sourceforge/plantuml/project3/Failable.java
+++ b/src/net/sourceforge/plantuml/project/Failable.java
@@ -33,7 +33,7 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project;
 
 public class Failable<O> {
 
diff --git a/src/net/sourceforge/plantuml/project3/GCalendarSimple.java b/src/net/sourceforge/plantuml/project/GCalendar.java
similarity index 84%
rename from src/net/sourceforge/plantuml/project3/GCalendarSimple.java
rename to src/net/sourceforge/plantuml/project/GCalendar.java
index 349c638e5..05090b701 100644
--- a/src/net/sourceforge/plantuml/project3/GCalendarSimple.java
+++ b/src/net/sourceforge/plantuml/project/GCalendar.java
@@ -33,19 +33,21 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project;
 
-public class GCalendarSimple implements GCalendar {
+import net.sourceforge.plantuml.project.core.Wink;
+
+public class GCalendar {
 
 	private final DayAsDate start;
 
-	public GCalendarSimple(DayAsDate start) {
+	public GCalendar(DayAsDate start) {
 		this.start = start;
 	}
 
-	public DayAsDate toDayAsDate(InstantDay day) {
+	public DayAsDate toDayAsDate(Wink day) {
 		DayAsDate result = start;
-		final int target = day.getNumDay();
+		final int target = day.getWink();
 		int work = 0;
 		while (work < target) {
 			result = result.next();
@@ -54,11 +56,11 @@ public class GCalendarSimple implements GCalendar {
 		return result;
 	}
 
-	public InstantDay fromDayAsDate(DayAsDate day) {
+	public Wink fromDayAsDate(DayAsDate day) {
 		if (day.compareTo(start) < 0) {
 			throw new IllegalArgumentException();
 		}
-		InstantDay result = new InstantDay(0);
+		Wink result = new Wink(0);
 		while (toDayAsDate(result).equals(day) == false) {
 			result = result.increment();
 		}
diff --git a/src/net/sourceforge/plantuml/project3/GanttArrow.java b/src/net/sourceforge/plantuml/project/GanttArrow.java
similarity index 92%
rename from src/net/sourceforge/plantuml/project3/GanttArrow.java
rename to src/net/sourceforge/plantuml/project/GanttArrow.java
index f8f0842e6..e5390b1c3 100644
--- a/src/net/sourceforge/plantuml/project3/GanttArrow.java
+++ b/src/net/sourceforge/plantuml/project/GanttArrow.java
@@ -33,11 +33,16 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project;
 
 import net.sourceforge.plantuml.Direction;
 import net.sourceforge.plantuml.graphic.HtmlColorUtils;
 import net.sourceforge.plantuml.graphic.UDrawable;
+import net.sourceforge.plantuml.project.core.Task;
+import net.sourceforge.plantuml.project.core.TaskAttribute;
+import net.sourceforge.plantuml.project.core.TaskInstant;
+import net.sourceforge.plantuml.project.draw.TaskDraw;
+import net.sourceforge.plantuml.project.timescale.TimeScale;
 import net.sourceforge.plantuml.ugraphic.UChangeBackColor;
 import net.sourceforge.plantuml.ugraphic.UChangeColor;
 import net.sourceforge.plantuml.ugraphic.UGraphic;
@@ -88,6 +93,10 @@ public class GanttArrow implements UDrawable {
 		final double x2 = getX(dest, atEnd.getInv());
 		final double y2 = draw2.getY(atEnd);
 
+		if (atStart == Direction.DOWN && y2 < y1) {
+			y1 = draw1.getY(atStart.getInv());
+		}
+
 		if (this.atStart == Direction.DOWN && this.atEnd == Direction.RIGHT) {
 			if (x2 > x1) {
 				drawLine(ug, x1, y1, x1, y2, x2, y2);
diff --git a/src/net/sourceforge/plantuml/project3/GanttConstraint.java b/src/net/sourceforge/plantuml/project/GanttConstraint.java
similarity index 88%
rename from src/net/sourceforge/plantuml/project3/GanttConstraint.java
rename to src/net/sourceforge/plantuml/project/GanttConstraint.java
index a5bbf5db0..19c1566a0 100644
--- a/src/net/sourceforge/plantuml/project3/GanttConstraint.java
+++ b/src/net/sourceforge/plantuml/project/GanttConstraint.java
@@ -33,9 +33,12 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project;
 
 import net.sourceforge.plantuml.graphic.UDrawable;
+import net.sourceforge.plantuml.project.core.TaskInstant;
+import net.sourceforge.plantuml.project.lang.Complement;
+import net.sourceforge.plantuml.project.timescale.TimeScale;
 
 public class GanttConstraint implements Complement {
 
diff --git a/src/net/sourceforge/plantuml/project3/GanttDiagram.java b/src/net/sourceforge/plantuml/project/GanttDiagram.java
similarity index 63%
rename from src/net/sourceforge/plantuml/project3/GanttDiagram.java
rename to src/net/sourceforge/plantuml/project/GanttDiagram.java
index b22f34344..6c6a66516 100644
--- a/src/net/sourceforge/plantuml/project3/GanttDiagram.java
+++ b/src/net/sourceforge/plantuml/project/GanttDiagram.java
@@ -33,7 +33,7 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project;
 
 import java.awt.geom.Dimension2D;
 import java.awt.geom.Rectangle2D;
@@ -55,15 +55,11 @@ import net.sourceforge.plantuml.Dimension2DDouble;
 import net.sourceforge.plantuml.FileFormatOption;
 import net.sourceforge.plantuml.Scale;
 import net.sourceforge.plantuml.SkinParam;
-import net.sourceforge.plantuml.SpriteContainerEmpty;
 import net.sourceforge.plantuml.TitledDiagram;
 import net.sourceforge.plantuml.UmlDiagramType;
 import net.sourceforge.plantuml.command.CommandExecutionResult;
 import net.sourceforge.plantuml.core.DiagramDescription;
 import net.sourceforge.plantuml.core.ImageData;
-import net.sourceforge.plantuml.cucadiagram.Display;
-import net.sourceforge.plantuml.graphic.FontConfiguration;
-import net.sourceforge.plantuml.graphic.HorizontalAlignment;
 import net.sourceforge.plantuml.graphic.HtmlColor;
 import net.sourceforge.plantuml.graphic.HtmlColorSetSimple;
 import net.sourceforge.plantuml.graphic.HtmlColorUtils;
@@ -71,16 +67,33 @@ import net.sourceforge.plantuml.graphic.IHtmlColorSet;
 import net.sourceforge.plantuml.graphic.InnerStrategy;
 import net.sourceforge.plantuml.graphic.StringBounder;
 import net.sourceforge.plantuml.graphic.TextBlock;
+import net.sourceforge.plantuml.project.core.Moment;
+import net.sourceforge.plantuml.project.core.MomentImpl;
+import net.sourceforge.plantuml.project.core.PrintScale;
+import net.sourceforge.plantuml.project.core.Resource;
+import net.sourceforge.plantuml.project.core.Task;
+import net.sourceforge.plantuml.project.core.TaskAttribute;
+import net.sourceforge.plantuml.project.core.TaskCode;
+import net.sourceforge.plantuml.project.core.TaskImpl;
+import net.sourceforge.plantuml.project.core.TaskInstant;
+import net.sourceforge.plantuml.project.core.TaskSeparator;
+import net.sourceforge.plantuml.project.core.Wink;
+import net.sourceforge.plantuml.project.draw.ResourceDraw;
+import net.sourceforge.plantuml.project.draw.TaskDraw;
+import net.sourceforge.plantuml.project.draw.TaskDrawRegular;
+import net.sourceforge.plantuml.project.draw.TaskDrawSeparator;
+import net.sourceforge.plantuml.project.draw.TimeHeader;
+import net.sourceforge.plantuml.project.draw.TimeHeaderDaily;
+import net.sourceforge.plantuml.project.draw.TimeHeaderSimple;
+import net.sourceforge.plantuml.project.draw.TimeHeaderWeekly;
+import net.sourceforge.plantuml.project.lang.ComplementColors;
+import net.sourceforge.plantuml.project.lang.Subject;
+import net.sourceforge.plantuml.project.timescale.TimeScale;
 import net.sourceforge.plantuml.svek.TextBlockBackcolored;
 import net.sourceforge.plantuml.ugraphic.ColorMapperIdentity;
 import net.sourceforge.plantuml.ugraphic.ImageBuilder;
 import net.sourceforge.plantuml.ugraphic.MinMax;
-import net.sourceforge.plantuml.ugraphic.UChangeBackColor;
-import net.sourceforge.plantuml.ugraphic.UChangeColor;
-import net.sourceforge.plantuml.ugraphic.UFont;
 import net.sourceforge.plantuml.ugraphic.UGraphic;
-import net.sourceforge.plantuml.ugraphic.ULine;
-import net.sourceforge.plantuml.ugraphic.URectangle;
 import net.sourceforge.plantuml.ugraphic.UTranslate;
 
 public class GanttDiagram extends TitledDiagram implements Subject {
@@ -89,13 +102,24 @@ public class GanttDiagram extends TitledDiagram implements Subject {
 	private final Map<String, Task> byShortName = new HashMap<String, Task>();
 	private final List<GanttConstraint> constraints = new ArrayList<GanttConstraint>();
 	private final IHtmlColorSet colorSet = new HtmlColorSetSimple();
+
 	private final Collection<DayOfWeek> closedDayOfWeek = EnumSet.noneOf(DayOfWeek.class);
 	private final Collection<DayAsDate> closedDayAsDate = new HashSet<DayAsDate>();
 	private final Collection<DayAsDate> openedDayAsDate = new HashSet<DayAsDate>();
-	private GCalendar calendar;
 
-	private final Instant min = new InstantDay(0);
-	private Instant max;
+	private final Map<String, Resource> resources = new LinkedHashMap<String, Resource>();
+	private final Map<DayAsDate, HtmlColor> colorDays = new HashMap<DayAsDate, HtmlColor>();
+	private final Map<DayAsDate, String> nameDays = new HashMap<DayAsDate, String>();
+
+	private PrintScale printScale = PrintScale.DAILY;
+	private DayAsDate today;
+	private GCalendar calendar;
+	private double totalHeight;
+	private Wink min = new Wink(0);
+	private Wink max;
+
+	private DayAsDate printStart;
+	private DayAsDate printEnd;
 
 	public DiagramDescription getDescription() {
 		return new DiagramDescription("(Project)");
@@ -148,16 +172,35 @@ public class GanttDiagram extends TitledDiagram implements Subject {
 		return imageBuilder.writeImageTOBEMOVED(fileFormatOption, seed, os);
 	}
 
+	public void setPrintScale(PrintScale printScale) {
+		this.printScale = printScale;
+	}
+
 	private TextBlockBackcolored getTextBlock() {
-		initMinMax();
-		final TimeScale timeScale = getTimeScale();
-		initTaskAndResourceDraws(timeScale);
+		if (printStart == null) {
+			initMinMax();
+		} else {
+			this.min = calendar.fromDayAsDate(printStart);
+			this.max = calendar.fromDayAsDate(printEnd);
+		}
+		final TimeHeader timeHeader;
+		if (calendar == null) {
+			timeHeader = new TimeHeaderSimple(min, max);
+		} else if (printScale == PrintScale.WEEKLY) {
+			timeHeader = new TimeHeaderWeekly(calendar, min, max, getDefaultPlan(), colorDays, nameDays);
+		} else {
+			timeHeader = new TimeHeaderDaily(calendar, min, max, getDefaultPlan(), colorDays, nameDays, printStart,
+					printEnd);
+		}
+		initTaskAndResourceDraws(timeHeader.getTimeScale(), timeHeader.getFullHeaderHeight());
 		return new TextBlockBackcolored() {
 
 			public void drawU(UGraphic ug) {
-				drawTimeHeader(ug, timeScale);
-				drawTasks(ug, timeScale);
-				drawConstraints(ug, timeScale);
+				timeHeader.drawTimeHeader(ug, totalHeight);
+				drawConstraints(ug, timeHeader.getTimeScale());
+				drawTasksRect(ug);
+				drawTasksTitle(ug);
+				drawResources(ug);
 			}
 
 			public Rectangle2D getInnerPosition(String member, StringBounder stringBounder, InnerStrategy strategy) {
@@ -165,8 +208,8 @@ public class GanttDiagram extends TitledDiagram implements Subject {
 			}
 
 			public Dimension2D calculateDimension(StringBounder stringBounder) {
-				final double xmin = timeScale.getStartingPosition(min);
-				final double xmax = timeScale.getEndingPosition(max);
+				final double xmin = timeHeader.getTimeScale().getStartingPosition(min);
+				final double xmax = timeHeader.getTimeScale().getEndingPosition(max);
 				return new Dimension2DDouble(xmax - xmin, totalHeight);
 			}
 
@@ -180,25 +223,43 @@ public class GanttDiagram extends TitledDiagram implements Subject {
 		};
 	}
 
-	private TimeScale getTimeScale() {
-		if (calendar == null) {
-			return new TimeScaleBasic();
+	private void drawTasksRect(UGraphic ug) {
+		for (Task task : tasks.values()) {
+			final TaskDraw draw = task.getTaskDraw();
+			final UTranslate move = new UTranslate(0, draw.getY());
+			draw.drawU(ug.apply(move));
 		}
-		return new TimeScaleBasic2(getCalendarSimple());
-		// return new TimeScaleWithoutWeekEnd(calendar);
 	}
 
-	private GCalendarSimple getCalendarSimple() {
-		return (GCalendarSimple) calendar;
+	private void drawConstraints(final UGraphic ug, TimeScale timeScale) {
+		for (GanttConstraint constraint : constraints) {
+			constraint.getUDrawable(timeScale).drawU(ug);
+		}
+	}
+
+	private void drawTasksTitle(final UGraphic ug1) {
+		for (Task task : tasks.values()) {
+			final TaskDraw draw = task.getTaskDraw();
+			final UTranslate move = new UTranslate(0, draw.getY());
+			draw.drawTitle(ug1.apply(move));
+		}
+	}
+
+	private void drawResources(UGraphic ug) {
+		for (Resource res : resources.values()) {
+			final ResourceDraw draw = res.getResourceDraw();
+			final UTranslate move = new UTranslate(0, draw.getY());
+			draw.drawU(ug.apply(move));
+		}
 	}
 
 	public final LoadPlanable getDefaultPlan() {
 		return new LoadPlanable() {
-			public int getLoadAt(Instant instant) {
+			public int getLoadAt(Wink instant) {
 				if (calendar == null) {
 					return 100;
 				}
-				final DayAsDate day = getCalendarSimple().toDayAsDate((InstantDay) instant);
+				final DayAsDate day = calendar.toDayAsDate((Wink) instant);
 				if (isClosed(day)) {
 					return 0;
 				}
@@ -227,152 +288,8 @@ public class GanttDiagram extends TitledDiagram implements Subject {
 		openedDayAsDate.add(day);
 	}
 
-	private void drawConstraints(final UGraphic ug, TimeScale timeScale) {
-		for (GanttConstraint constraint : constraints) {
-			constraint.getUDrawable(timeScale).drawU(ug);
-		}
-
-	}
-
-	private double totalHeight;
-
-	private void drawTimeHeader(final UGraphic ug, TimeScale timeScale) {
-
-		final double xmin = timeScale.getStartingPosition(min);
-		final double xmax = timeScale.getEndingPosition(max);
-		if (calendar == null) {
-			drawSimpleDayCounter(ug, timeScale);
-		} else {
-			drawCalendar(ug, timeScale);
-		}
-		ug.apply(new UChangeColor(HtmlColorUtils.LIGHT_GRAY)).draw(new ULine(xmax - xmin, 0));
-		ug.apply(new UChangeColor(HtmlColorUtils.LIGHT_GRAY)).apply(new UTranslate(0, getHeaderHeight() - 3))
-				.draw(new ULine(xmax - xmin, 0));
-
-	}
-
-	private final HtmlColor veryLightGray = new HtmlColorSetSimple().getColorIfValid("#E0E8E8");
-
-	private double getHeaderHeight() {
-		return getTimeHeaderHeight() + getHeaderNameDayHeight();
-	}
-
-	private double getTimeHeaderHeight() {
-		if (calendar != null) {
-			return Y_WEEKDAY + Y_NUMDAY;
-		}
-		return 16;
-	}
-
-	private double getHeaderNameDayHeight() {
-		if (calendar != null && nameDays.size() > 0) {
-			return 16;
-		}
-		return 0;
-	}
-
-	private static final int Y_WEEKDAY = 16;
-	private static final int Y_NUMDAY = 28;
-
-	private void drawCalendar(final UGraphic ug, TimeScale timeScale) {
-		timeScale = new TimeScaleBasic();
-		final ULine vbar = new ULine(0, totalHeight - Y_WEEKDAY);
-		Month lastMonth = null;
-		final GCalendarSimple calendarAll = getCalendarSimple();
-		final Instant max2 = calendarAll.fromDayAsDate(calendar.toDayAsDate((InstantDay) max));
-		for (Instant i = min; i.compareTo(max2.increment()) <= 0; i = i.increment()) {
-			final DayAsDate day = calendarAll.toDayAsDate((InstantDay) i);
-			final DayOfWeek dayOfWeek = day.getDayOfWeek();
-			final boolean isWorkingDay = getDefaultPlan().getLoadAt(i) > 0;
-			final String d1 = "" + day.getDayOfMonth();
-			final TextBlock num = getTextBlock(d1, 10, false);
-			final double x1 = timeScale.getStartingPosition(i);
-			final double x2 = timeScale.getEndingPosition(i);
-			if (i.compareTo(max2.increment()) < 0) {
-				final TextBlock weekDay = getTextBlock(dayOfWeek.shortName(), 10, false);
-
-				final URectangle rect = new URectangle(x2 - x1 - 1, totalHeight - Y_WEEKDAY);
-				if (isWorkingDay) {
-					final HtmlColor back = colorDays.get(day);
-					if (back != null) {
-						ug.apply(new UChangeColor(null)).apply(new UChangeBackColor(back))
-								.apply(new UTranslate(x1 + 1, Y_WEEKDAY)).draw(rect);
-					}
-					drawCenter(ug.apply(new UTranslate(0, Y_NUMDAY)), num, x1, x2);
-					drawCenter(ug.apply(new UTranslate(0, Y_WEEKDAY)), weekDay, x1, x2);
-				} else {
-					ug.apply(new UChangeColor(null)).apply(new UChangeBackColor(veryLightGray))
-							.apply(new UTranslate(x1 + 1, Y_WEEKDAY)).draw(rect);
-				}
-				if (lastMonth != day.getMonth()) {
-					final int delta = 5;
-					if (lastMonth != null) {
-						final TextBlock lastMonthBlock = getTextBlock(lastMonth.name(), 12, true);
-						lastMonthBlock.drawU(ug.apply(new UTranslate(x1
-								- lastMonthBlock.calculateDimension(ug.getStringBounder()).getWidth() - delta, 0)));
-					}
-					final TextBlock month = getTextBlock(day.getMonth().name(), 12, true);
-					month.drawU(ug.apply(new UTranslate(x1 + delta, 0)));
-					ug.apply(new UChangeColor(HtmlColorUtils.LIGHT_GRAY)).apply(new UTranslate(x1, 0))
-							.draw(new ULine(0, Y_WEEKDAY));
-				}
-				lastMonth = day.getMonth();
-			}
-			ug.apply(new UChangeColor(HtmlColorUtils.LIGHT_GRAY)).apply(new UTranslate(x1, Y_WEEKDAY)).draw(vbar);
-		}
-
-		if (nameDays.size() > 0) {
-			String last = null;
-			for (Instant i = min; i.compareTo(max2.increment()) <= 0; i = i.increment()) {
-				final DayAsDate day = calendarAll.toDayAsDate((InstantDay) i);
-				final String name = nameDays.get(day);
-				if (name != null && name.equals(last) == false) {
-					final double x1 = timeScale.getStartingPosition(i);
-					final double x2 = timeScale.getEndingPosition(i);
-					final TextBlock label = getTextBlock(name, 12, false);
-					final double h = label.calculateDimension(ug.getStringBounder()).getHeight();
-					double y1 = getTimeHeaderHeight();
-					double y2 = getHeaderHeight();
-					label.drawU(ug.apply(new UTranslate(x1, Y_NUMDAY + 11)));
-				}
-				last = name;
-			}
-
-		}
-	}
-
-	private TextBlock getTextBlock(final String text, int size, boolean bold) {
-		return Display.getWithNewlines(text).create(getFontConfiguration(size, bold), HorizontalAlignment.LEFT,
-				new SpriteContainerEmpty());
-	}
-
-	private void drawCenter(final UGraphic ug, final TextBlock text, final double x1, final double x2) {
-		final double width = text.calculateDimension(ug.getStringBounder()).getWidth();
-		final double delta = (x2 - x1) - width;
-		if (delta < 0) {
-			return;
-		}
-		text.drawU(ug.apply(new UTranslate(x1 + delta / 2, 0)));
-	}
-
-	private void drawSimpleDayCounter(final UGraphic ug, TimeScale timeScale) {
-		final ULine vbar = new ULine(0, totalHeight);
-		for (Instant i = min; i.compareTo(max.increment()) <= 0; i = i.increment()) {
-			final TextBlock num = Display.getWithNewlines(i.toShortString()).create(getFontConfiguration(10, false),
-					HorizontalAlignment.LEFT, new SpriteContainerEmpty());
-			final double x1 = timeScale.getStartingPosition(i);
-			final double x2 = timeScale.getEndingPosition(i);
-			final double width = num.calculateDimension(ug.getStringBounder()).getWidth();
-			final double delta = (x2 - x1) - width;
-			if (i.compareTo(max.increment()) < 0) {
-				num.drawU(ug.apply(new UTranslate(x1 + delta / 2, 0)));
-			}
-			ug.apply(new UChangeColor(HtmlColorUtils.LIGHT_GRAY)).apply(new UTranslate(x1, 0)).draw(vbar);
-		}
-	}
-
-	private void initTaskAndResourceDraws(TimeScale timeScale) {
-		double y = getHeaderHeight();
+	private void initTaskAndResourceDraws(TimeScale timeScale, double headerHeight) {
+		double y = headerHeight;
 		for (Task task : tasks.values()) {
 			final TaskDraw draw;
 			if (task instanceof TaskSeparator) {
@@ -402,8 +319,8 @@ public class GanttDiagram extends TitledDiagram implements Subject {
 				if (task instanceof TaskSeparator) {
 					continue;
 				}
-				final Instant start = task.getStart();
-				final Instant end = task.getEnd();
+				final Wink start = task.getStart();
+				final Wink end = task.getEnd();
 				// if (min.compareTo(start) > 0) {
 				// min = start;
 				// }
@@ -414,13 +331,13 @@ public class GanttDiagram extends TitledDiagram implements Subject {
 		}
 		if (calendar != null) {
 			for (DayAsDate d : colorDays.keySet()) {
-				final Instant instant = calendar.fromDayAsDate(d);
+				final Wink instant = calendar.fromDayAsDate(d);
 				if (instant.compareTo(max) > 0) {
 					max = instant;
 				}
 			}
 			for (DayAsDate d : nameDays.keySet()) {
-				final Instant instant = calendar.fromDayAsDate(d);
+				final Wink instant = calendar.fromDayAsDate(d);
 				if (instant.compareTo(max) > 0) {
 					max = instant;
 				}
@@ -443,28 +360,6 @@ public class GanttDiagram extends TitledDiagram implements Subject {
 		return result;
 	}
 
-	private void drawTasks(final UGraphic ug, TimeScale timeScale) {
-		for (Task task : tasks.values()) {
-			final TaskDraw draw = task.getTaskDraw();
-			final UTranslate move = new UTranslate(0, draw.getY());
-			draw.drawU(ug.apply(move));
-			draw.drawTitle(ug.apply(move));
-		}
-		for (Resource res : resources.values()) {
-			final ResourceDraw draw = res.getResourceDraw();
-			final UTranslate move = new UTranslate(0, draw.getY());
-			draw.drawU(ug.apply(move));
-		}
-	}
-
-	private FontConfiguration getFontConfiguration(int size, boolean bold) {
-		UFont font = UFont.serif(size);
-		if (bold) {
-			font = font.bold();
-		}
-		return new FontConfiguration(font, HtmlColorUtils.BLACK, HtmlColorUtils.BLACK, false);
-	}
-
 	public Task getExistingTask(String id) {
 		if (id == null) {
 			throw new IllegalArgumentException();
@@ -538,7 +433,7 @@ public class GanttDiagram extends TitledDiagram implements Subject {
 	}
 
 	public void setStartingDate(DayAsDate start) {
-		this.calendar = new GCalendarSimple(start);
+		this.calendar = new GCalendar(start);
 	}
 
 	public DayAsDate getStartingDate() {
@@ -552,14 +447,14 @@ public class GanttDiagram extends TitledDiagram implements Subject {
 		if (this.calendar == null) {
 			return null;
 		}
-		return ((GCalendarSimple) this.calendar).toDayAsDate(new InstantDay(nday));
+		return this.calendar.toDayAsDate(new Wink(nday));
 	}
 
 	public int daysInWeek() {
 		return 7 - closedDayOfWeek.size();
 	}
 
-	public Instant convert(DayAsDate day) {
+	public Wink convert(DayAsDate day) {
 		return calendar.fromDayAsDate(day);
 	}
 
@@ -567,8 +462,6 @@ public class GanttDiagram extends TitledDiagram implements Subject {
 		return getDefaultPlan().getLoadAt(convert(day)) > 0;
 	}
 
-	private final Map<String, Resource> resources = new LinkedHashMap<String, Resource>();
-
 	public void affectResource(Task result, String description) {
 		final Pattern p = Pattern.compile("([^:]+)(:(\\d+))?");
 		final Matcher m = p.matcher(description);
@@ -586,13 +479,13 @@ public class GanttDiagram extends TitledDiagram implements Subject {
 	public Resource getResource(String resourceName) {
 		Resource resource = resources.get(resourceName);
 		if (resource == null) {
-			resource = new Resource(resourceName, getDefaultPlan(), getCalendarSimple());
+			resource = new Resource(resourceName, getDefaultPlan(), calendar);
 		}
 		resources.put(resourceName, resource);
 		return resource;
 	}
 
-	public int getLoadForResource(Resource res, Instant i) {
+	public int getLoadForResource(Resource res, Wink i) {
 		int result = 0;
 		for (Task task : tasks.values()) {
 			if (task instanceof TaskSeparator) {
@@ -604,9 +497,6 @@ public class GanttDiagram extends TitledDiagram implements Subject {
 		return result;
 	}
 
-	private final Map<DayAsDate, HtmlColor> colorDays = new HashMap<DayAsDate, HtmlColor>();
-	private final Map<DayAsDate, String> nameDays = new HashMap<DayAsDate, String>();
-
 	public Moment getExistingMoment(String id) {
 		Moment result = getExistingTask(id);
 		if (result == null) {
@@ -661,8 +551,6 @@ public class GanttDiagram extends TitledDiagram implements Subject {
 		colorDay(today, colors.getCenter());
 	}
 
-	private DayAsDate today;
-
 	public CommandExecutionResult setToday(DayAsDate date) {
 		this.today = date;
 		return CommandExecutionResult.ok();
@@ -673,4 +561,9 @@ public class GanttDiagram extends TitledDiagram implements Subject {
 		return CommandExecutionResult.ok();
 	}
 
+	public void setPrintInterval(DayAsDate start, DayAsDate end) {
+		this.printStart = start;
+		this.printEnd = end;
+	}
+
 }
diff --git a/src/net/sourceforge/plantuml/project3/GanttDiagramFactory.java b/src/net/sourceforge/plantuml/project/GanttDiagramFactory.java
similarity index 74%
rename from src/net/sourceforge/plantuml/project3/GanttDiagramFactory.java
rename to src/net/sourceforge/plantuml/project/GanttDiagramFactory.java
index 06ae4948f..d97972001 100644
--- a/src/net/sourceforge/plantuml/project3/GanttDiagramFactory.java
+++ b/src/net/sourceforge/plantuml/project/GanttDiagramFactory.java
@@ -33,7 +33,7 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -45,11 +45,30 @@ import net.sourceforge.plantuml.command.CommandNope;
 import net.sourceforge.plantuml.command.CommandScale;
 import net.sourceforge.plantuml.command.UmlDiagramFactory;
 import net.sourceforge.plantuml.core.DiagramType;
+import net.sourceforge.plantuml.project.command.CommandGanttArrow;
+import net.sourceforge.plantuml.project.command.CommandGanttArrow2;
+import net.sourceforge.plantuml.project.command.CommandPage;
+import net.sourceforge.plantuml.project.command.CommandPrintBetween;
+import net.sourceforge.plantuml.project.command.CommandPrintScale;
+import net.sourceforge.plantuml.project.command.CommandSeparator;
+import net.sourceforge.plantuml.project.command.NaturalCommand;
+import net.sourceforge.plantuml.project.command.NaturalCommandAnd;
+import net.sourceforge.plantuml.project.command.NaturalCommandAndAnd;
+import net.sourceforge.plantuml.project.lang.ComplementPattern;
+import net.sourceforge.plantuml.project.lang.SubjectDayAsDate;
+import net.sourceforge.plantuml.project.lang.SubjectDayOfWeek;
+import net.sourceforge.plantuml.project.lang.SubjectDaysAsDates;
+import net.sourceforge.plantuml.project.lang.SubjectPattern;
+import net.sourceforge.plantuml.project.lang.SubjectProject;
+import net.sourceforge.plantuml.project.lang.SubjectResource;
+import net.sourceforge.plantuml.project.lang.SubjectTask;
+import net.sourceforge.plantuml.project.lang.SubjectToday;
+import net.sourceforge.plantuml.project.lang.VerbPattern;
 
 public class GanttDiagramFactory extends UmlDiagramFactory {
 
 	static private final List<SubjectPattern> subjects() {
-		return Arrays.<SubjectPattern> asList(new SubjectTask(), new SubjectProject(), new SubjectDayOfWeek(),
+		return Arrays.<SubjectPattern>asList(new SubjectTask(), new SubjectProject(), new SubjectDayOfWeek(),
 				new SubjectDayAsDate(), new SubjectDaysAsDates(), new SubjectResource(), new SubjectToday());
 	}
 
@@ -70,6 +89,8 @@ public class GanttDiagramFactory extends UmlDiagramFactory {
 		cmds.add(new CommandGanttArrow2());
 		cmds.add(new CommandSeparator());
 
+		cmds.add(new CommandPrintScale());
+		cmds.add(new CommandPrintBetween());
 		cmds.add(new CommandScale());
 		cmds.add(new CommandPage());
 		// cmds.add(new CommandScaleWidthAndHeight());
@@ -102,7 +123,8 @@ public class GanttDiagramFactory extends UmlDiagramFactory {
 							}
 							for (ComplementPattern complement1 : verb1.getComplements()) {
 								for (ComplementPattern complement2 : verb2.getComplements()) {
-									cache.add(NaturalCommandAnd.create(subject, verb1, complement1, verb2, complement2));
+									cache.add(
+											NaturalCommandAnd.create(subject, verb1, complement1, verb2, complement2));
 								}
 							}
 						}
diff --git a/src/net/sourceforge/plantuml/project3/Load.java b/src/net/sourceforge/plantuml/project/Load.java
similarity index 78%
rename from src/net/sourceforge/plantuml/project3/Load.java
rename to src/net/sourceforge/plantuml/project/Load.java
index d74b619a3..74d807464 100644
--- a/src/net/sourceforge/plantuml/project3/Load.java
+++ b/src/net/sourceforge/plantuml/project/Load.java
@@ -33,10 +33,24 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project;
 
-public interface Load extends Value, Complement {
+import net.sourceforge.plantuml.project.lang.Complement;
 
-	int getFullLoad();
+public class Load implements Value, Complement {
+
+	private final int winks;
+
+	private Load(int winks) {
+		this.winks = winks;
+	}
+
+	public static Load inWinks(int winks) {
+		return new Load(winks);
+	}
+
+	public int getFullLoad() {
+		return winks * 100;
+	}
 
 }
diff --git a/src/net/sourceforge/plantuml/project3/LoadPlanable.java b/src/net/sourceforge/plantuml/project/LoadPlanable.java
similarity index 90%
rename from src/net/sourceforge/plantuml/project3/LoadPlanable.java
rename to src/net/sourceforge/plantuml/project/LoadPlanable.java
index 11560e6c1..15a909c7c 100644
--- a/src/net/sourceforge/plantuml/project3/LoadPlanable.java
+++ b/src/net/sourceforge/plantuml/project/LoadPlanable.java
@@ -33,9 +33,11 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project;
+
+import net.sourceforge.plantuml.project.core.Wink;
 
 public interface LoadPlanable {
 
-	public int getLoadAt(Instant instant);
+	public int getLoadAt(Wink instant);
 }
diff --git a/src/net/sourceforge/plantuml/project3/PlanUtils.java b/src/net/sourceforge/plantuml/project/PlanUtils.java
similarity index 90%
rename from src/net/sourceforge/plantuml/project3/PlanUtils.java
rename to src/net/sourceforge/plantuml/project/PlanUtils.java
index 8afc8e87c..0d1ce7b18 100644
--- a/src/net/sourceforge/plantuml/project3/PlanUtils.java
+++ b/src/net/sourceforge/plantuml/project/PlanUtils.java
@@ -33,7 +33,9 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project;
+
+import net.sourceforge.plantuml.project.core.Wink;
 
 public class PlanUtils {
 
@@ -43,7 +45,7 @@ public class PlanUtils {
 
 	public static LoadPlanable minOf(final LoadPlanable p1, final LoadPlanable p2) {
 		return new LoadPlanable() {
-			public int getLoadAt(Instant instant) {
+			public int getLoadAt(Wink instant) {
 				return Math.min(p1.getLoadAt(instant), p2.getLoadAt(instant));
 			}
 		};
@@ -51,7 +53,7 @@ public class PlanUtils {
 
 	public static LoadPlanable multiply(final LoadPlanable p1, final LoadPlanable p2) {
 		return new LoadPlanable() {
-			public int getLoadAt(Instant instant) {
+			public int getLoadAt(Wink instant) {
 				return p1.getLoadAt(instant) * p2.getLoadAt(instant) / 100;
 			}
 		};
diff --git a/src/net/sourceforge/plantuml/project3/Solver3.java b/src/net/sourceforge/plantuml/project/Solver3.java
similarity index 84%
rename from src/net/sourceforge/plantuml/project3/Solver3.java
rename to src/net/sourceforge/plantuml/project/Solver3.java
index 203517de2..976ab40bd 100644
--- a/src/net/sourceforge/plantuml/project3/Solver3.java
+++ b/src/net/sourceforge/plantuml/project/Solver3.java
@@ -33,13 +33,16 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project;
 
 import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.Map;
 import java.util.Map.Entry;
 
+import net.sourceforge.plantuml.project.core.TaskAttribute;
+import net.sourceforge.plantuml.project.core.Wink;
+
 public class Solver3 {
 
 	private final Map<TaskAttribute, Value> values = new LinkedHashMap<TaskAttribute, Value>();
@@ -53,8 +56,8 @@ public class Solver3 {
 	public void setData(TaskAttribute attribute, Value value) {
 		final Value previous = values.remove(attribute);
 		if (previous != null && attribute == TaskAttribute.START) {
-			final Instant previousInstant = (Instant) previous;
-			if (previousInstant.compareTo((Instant) value) > 0) {
+			final Wink previousInstant = (Wink) previous;
+			if (previousInstant.compareTo((Wink) value) > 0) {
 				value = previous;
 			}
 		}
@@ -81,14 +84,14 @@ public class Solver3 {
 			if (attribute == TaskAttribute.START) {
 				return computeStart();
 			}
-			return LoadInDays.inDay(1);
+			return Load.inWinks(1);
 			// throw new UnsupportedOperationException(attribute.toString());
 		}
 		return result;
 	}
 
-	private Instant computeEnd() {
-		Instant current = (Instant) values.get(TaskAttribute.START);
+	private Wink computeEnd() {
+		Wink current = (Wink) values.get(TaskAttribute.START);
 		int fullLoad = ((Load) values.get(TaskAttribute.LOAD)).getFullLoad();
 		while (fullLoad > 0) {
 			fullLoad -= loadPlanable.getLoadAt(current);
@@ -97,12 +100,15 @@ public class Solver3 {
 		return current.decrement();
 	}
 
-	private Instant computeStart() {
-		Instant current = (Instant) values.get(TaskAttribute.END);
+	private Wink computeStart() {
+		Wink current = (Wink) values.get(TaskAttribute.END);
 		int fullLoad = ((Load) values.get(TaskAttribute.LOAD)).getFullLoad();
 		while (fullLoad > 0) {
 			fullLoad -= loadPlanable.getLoadAt(current);
 			current = current.decrement();
+			if (current.getWink() <= 0) {
+				return current;
+			}
 		}
 		return current.increment();
 	}
diff --git a/src/net/sourceforge/plantuml/project3/Today.java b/src/net/sourceforge/plantuml/project/Today.java
similarity index 93%
rename from src/net/sourceforge/plantuml/project3/Today.java
rename to src/net/sourceforge/plantuml/project/Today.java
index 9a15b24b8..f7c03b3d5 100644
--- a/src/net/sourceforge/plantuml/project3/Today.java
+++ b/src/net/sourceforge/plantuml/project/Today.java
@@ -33,7 +33,9 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project;
+
+import net.sourceforge.plantuml.project.lang.Subject;
 
 public class Today implements Subject {
 
diff --git a/src/net/sourceforge/plantuml/project3/Value.java b/src/net/sourceforge/plantuml/project/Value.java
similarity index 96%
rename from src/net/sourceforge/plantuml/project3/Value.java
rename to src/net/sourceforge/plantuml/project/Value.java
index 18df4746b..59f98828a 100644
--- a/src/net/sourceforge/plantuml/project3/Value.java
+++ b/src/net/sourceforge/plantuml/project/Value.java
@@ -33,7 +33,7 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project;
 
 public interface Value {
 
diff --git a/src/net/sourceforge/plantuml/project3/CommandGanttArrow.java b/src/net/sourceforge/plantuml/project/command/CommandGanttArrow.java
similarity index 89%
rename from src/net/sourceforge/plantuml/project3/CommandGanttArrow.java
rename to src/net/sourceforge/plantuml/project/command/CommandGanttArrow.java
index 6d47510ba..72416d7f8 100644
--- a/src/net/sourceforge/plantuml/project3/CommandGanttArrow.java
+++ b/src/net/sourceforge/plantuml/project/command/CommandGanttArrow.java
@@ -33,7 +33,7 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.command;
 
 import net.sourceforge.plantuml.LineLocation;
 import net.sourceforge.plantuml.command.CommandExecutionResult;
@@ -42,6 +42,11 @@ import net.sourceforge.plantuml.command.regex.IRegex;
 import net.sourceforge.plantuml.command.regex.RegexConcat;
 import net.sourceforge.plantuml.command.regex.RegexLeaf;
 import net.sourceforge.plantuml.command.regex.RegexResult;
+import net.sourceforge.plantuml.project.GanttConstraint;
+import net.sourceforge.plantuml.project.GanttDiagram;
+import net.sourceforge.plantuml.project.core.Task;
+import net.sourceforge.plantuml.project.core.TaskAttribute;
+import net.sourceforge.plantuml.project.core.TaskInstant;
 
 public class CommandGanttArrow extends SingleLineCommand2<GanttDiagram> {
 
diff --git a/src/net/sourceforge/plantuml/project3/CommandGanttArrow2.java b/src/net/sourceforge/plantuml/project/command/CommandGanttArrow2.java
similarity index 94%
rename from src/net/sourceforge/plantuml/project3/CommandGanttArrow2.java
rename to src/net/sourceforge/plantuml/project/command/CommandGanttArrow2.java
index 33283a808..900dfce8c 100644
--- a/src/net/sourceforge/plantuml/project3/CommandGanttArrow2.java
+++ b/src/net/sourceforge/plantuml/project/command/CommandGanttArrow2.java
@@ -33,7 +33,7 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.command;
 
 import net.sourceforge.plantuml.LineLocation;
 import net.sourceforge.plantuml.command.CommandExecutionResult;
@@ -42,6 +42,8 @@ import net.sourceforge.plantuml.command.regex.IRegex;
 import net.sourceforge.plantuml.command.regex.RegexConcat;
 import net.sourceforge.plantuml.command.regex.RegexLeaf;
 import net.sourceforge.plantuml.command.regex.RegexResult;
+import net.sourceforge.plantuml.project.GanttDiagram;
+import net.sourceforge.plantuml.project.core.Task;
 
 public class CommandGanttArrow2 extends SingleLineCommand2<GanttDiagram> {
 
diff --git a/src/net/sourceforge/plantuml/project3/CommandPage.java b/src/net/sourceforge/plantuml/project/command/CommandPage.java
similarity index 96%
rename from src/net/sourceforge/plantuml/project3/CommandPage.java
rename to src/net/sourceforge/plantuml/project/command/CommandPage.java
index 8c62678b8..60da3b674 100644
--- a/src/net/sourceforge/plantuml/project3/CommandPage.java
+++ b/src/net/sourceforge/plantuml/project/command/CommandPage.java
@@ -33,7 +33,7 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.command;
 
 import net.sourceforge.plantuml.LineLocation;
 import net.sourceforge.plantuml.command.CommandExecutionResult;
@@ -42,6 +42,7 @@ import net.sourceforge.plantuml.command.regex.IRegex;
 import net.sourceforge.plantuml.command.regex.RegexConcat;
 import net.sourceforge.plantuml.command.regex.RegexLeaf;
 import net.sourceforge.plantuml.command.regex.RegexResult;
+import net.sourceforge.plantuml.project.GanttDiagram;
 
 public class CommandPage extends SingleLineCommand2<GanttDiagram> {
 
diff --git a/src/net/sourceforge/plantuml/project/command/CommandPrintBetween.java b/src/net/sourceforge/plantuml/project/command/CommandPrintBetween.java
new file mode 100644
index 000000000..ed436bbc1
--- /dev/null
+++ b/src/net/sourceforge/plantuml/project/command/CommandPrintBetween.java
@@ -0,0 +1,84 @@
+/* ========================================================================
+ * PlantUML : a free UML diagram generator
+ * ========================================================================
+ *
+ * (C) Copyright 2009-2020, Arnaud Roques
+ *
+ * Project Info:  http://plantuml.com
+ * 
+ * If you like this project or if you find it useful, you can support us at:
+ * 
+ * http://plantuml.com/patreon (only 1$ per month!)
+ * http://plantuml.com/paypal
+ * 
+ * This file is part of PlantUML.
+ *
+ * PlantUML is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * PlantUML distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
+ * USA.
+ *
+ *
+ * Original Author:  Arnaud Roques
+ * 
+ *
+ */
+package net.sourceforge.plantuml.project.command;
+
+import net.sourceforge.plantuml.LineLocation;
+import net.sourceforge.plantuml.command.CommandExecutionResult;
+import net.sourceforge.plantuml.command.SingleLineCommand2;
+import net.sourceforge.plantuml.command.regex.IRegex;
+import net.sourceforge.plantuml.command.regex.RegexConcat;
+import net.sourceforge.plantuml.command.regex.RegexLeaf;
+import net.sourceforge.plantuml.command.regex.RegexOr;
+import net.sourceforge.plantuml.command.regex.RegexResult;
+import net.sourceforge.plantuml.project.DayAsDate;
+import net.sourceforge.plantuml.project.Failable;
+import net.sourceforge.plantuml.project.GanttDiagram;
+import net.sourceforge.plantuml.project.core.PrintScale;
+import net.sourceforge.plantuml.project.lang.Complement;
+import net.sourceforge.plantuml.project.lang.ComplementDate;
+
+public class CommandPrintBetween extends SingleLineCommand2<GanttDiagram> {
+
+	private static final ComplementDate pattern = new ComplementDate();
+
+	public CommandPrintBetween() {
+		super(getRegexConcat());
+	}
+
+	static IRegex getRegexConcat() {
+		return RegexConcat.build(CommandPrintBetween.class.getName(), RegexLeaf.start(), //
+				// Print between 2020/02/14 and 2020/03/04
+				new RegexLeaf("print"), //
+				RegexLeaf.spaceOneOrMore(), //
+				new RegexLeaf("between"), //
+				RegexLeaf.spaceOneOrMore(), //
+				pattern.toRegex("START"), //
+				RegexLeaf.spaceOneOrMore(), //
+				new RegexLeaf("and"), //
+				RegexLeaf.spaceOneOrMore(), //
+				pattern.toRegex("END"), //
+				RegexLeaf.end()); //
+	}
+
+	@Override
+	protected CommandExecutionResult executeArg(GanttDiagram diagram, LineLocation location, RegexResult arg) {
+		final DayAsDate start = (DayAsDate) pattern.getComplement(diagram, arg, "START").get();
+		final DayAsDate end = (DayAsDate) pattern.getComplement(diagram, arg, "END").get();
+		diagram.setPrintInterval(start, end);
+		return CommandExecutionResult.ok();
+	}
+
+}
diff --git a/src/net/sourceforge/plantuml/project/command/CommandPrintScale.java b/src/net/sourceforge/plantuml/project/command/CommandPrintScale.java
new file mode 100644
index 000000000..64c09ceda
--- /dev/null
+++ b/src/net/sourceforge/plantuml/project/command/CommandPrintScale.java
@@ -0,0 +1,73 @@
+/* ========================================================================
+ * PlantUML : a free UML diagram generator
+ * ========================================================================
+ *
+ * (C) Copyright 2009-2020, Arnaud Roques
+ *
+ * Project Info:  http://plantuml.com
+ * 
+ * If you like this project or if you find it useful, you can support us at:
+ * 
+ * http://plantuml.com/patreon (only 1$ per month!)
+ * http://plantuml.com/paypal
+ * 
+ * This file is part of PlantUML.
+ *
+ * PlantUML is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * PlantUML distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
+ * USA.
+ *
+ *
+ * Original Author:  Arnaud Roques
+ * 
+ *
+ */
+package net.sourceforge.plantuml.project.command;
+
+import net.sourceforge.plantuml.LineLocation;
+import net.sourceforge.plantuml.command.CommandExecutionResult;
+import net.sourceforge.plantuml.command.SingleLineCommand2;
+import net.sourceforge.plantuml.command.regex.IRegex;
+import net.sourceforge.plantuml.command.regex.RegexConcat;
+import net.sourceforge.plantuml.command.regex.RegexLeaf;
+import net.sourceforge.plantuml.command.regex.RegexOr;
+import net.sourceforge.plantuml.command.regex.RegexResult;
+import net.sourceforge.plantuml.project.GanttDiagram;
+import net.sourceforge.plantuml.project.core.PrintScale;
+
+public class CommandPrintScale extends SingleLineCommand2<GanttDiagram> {
+
+	public CommandPrintScale() {
+		super(getRegexConcat());
+	}
+
+	static IRegex getRegexConcat() {
+		return RegexConcat.build(CommandPrintScale.class.getName(), RegexLeaf.start(), //
+				new RegexLeaf("printscale"), //
+				RegexLeaf.spaceOneOrMore(), //
+				new RegexOr("SCALE", //
+						new RegexLeaf("daily"), //
+						new RegexLeaf("weekly")), //
+				RegexLeaf.end()); //
+	}
+
+	@Override
+	protected CommandExecutionResult executeArg(GanttDiagram diagram, LineLocation location, RegexResult arg) {
+		final String scaleString = arg.get("SCALE", 0);
+		final PrintScale scale = PrintScale.fromString(scaleString);
+		diagram.setPrintScale(scale);
+		return CommandExecutionResult.ok();
+	}
+
+}
diff --git a/src/net/sourceforge/plantuml/project3/CommandSeparator.java b/src/net/sourceforge/plantuml/project/command/CommandSeparator.java
similarity index 95%
rename from src/net/sourceforge/plantuml/project3/CommandSeparator.java
rename to src/net/sourceforge/plantuml/project/command/CommandSeparator.java
index 15fc31546..11e9bbfca 100644
--- a/src/net/sourceforge/plantuml/project3/CommandSeparator.java
+++ b/src/net/sourceforge/plantuml/project/command/CommandSeparator.java
@@ -33,7 +33,7 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.command;
 
 import net.sourceforge.plantuml.LineLocation;
 import net.sourceforge.plantuml.command.CommandExecutionResult;
@@ -42,6 +42,7 @@ import net.sourceforge.plantuml.command.regex.IRegex;
 import net.sourceforge.plantuml.command.regex.RegexConcat;
 import net.sourceforge.plantuml.command.regex.RegexLeaf;
 import net.sourceforge.plantuml.command.regex.RegexResult;
+import net.sourceforge.plantuml.project.GanttDiagram;
 
 public class CommandSeparator extends SingleLineCommand2<GanttDiagram> {
 
diff --git a/src/net/sourceforge/plantuml/project3/NaturalCommand.java b/src/net/sourceforge/plantuml/project/command/NaturalCommand.java
similarity index 86%
rename from src/net/sourceforge/plantuml/project3/NaturalCommand.java
rename to src/net/sourceforge/plantuml/project/command/NaturalCommand.java
index d3b11bbaa..4266907ae 100644
--- a/src/net/sourceforge/plantuml/project3/NaturalCommand.java
+++ b/src/net/sourceforge/plantuml/project/command/NaturalCommand.java
@@ -33,7 +33,7 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.command;
 
 import net.sourceforge.plantuml.LineLocation;
 import net.sourceforge.plantuml.command.Command;
@@ -42,6 +42,15 @@ import net.sourceforge.plantuml.command.SingleLineCommand2;
 import net.sourceforge.plantuml.command.regex.RegexConcat;
 import net.sourceforge.plantuml.command.regex.RegexLeaf;
 import net.sourceforge.plantuml.command.regex.RegexResult;
+import net.sourceforge.plantuml.project.Failable;
+import net.sourceforge.plantuml.project.GanttDiagram;
+import net.sourceforge.plantuml.project.lang.Complement;
+import net.sourceforge.plantuml.project.lang.ComplementEmpty;
+import net.sourceforge.plantuml.project.lang.ComplementPattern;
+import net.sourceforge.plantuml.project.lang.Subject;
+import net.sourceforge.plantuml.project.lang.SubjectPattern;
+import net.sourceforge.plantuml.project.lang.Verb;
+import net.sourceforge.plantuml.project.lang.VerbPattern;
 
 public class NaturalCommand extends SingleLineCommand2<GanttDiagram> {
 
diff --git a/src/net/sourceforge/plantuml/project3/NaturalCommandAnd.java b/src/net/sourceforge/plantuml/project/command/NaturalCommandAnd.java
similarity index 89%
rename from src/net/sourceforge/plantuml/project3/NaturalCommandAnd.java
rename to src/net/sourceforge/plantuml/project/command/NaturalCommandAnd.java
index 82346d993..663c0d8b1 100644
--- a/src/net/sourceforge/plantuml/project3/NaturalCommandAnd.java
+++ b/src/net/sourceforge/plantuml/project/command/NaturalCommandAnd.java
@@ -33,7 +33,7 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.command;
 
 import net.sourceforge.plantuml.LineLocation;
 import net.sourceforge.plantuml.command.Command;
@@ -42,6 +42,14 @@ import net.sourceforge.plantuml.command.SingleLineCommand2;
 import net.sourceforge.plantuml.command.regex.RegexConcat;
 import net.sourceforge.plantuml.command.regex.RegexLeaf;
 import net.sourceforge.plantuml.command.regex.RegexResult;
+import net.sourceforge.plantuml.project.Failable;
+import net.sourceforge.plantuml.project.GanttDiagram;
+import net.sourceforge.plantuml.project.lang.Complement;
+import net.sourceforge.plantuml.project.lang.ComplementPattern;
+import net.sourceforge.plantuml.project.lang.Subject;
+import net.sourceforge.plantuml.project.lang.SubjectPattern;
+import net.sourceforge.plantuml.project.lang.Verb;
+import net.sourceforge.plantuml.project.lang.VerbPattern;
 
 public class NaturalCommandAnd extends SingleLineCommand2<GanttDiagram> {
 
diff --git a/src/net/sourceforge/plantuml/project3/NaturalCommandAndAnd.java b/src/net/sourceforge/plantuml/project/command/NaturalCommandAndAnd.java
similarity index 91%
rename from src/net/sourceforge/plantuml/project3/NaturalCommandAndAnd.java
rename to src/net/sourceforge/plantuml/project/command/NaturalCommandAndAnd.java
index 4395d8be9..63b0d8080 100644
--- a/src/net/sourceforge/plantuml/project3/NaturalCommandAndAnd.java
+++ b/src/net/sourceforge/plantuml/project/command/NaturalCommandAndAnd.java
@@ -33,7 +33,7 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.command;
 
 import net.sourceforge.plantuml.LineLocation;
 import net.sourceforge.plantuml.command.Command;
@@ -42,6 +42,13 @@ import net.sourceforge.plantuml.command.SingleLineCommand2;
 import net.sourceforge.plantuml.command.regex.RegexConcat;
 import net.sourceforge.plantuml.command.regex.RegexLeaf;
 import net.sourceforge.plantuml.command.regex.RegexResult;
+import net.sourceforge.plantuml.project.GanttDiagram;
+import net.sourceforge.plantuml.project.lang.Complement;
+import net.sourceforge.plantuml.project.lang.ComplementPattern;
+import net.sourceforge.plantuml.project.lang.Subject;
+import net.sourceforge.plantuml.project.lang.SubjectPattern;
+import net.sourceforge.plantuml.project.lang.Verb;
+import net.sourceforge.plantuml.project.lang.VerbPattern;
 
 public class NaturalCommandAndAnd extends SingleLineCommand2<GanttDiagram> {
 
diff --git a/src/net/sourceforge/plantuml/project3/Moment.java b/src/net/sourceforge/plantuml/project/core/Moment.java
similarity index 92%
rename from src/net/sourceforge/plantuml/project3/Moment.java
rename to src/net/sourceforge/plantuml/project/core/Moment.java
index fb18a4396..cea85f99a 100644
--- a/src/net/sourceforge/plantuml/project3/Moment.java
+++ b/src/net/sourceforge/plantuml/project/core/Moment.java
@@ -33,12 +33,12 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.core;
 
 public interface Moment {
 
-	public Instant getStart();
+	public Wink getStart();
 
-	public Instant getEnd();
+	public Wink getEnd();
 
 }
diff --git a/src/net/sourceforge/plantuml/project3/MomentImpl.java b/src/net/sourceforge/plantuml/project/core/MomentImpl.java
similarity index 87%
rename from src/net/sourceforge/plantuml/project3/MomentImpl.java
rename to src/net/sourceforge/plantuml/project/core/MomentImpl.java
index a56938456..410d3059b 100644
--- a/src/net/sourceforge/plantuml/project3/MomentImpl.java
+++ b/src/net/sourceforge/plantuml/project/core/MomentImpl.java
@@ -33,23 +33,23 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.core;
 
 public class MomentImpl implements Moment {
 
-	private final Instant start;
-	private final Instant end;
+	private final Wink start;
+	private final Wink end;
 
-	public MomentImpl(Instant start, Instant end) {
+	public MomentImpl(Wink start, Wink end) {
 		this.start = start;
 		this.end = end;
 	}
 
-	public Instant getStart() {
+	public Wink getStart() {
 		return start;
 	}
 
-	public Instant getEnd() {
+	public Wink getEnd() {
 		return end;
 	}
 
diff --git a/src/net/sourceforge/plantuml/project3/Month.java b/src/net/sourceforge/plantuml/project/core/Month.java
similarity index 92%
rename from src/net/sourceforge/plantuml/project3/Month.java
rename to src/net/sourceforge/plantuml/project/core/Month.java
index 64fe1f816..73528c4fc 100644
--- a/src/net/sourceforge/plantuml/project3/Month.java
+++ b/src/net/sourceforge/plantuml/project/core/Month.java
@@ -33,7 +33,7 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.core;
 
 import net.sourceforge.plantuml.StringUtils;
 
@@ -48,6 +48,14 @@ public enum Month {
 		this.daysPerMonth = daysPerMonth;
 	}
 
+	public String shortName() {
+		return niceName().substring(0, 3);
+	}
+
+	public String niceName() {
+		return StringUtils.capitalize(name());
+	}
+
 	static public String getRegexString() {
 		final StringBuilder sb = new StringBuilder();
 		for (Month month : Month.values()) {
@@ -83,4 +91,5 @@ public enum Month {
 	public int m() {
 		return 3 + (ordinal() + 10) % 12;
 	}
+
 }
diff --git a/src/net/sourceforge/plantuml/project3/GCalendar.java b/src/net/sourceforge/plantuml/project/core/PrintScale.java
similarity index 85%
rename from src/net/sourceforge/plantuml/project3/GCalendar.java
rename to src/net/sourceforge/plantuml/project/core/PrintScale.java
index b621e33e8..ecde6640c 100644
--- a/src/net/sourceforge/plantuml/project3/GCalendar.java
+++ b/src/net/sourceforge/plantuml/project/core/PrintScale.java
@@ -33,14 +33,15 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.core;
 
-public interface GCalendar {
-
-	public DayAsDate toDayAsDate(InstantDay day);
-
-	public DayAsDate getStartingDate();
-	
-	public InstantDay fromDayAsDate(DayAsDate day);
+public enum PrintScale {
+	DAILY, WEEKLY;
 
+	static public PrintScale fromString(String value) {
+		if (value.startsWith("w")) {
+			return WEEKLY;
+		}
+		return DAILY;
+	}
 }
diff --git a/src/net/sourceforge/plantuml/project3/Resource.java b/src/net/sourceforge/plantuml/project/core/Resource.java
similarity index 79%
rename from src/net/sourceforge/plantuml/project3/Resource.java
rename to src/net/sourceforge/plantuml/project/core/Resource.java
index 54eb1d9b5..a5cb17364 100644
--- a/src/net/sourceforge/plantuml/project3/Resource.java
+++ b/src/net/sourceforge/plantuml/project/core/Resource.java
@@ -33,19 +33,26 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.core;
 
 import java.util.Collection;
 import java.util.EnumSet;
 import java.util.Set;
 import java.util.TreeSet;
 
+import net.sourceforge.plantuml.project.DayAsDate;
+import net.sourceforge.plantuml.project.DayOfWeek;
+import net.sourceforge.plantuml.project.GCalendar;
+import net.sourceforge.plantuml.project.LoadPlanable;
+import net.sourceforge.plantuml.project.draw.ResourceDraw;
+import net.sourceforge.plantuml.project.lang.Subject;
+
 public class Resource implements Subject {
 
 	private final String name;
 	private ResourceDraw draw;
-	private final Set<Instant> closed = new TreeSet<Instant>();
-	private final Set<Instant> forcedOn = new TreeSet<Instant>();
+	private final Set<Wink> closed = new TreeSet<Wink>();
+	private final Set<Wink> forcedOn = new TreeSet<Wink>();
 	private final GCalendar calendar;
 
 	private final Collection<DayOfWeek> closedDayOfWeek = EnumSet.noneOf(DayOfWeek.class);
@@ -83,12 +90,12 @@ public class Resource implements Subject {
 		this.draw = draw;
 	}
 
-	public boolean isClosedAt(Instant instant) {
+	public boolean isClosedAt(Wink instant) {
 		if (this.forcedOn.contains(instant)) {
 			return false;
 		}
 		if (closedDayOfWeek.size() > 0 && calendar != null) {
-			final DayAsDate d = calendar.toDayAsDate((InstantDay) instant);
+			final DayAsDate d = calendar.toDayAsDate((Wink) instant);
 			if (closedDayOfWeek.contains(d.getDayOfWeek())) {
 				return true;
 			}
@@ -96,11 +103,11 @@ public class Resource implements Subject {
 		return this.closed.contains(instant);
 	}
 
-	public void addCloseDay(Instant instant) {
+	public void addCloseDay(Wink instant) {
 		this.closed.add(instant);
 	}
 
-	public void addForceOnDay(Instant instant) {
+	public void addForceOnDay(Wink instant) {
 		this.forcedOn.add(instant);
 	}
 
diff --git a/src/net/sourceforge/plantuml/project3/Task.java b/src/net/sourceforge/plantuml/project/core/Task.java
similarity index 79%
rename from src/net/sourceforge/plantuml/project3/Task.java
rename to src/net/sourceforge/plantuml/project/core/Task.java
index 0f2e8410f..74353fc61 100644
--- a/src/net/sourceforge/plantuml/project3/Task.java
+++ b/src/net/sourceforge/plantuml/project/core/Task.java
@@ -33,23 +33,28 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.core;
+
+import net.sourceforge.plantuml.project.Load;
+import net.sourceforge.plantuml.project.draw.TaskDraw;
+import net.sourceforge.plantuml.project.lang.ComplementColors;
+import net.sourceforge.plantuml.project.lang.Subject;
 
 public interface Task extends Subject, Moment {
 
 	public TaskCode getCode();
 
-	public Instant getStart();
+	public Wink getStart();
 
-	public Instant getEnd();
+	public Wink getEnd();
 
 	public Load getLoad();
 
 	public void setLoad(Load load);
 
-	public void setStart(Instant start);
+	public void setStart(Wink start);
 
-	public void setEnd(Instant end);
+	public void setEnd(Wink end);
 
 	public void setTaskDraw(TaskDraw taskDraw);
 
@@ -63,5 +68,7 @@ public interface Task extends Subject, Moment {
 	
 	public boolean isDiamond();
 
+	public void setCompletion(int completion);
+
 
 }
diff --git a/src/net/sourceforge/plantuml/project3/TaskAttribute.java b/src/net/sourceforge/plantuml/project/core/TaskAttribute.java
similarity index 96%
rename from src/net/sourceforge/plantuml/project3/TaskAttribute.java
rename to src/net/sourceforge/plantuml/project/core/TaskAttribute.java
index f326e3778..6137ebd8e 100644
--- a/src/net/sourceforge/plantuml/project3/TaskAttribute.java
+++ b/src/net/sourceforge/plantuml/project/core/TaskAttribute.java
@@ -33,7 +33,7 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.core;
 
 public enum TaskAttribute {
 	START, END, LOAD;
diff --git a/src/net/sourceforge/plantuml/project3/TaskCode.java b/src/net/sourceforge/plantuml/project/core/TaskCode.java
similarity index 97%
rename from src/net/sourceforge/plantuml/project3/TaskCode.java
rename to src/net/sourceforge/plantuml/project/core/TaskCode.java
index e6466abc0..5ea518c0a 100644
--- a/src/net/sourceforge/plantuml/project3/TaskCode.java
+++ b/src/net/sourceforge/plantuml/project/core/TaskCode.java
@@ -33,7 +33,7 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.core;
 
 public class TaskCode {
 
diff --git a/src/net/sourceforge/plantuml/project3/TaskImpl.java b/src/net/sourceforge/plantuml/project/core/TaskImpl.java
similarity index 82%
rename from src/net/sourceforge/plantuml/project3/TaskImpl.java
rename to src/net/sourceforge/plantuml/project/core/TaskImpl.java
index f5ed4f63d..f800558f6 100644
--- a/src/net/sourceforge/plantuml/project3/TaskImpl.java
+++ b/src/net/sourceforge/plantuml/project/core/TaskImpl.java
@@ -33,12 +33,19 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.core;
 
 import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.Map;
 
+import net.sourceforge.plantuml.project.Load;
+import net.sourceforge.plantuml.project.LoadPlanable;
+import net.sourceforge.plantuml.project.PlanUtils;
+import net.sourceforge.plantuml.project.Solver3;
+import net.sourceforge.plantuml.project.draw.TaskDraw;
+import net.sourceforge.plantuml.project.lang.ComplementColors;
+
 public class TaskImpl implements Task, LoadPlanable {
 
 	private final TaskCode code;
@@ -51,11 +58,11 @@ public class TaskImpl implements Task, LoadPlanable {
 		this.code = code;
 		this.defaultPlan = defaultPlan;
 		this.solver = new Solver3(this);
-		setStart(new InstantDay(0));
-		setLoad(LoadInDays.inDay(1));
+		setStart(new Wink(0));
+		setLoad(Load.inWinks(1));
 	}
 
-	public int getLoadAt(Instant instant) {
+	public int getLoadAt(Wink instant) {
 		LoadPlanable result = defaultPlan;
 		if (resources2.size() > 0) {
 			result = PlanUtils.multiply(defaultPlan, getRessourcePlan());
@@ -64,7 +71,7 @@ public class TaskImpl implements Task, LoadPlanable {
 		// return PlanUtils.minOf(getLoad(), plan1).getLoadAt(instant);
 	}
 
-	public int loadForResource(Resource res, Instant instant) {
+	public int loadForResource(Resource res, Wink instant) {
 		if (resources2.keySet().contains(res) && instant.compareTo(getStart()) >= 0 && instant.compareTo(getEnd()) <= 0) {
 			if (res.isClosedAt(instant)) {
 				return 0;
@@ -87,7 +94,7 @@ public class TaskImpl implements Task, LoadPlanable {
 		}
 		return new LoadPlanable() {
 
-			public int getLoadAt(Instant instant) {
+			public int getLoadAt(Wink instant) {
 				int result = 0;
 				for (Map.Entry<Resource, Integer> ent : resources2.entrySet()) {
 					final Resource res = ent.getKey();
@@ -137,16 +144,16 @@ public class TaskImpl implements Task, LoadPlanable {
 		return code;
 	}
 
-	public Instant getStart() {
-		Instant result = (Instant) solver.getData(TaskAttribute.START);
+	public Wink getStart() {
+		Wink result = (Wink) solver.getData(TaskAttribute.START);
 		while (getLoadAt(result) == 0) {
 			result = result.increment();
 		}
 		return result;
 	}
 
-	public Instant getEnd() {
-		return (Instant) solver.getData(TaskAttribute.END);
+	public Wink getEnd() {
+		return (Wink) solver.getData(TaskAttribute.END);
 	}
 
 	public Load getLoad() {
@@ -157,11 +164,11 @@ public class TaskImpl implements Task, LoadPlanable {
 		solver.setData(TaskAttribute.LOAD, load);
 	}
 
-	public void setStart(Instant start) {
+	public void setStart(Wink start) {
 		solver.setData(TaskAttribute.START, start);
 	}
 
-	public void setEnd(Instant end) {
+	public void setEnd(Wink end) {
 		solver.setData(TaskAttribute.END, end);
 	}
 
@@ -169,7 +176,7 @@ public class TaskImpl implements Task, LoadPlanable {
 	private ComplementColors colors;
 
 	public void setTaskDraw(TaskDraw taskDraw) {
-		taskDraw.setColors(colors);
+		taskDraw.setColorsAndCompletion(colors, completion);
 		this.taskDraw = taskDraw;
 	}
 
@@ -192,5 +199,11 @@ public class TaskImpl implements Task, LoadPlanable {
 	public boolean isDiamond() {
 		return this.diamond;
 	}
+	
+	private int completion = 100;
+
+	public void setCompletion(int completion) {
+		this.completion = completion;
+	}
 
 }
diff --git a/src/net/sourceforge/plantuml/project3/TaskInstant.java b/src/net/sourceforge/plantuml/project/core/TaskInstant.java
similarity index 92%
rename from src/net/sourceforge/plantuml/project3/TaskInstant.java
rename to src/net/sourceforge/plantuml/project/core/TaskInstant.java
index de638c38f..3d4d72e1e 100644
--- a/src/net/sourceforge/plantuml/project3/TaskInstant.java
+++ b/src/net/sourceforge/plantuml/project/core/TaskInstant.java
@@ -33,7 +33,9 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.core;
+
+import net.sourceforge.plantuml.project.lang.Complement;
 
 public class TaskInstant implements Complement {
 
@@ -58,7 +60,7 @@ public class TaskInstant implements Complement {
 		return new TaskInstant(task, attribute, newDelta);
 	}
 
-	private Instant manageDelta(Instant value) {
+	private Wink manageDelta(Wink value) {
 		if (delta > 0) {
 			for (int i = 0; i < delta; i++) {
 				value = value.increment();
@@ -72,7 +74,7 @@ public class TaskInstant implements Complement {
 		return value;
 	}
 
-	public Instant getInstantPrecise() {
+	public Wink getInstantPrecise() {
 		if (attribute == TaskAttribute.START) {
 			return manageDelta(task.getStart());
 		}
@@ -82,7 +84,7 @@ public class TaskInstant implements Complement {
 		throw new IllegalStateException();
 	}
 
-	public Instant getInstantTheorical() {
+	public Wink getInstantTheorical() {
 		if (attribute == TaskAttribute.START) {
 			return manageDelta(task.getStart());
 		}
diff --git a/src/net/sourceforge/plantuml/project3/TaskSeparator.java b/src/net/sourceforge/plantuml/project/core/TaskSeparator.java
similarity index 85%
rename from src/net/sourceforge/plantuml/project3/TaskSeparator.java
rename to src/net/sourceforge/plantuml/project/core/TaskSeparator.java
index 093e82221..8f5942c74 100644
--- a/src/net/sourceforge/plantuml/project3/TaskSeparator.java
+++ b/src/net/sourceforge/plantuml/project/core/TaskSeparator.java
@@ -33,7 +33,11 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.core;
+
+import net.sourceforge.plantuml.project.Load;
+import net.sourceforge.plantuml.project.draw.TaskDraw;
+import net.sourceforge.plantuml.project.lang.ComplementColors;
 
 public class TaskSeparator implements Task {
 	// public static final double SPACE = 15;
@@ -51,19 +55,19 @@ public class TaskSeparator implements Task {
 		return code;
 	}
 
-	public Instant getStart() {
+	public Wink getStart() {
 		throw new UnsupportedOperationException();
 	}
 
-	public Instant getEnd() {
+	public Wink getEnd() {
 		throw new UnsupportedOperationException();
 	}
 
-	public void setStart(Instant start) {
+	public void setStart(Wink start) {
 		throw new UnsupportedOperationException();
 	}
 
-	public void setEnd(Instant end) {
+	public void setEnd(Wink end) {
 		throw new UnsupportedOperationException();
 
 	}
@@ -104,4 +108,8 @@ public class TaskSeparator implements Task {
 		throw new UnsupportedOperationException();
 	}
 
+	public void setCompletion(int completion) {
+		throw new UnsupportedOperationException();
+	}
+
 }
diff --git a/src/net/sourceforge/plantuml/project3/InstantDay.java b/src/net/sourceforge/plantuml/project/core/Wink.java
similarity index 72%
rename from src/net/sourceforge/plantuml/project3/InstantDay.java
rename to src/net/sourceforge/plantuml/project/core/Wink.java
index 4a198651d..94c83dba5 100644
--- a/src/net/sourceforge/plantuml/project3/InstantDay.java
+++ b/src/net/sourceforge/plantuml/project/core/Wink.java
@@ -33,39 +33,41 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.core;
 
-public class InstantDay implements Instant {
+import net.sourceforge.plantuml.project.Value;
 
-	private final int numDay;
+public class Wink implements Value, Comparable<Wink> {
 
-	public InstantDay(int numDay) {
-		this.numDay = numDay;
+	private final int wink;
+
+	public Wink(int wink) {
+		this.wink = wink;
 	}
 
 	@Override
 	public String toString() {
-		return "(day +" + numDay + ")";
+		return "(Wink +" + wink + ")";
 	}
 
-	public InstantDay increment() {
-		return new InstantDay(numDay + 1);
+	public Wink increment() {
+		return new Wink(wink + 1);
 	}
 
-	public InstantDay decrement() {
-		return new InstantDay(numDay - 1);
+	public Wink decrement() {
+		return new Wink(wink - 1);
 	}
 
-	final int getNumDay() {
-		return numDay;
+	public final int getWink() {
+		return wink;
 	}
 
-	public int compareTo(Instant other) {
-		return this.numDay - ((InstantDay) other).numDay;
+	public int compareTo(Wink other) {
+		return this.wink - other.wink;
 	}
 
 	public String toShortString() {
-		return "" + (numDay + 1);
+		return "" + (wink + 1);
 	}
 
 }
diff --git a/src/net/sourceforge/plantuml/project3/ResourceDraw.java b/src/net/sourceforge/plantuml/project/draw/ResourceDraw.java
similarity index 90%
rename from src/net/sourceforge/plantuml/project3/ResourceDraw.java
rename to src/net/sourceforge/plantuml/project/draw/ResourceDraw.java
index 2f7e4e768..e95c35054 100644
--- a/src/net/sourceforge/plantuml/project3/ResourceDraw.java
+++ b/src/net/sourceforge/plantuml/project/draw/ResourceDraw.java
@@ -33,7 +33,7 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.draw;
 
 import net.sourceforge.plantuml.SpriteContainerEmpty;
 import net.sourceforge.plantuml.cucadiagram.Display;
@@ -43,6 +43,10 @@ import net.sourceforge.plantuml.graphic.HtmlColor;
 import net.sourceforge.plantuml.graphic.HtmlColorUtils;
 import net.sourceforge.plantuml.graphic.TextBlock;
 import net.sourceforge.plantuml.graphic.UDrawable;
+import net.sourceforge.plantuml.project.GanttDiagram;
+import net.sourceforge.plantuml.project.core.Resource;
+import net.sourceforge.plantuml.project.core.Wink;
+import net.sourceforge.plantuml.project.timescale.TimeScale;
 import net.sourceforge.plantuml.ugraphic.UChangeColor;
 import net.sourceforge.plantuml.ugraphic.UFont;
 import net.sourceforge.plantuml.ugraphic.UGraphic;
@@ -54,11 +58,11 @@ public class ResourceDraw implements UDrawable {
 	private final Resource res;
 	private final TimeScale timeScale;
 	private final double y;
-	private final Instant min;
-	private final Instant max;
+	private final Wink min;
+	private final Wink max;
 	private final GanttDiagram gantt;
 
-	public ResourceDraw(GanttDiagram gantt, Resource res, TimeScale timeScale, double y, Instant min, Instant max) {
+	public ResourceDraw(GanttDiagram gantt, Resource res, TimeScale timeScale, double y, Wink min, Wink max) {
 		this.res = res;
 		this.timeScale = timeScale;
 		this.y = y;
@@ -74,7 +78,7 @@ public class ResourceDraw implements UDrawable {
 		final ULine line = new ULine(timeScale.getEndingPosition(max) - timeScale.getStartingPosition(min), 0);
 		ug.apply(new UChangeColor(HtmlColorUtils.BLACK))
 				.apply(new UTranslate(0, title.calculateDimension(ug.getStringBounder()).getHeight())).draw(line);
-		for (Instant i = min; i.compareTo(max) <= 0; i = i.increment()) {
+		for (Wink i = min; i.compareTo(max) <= 0; i = i.increment()) {
 			final int load = gantt.getLoadForResource(res, i);
 			if (load > 0) {
 				final FontConfiguration fontConfiguration = getFontConfiguration(9, load > 100 ? HtmlColorUtils.RED
diff --git a/src/net/sourceforge/plantuml/project3/TaskDraw.java b/src/net/sourceforge/plantuml/project/draw/TaskDraw.java
similarity index 89%
rename from src/net/sourceforge/plantuml/project3/TaskDraw.java
rename to src/net/sourceforge/plantuml/project/draw/TaskDraw.java
index e84c44594..bd2b5381d 100644
--- a/src/net/sourceforge/plantuml/project3/TaskDraw.java
+++ b/src/net/sourceforge/plantuml/project/draw/TaskDraw.java
@@ -33,15 +33,16 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.draw;
 
 import net.sourceforge.plantuml.Direction;
 import net.sourceforge.plantuml.graphic.UDrawable;
+import net.sourceforge.plantuml.project.lang.ComplementColors;
 import net.sourceforge.plantuml.ugraphic.UGraphic;
 
 public interface TaskDraw extends UDrawable {
 
-	public void setColors(ComplementColors colors);
+	public void setColorsAndCompletion(ComplementColors colors, int completion);
 
 	public double getY();
 
diff --git a/src/net/sourceforge/plantuml/project3/TaskDrawRegular.java b/src/net/sourceforge/plantuml/project/draw/TaskDrawRegular.java
similarity index 70%
rename from src/net/sourceforge/plantuml/project3/TaskDrawRegular.java
rename to src/net/sourceforge/plantuml/project/draw/TaskDrawRegular.java
index 0ea267f01..1b8f26a0b 100644
--- a/src/net/sourceforge/plantuml/project3/TaskDrawRegular.java
+++ b/src/net/sourceforge/plantuml/project/draw/TaskDrawRegular.java
@@ -33,7 +33,7 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.draw;
 
 import net.sourceforge.plantuml.Direction;
 import net.sourceforge.plantuml.SpriteContainerEmpty;
@@ -44,6 +44,10 @@ import net.sourceforge.plantuml.graphic.HtmlColor;
 import net.sourceforge.plantuml.graphic.HtmlColorSetSimple;
 import net.sourceforge.plantuml.graphic.HtmlColorUtils;
 import net.sourceforge.plantuml.graphic.TextBlock;
+import net.sourceforge.plantuml.project.core.TaskImpl;
+import net.sourceforge.plantuml.project.core.Wink;
+import net.sourceforge.plantuml.project.lang.ComplementColors;
+import net.sourceforge.plantuml.project.timescale.TimeScale;
 import net.sourceforge.plantuml.ugraphic.UChangeBackColor;
 import net.sourceforge.plantuml.ugraphic.UChangeColor;
 import net.sourceforge.plantuml.ugraphic.UFont;
@@ -61,6 +65,7 @@ public class TaskDrawRegular implements TaskDraw {
 	private final TimeScale timeScale;
 	private final double y;
 	private ComplementColors colors;
+	private int completion = 100;
 
 	private final double margin = 2;
 
@@ -73,10 +78,15 @@ public class TaskDrawRegular implements TaskDraw {
 	public void drawTitle(UGraphic ug) {
 		final TextBlock title = Display.getWithNewlines(task.getPrettyDisplay()).create(getFontConfiguration(),
 				HorizontalAlignment.LEFT, new SpriteContainerEmpty());
-		final double shapeHeight = getShapeHeight(100);
 		final double titleHeight = title.calculateDimension(ug.getStringBounder()).getHeight();
-		final double h = (margin + shapeHeight - titleHeight) / 2;
-		title.drawU(ug.apply(new UTranslate(timeScale.getEndingPosition(task.getStart()), h)));
+		final double h = (margin + getShapeHeight() - titleHeight) / 2;
+		final double endingPosition;
+		if (isDiamond()) {
+			endingPosition = timeScale.getStartingPosition(task.getStart()) + getHeight();
+		} else {
+			endingPosition = timeScale.getEndingPosition(task.getStart());
+		}
+		title.drawU(ug.apply(new UTranslate(endingPosition, h)));
 	}
 
 	private FontConfiguration getFontConfiguration() {
@@ -88,12 +98,7 @@ public class TaskDrawRegular implements TaskDraw {
 		final double start = timeScale.getStartingPosition(task.getStart());
 		ug1 = applyColors(ug1);
 		UGraphic ug2 = ug1.apply(new UTranslate(start + margin, margin));
-		final UShape shapeFull = getShape(100);
-		if (shapeFull instanceof UPolygon) {
-			ug2.draw(shapeFull);
-		} else {
-			ug2.draw(shapeFull);
-		}
+		drawShape(ug2);
 	}
 
 	private UGraphic applyColors(UGraphic ug) {
@@ -106,25 +111,47 @@ public class TaskDrawRegular implements TaskDraw {
 		return ug.apply(new UChangeColor(HtmlColorUtils.BLUE)).apply(new UChangeBackColor(defaultColor));
 	}
 
-	private UShape getShape(int load) {
+	private void drawShape(UGraphic ug) {
 		if (isDiamond()) {
-			return getDiamond();
+			ug.draw(getDiamond());
+			return;
 		}
-		final Instant instantStart = task.getStart();
-		final Instant instantEnd = task.getEnd();
+		final Wink instantStart = task.getStart();
+		final Wink instantEnd = task.getEnd();
 		final double start = timeScale.getStartingPosition(instantStart);
 		final double end = timeScale.getEndingPosition(instantEnd);
-		return new URectangle(end - start - 2 * margin, getShapeHeight(load), 8, 8);
+
+		final double fullLength = end - start - 2 * margin;
+		if (fullLength < 10) {
+			return;
+		}
+		final URectangle full = new URectangle(fullLength, getShapeHeight(), 8, 8);
+		if (completion == 100) {
+			ug.draw(full);
+			return;
+		}
+		final double partialLength = fullLength * completion / 100.;
+		ug.apply(new UChangeColor(HtmlColorUtils.WHITE)).apply(new UChangeBackColor(HtmlColorUtils.WHITE)).draw(full);
+		if (partialLength > 2) {
+			final URectangle partial = new URectangle(partialLength, getShapeHeight(), 8, 8);
+			ug.apply(new UChangeColor(null)).draw(partial);
+		}
+		if (partialLength > 10 && partialLength < fullLength - 10) {
+			final URectangle patch = new URectangle(8, getShapeHeight());
+			ug.apply(new UChangeColor(null)).apply(new UTranslate(partialLength - 8, 0)).draw(patch);
+		}
+		ug.apply(new UChangeBackColor(null)).draw(full);
+
 	}
 
-	private double getShapeHeight(int load) {
-		return (getHeight() - 2 * margin) * load / 100.0;
+	private double getShapeHeight() {
+		return getHeight() - 2 * margin;
 	}
 
 	private boolean isDiamond() {
 		if (task.isDiamond()) {
-			final Instant instantStart = task.getStart();
-			final Instant instantEnd = task.getEnd();
+			final Wink instantStart = task.getStart();
+			final Wink instantEnd = task.getEnd();
 			return instantStart.compareTo(instantEnd) == 0;
 		}
 		return false;
@@ -158,7 +185,8 @@ public class TaskDrawRegular implements TaskDraw {
 		return y + getHeight() / 2;
 	}
 
-	public void setColors(ComplementColors colors) {
+	public void setColorsAndCompletion(ComplementColors colors, int completion) {
 		this.colors = colors;
+		this.completion = completion;
 	}
 }
diff --git a/src/net/sourceforge/plantuml/project3/TaskDrawSeparator.java b/src/net/sourceforge/plantuml/project/draw/TaskDrawSeparator.java
similarity index 89%
rename from src/net/sourceforge/plantuml/project3/TaskDrawSeparator.java
rename to src/net/sourceforge/plantuml/project/draw/TaskDrawSeparator.java
index d29f73011..e54aeb308 100644
--- a/src/net/sourceforge/plantuml/project3/TaskDrawSeparator.java
+++ b/src/net/sourceforge/plantuml/project/draw/TaskDrawSeparator.java
@@ -33,7 +33,7 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.draw;
 
 import net.sourceforge.plantuml.Direction;
 import net.sourceforge.plantuml.SpriteContainerEmpty;
@@ -43,6 +43,10 @@ import net.sourceforge.plantuml.graphic.HorizontalAlignment;
 import net.sourceforge.plantuml.graphic.HtmlColorUtils;
 import net.sourceforge.plantuml.graphic.TextBlock;
 import net.sourceforge.plantuml.graphic.TextBlockUtils;
+import net.sourceforge.plantuml.project.core.TaskSeparator;
+import net.sourceforge.plantuml.project.core.Wink;
+import net.sourceforge.plantuml.project.lang.ComplementColors;
+import net.sourceforge.plantuml.project.timescale.TimeScale;
 import net.sourceforge.plantuml.ugraphic.UChangeColor;
 import net.sourceforge.plantuml.ugraphic.UFont;
 import net.sourceforge.plantuml.ugraphic.UGraphic;
@@ -53,11 +57,11 @@ public class TaskDrawSeparator implements TaskDraw {
 
 	private final TimeScale timeScale;
 	private final double y;
-	private final Instant min;
-	private final Instant max;
+	private final Wink min;
+	private final Wink max;
 	private final String name;
 
-	public TaskDrawSeparator(TaskSeparator task, TimeScale timeScale, double y, Instant min, Instant max) {
+	public TaskDrawSeparator(TaskSeparator task, TimeScale timeScale, double y, Wink min, Wink max) {
 		this.name = task.getName();
 		this.y = y;
 		this.timeScale = timeScale;
@@ -122,7 +126,7 @@ public class TaskDrawSeparator implements TaskDraw {
 		return y + getHeight() / 2;
 	}
 
-	public void setColors(ComplementColors colors) {
+	public void setColorsAndCompletion(ComplementColors colors, int completion) {
 	}
 
 }
diff --git a/src/net/sourceforge/plantuml/project/draw/TimeHeader.java b/src/net/sourceforge/plantuml/project/draw/TimeHeader.java
new file mode 100644
index 000000000..982f9c82d
--- /dev/null
+++ b/src/net/sourceforge/plantuml/project/draw/TimeHeader.java
@@ -0,0 +1,113 @@
+/* ========================================================================
+ * PlantUML : a free UML diagram generator
+ * ========================================================================
+ *
+ * (C) Copyright 2009-2020, Arnaud Roques
+ *
+ * Project Info:  http://plantuml.com
+ * 
+ * If you like this project or if you find it useful, you can support us at:
+ * 
+ * http://plantuml.com/patreon (only 1$ per month!)
+ * http://plantuml.com/paypal
+ * 
+ * This file is part of PlantUML.
+ *
+ * PlantUML is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * PlantUML distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
+ * USA.
+ *
+ *
+ * Original Author:  Arnaud Roques
+ * 
+ *
+ */
+package net.sourceforge.plantuml.project.draw;
+
+import net.sourceforge.plantuml.SpriteContainerEmpty;
+import net.sourceforge.plantuml.cucadiagram.Display;
+import net.sourceforge.plantuml.graphic.FontConfiguration;
+import net.sourceforge.plantuml.graphic.HorizontalAlignment;
+import net.sourceforge.plantuml.graphic.HtmlColorUtils;
+import net.sourceforge.plantuml.graphic.TextBlock;
+import net.sourceforge.plantuml.project.core.Wink;
+import net.sourceforge.plantuml.project.timescale.TimeScale;
+import net.sourceforge.plantuml.ugraphic.UChangeColor;
+import net.sourceforge.plantuml.ugraphic.UFont;
+import net.sourceforge.plantuml.ugraphic.UGraphic;
+import net.sourceforge.plantuml.ugraphic.ULine;
+import net.sourceforge.plantuml.ugraphic.UTranslate;
+import sun.security.x509.AVA;
+
+public abstract class TimeHeader {
+
+	private final TimeScale timeScale;
+	protected final Wink min;
+	protected final Wink max;
+
+	public TimeHeader(Wink min, Wink max, TimeScale timeScale) {
+		this.timeScale = timeScale;
+		this.min = min;
+		this.max = max;
+	}
+
+	public abstract void drawTimeHeader(final UGraphic ug, double totalHeight);
+
+	public abstract double getFullHeaderHeight();
+
+	protected final void drawHline(UGraphic ug, double y) {
+		final double xmin = getTimeScale().getStartingPosition(min);
+		final double xmax = getTimeScale().getEndingPosition(max);
+		final ULine hline = new ULine(xmax - xmin, 0);
+		ug.apply(new UChangeColor(HtmlColorUtils.LIGHT_GRAY)).apply(new UTranslate(0, y)).draw(hline);
+	}
+
+	final protected FontConfiguration getFontConfiguration(int size, boolean bold) {
+		UFont font = UFont.serif(size);
+		if (bold) {
+			font = font.bold();
+		}
+		return new FontConfiguration(font, HtmlColorUtils.BLACK, HtmlColorUtils.BLACK, false);
+	}
+
+	public final TimeScale getTimeScale() {
+		return timeScale;
+	}
+
+	protected final TextBlock getTextBlock(final String text, int size, boolean bold) {
+		return Display.getWithNewlines(text).create(getFontConfiguration(size, bold), HorizontalAlignment.LEFT,
+				new SpriteContainerEmpty());
+	}
+
+	protected final void printCentered(UGraphic ug, TextBlock text, double start, double end) {
+		final double width = text.calculateDimension(ug.getStringBounder()).getWidth();
+		final double available = end - start;
+		final double diff = Math.max(0, available - width);
+		text.drawU(ug.apply(new UTranslate(start + diff / 2, 0)));
+	}
+
+	protected final void printCentered(UGraphic ug, double start, double end, TextBlock... texts) {
+		final double available = end - start;
+		for (int i = texts.length - 1; i >= 0; i--) {
+			final TextBlock text = texts[i];
+			final double width = text.calculateDimension(ug.getStringBounder()).getWidth();
+			if (i == 0 || width <= available) {
+				final double diff = Math.max(0, available - width);
+				text.drawU(ug.apply(new UTranslate(start + diff / 2, 0)));
+				return;
+			}
+		}
+	}
+
+}
diff --git a/src/net/sourceforge/plantuml/project/draw/TimeHeaderDaily.java b/src/net/sourceforge/plantuml/project/draw/TimeHeaderDaily.java
new file mode 100644
index 000000000..a0a819fef
--- /dev/null
+++ b/src/net/sourceforge/plantuml/project/draw/TimeHeaderDaily.java
@@ -0,0 +1,188 @@
+/* ========================================================================
+ * PlantUML : a free UML diagram generator
+ * ========================================================================
+ *
+ * (C) Copyright 2009-2020, Arnaud Roques
+ *
+ * Project Info:  http://plantuml.com
+ * 
+ * If you like this project or if you find it useful, you can support us at:
+ * 
+ * http://plantuml.com/patreon (only 1$ per month!)
+ * http://plantuml.com/paypal
+ * 
+ * This file is part of PlantUML.
+ *
+ * PlantUML is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * PlantUML distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
+ * USA.
+ *
+ *
+ * Original Author:  Arnaud Roques
+ * 
+ *
+ */
+package net.sourceforge.plantuml.project.draw;
+
+import java.util.Map;
+
+import net.sourceforge.plantuml.graphic.HtmlColor;
+import net.sourceforge.plantuml.graphic.HtmlColorSetSimple;
+import net.sourceforge.plantuml.graphic.HtmlColorUtils;
+import net.sourceforge.plantuml.graphic.TextBlock;
+import net.sourceforge.plantuml.project.DayAsDate;
+import net.sourceforge.plantuml.project.DayOfWeek;
+import net.sourceforge.plantuml.project.GCalendar;
+import net.sourceforge.plantuml.project.LoadPlanable;
+import net.sourceforge.plantuml.project.core.Month;
+import net.sourceforge.plantuml.project.core.Wink;
+import net.sourceforge.plantuml.project.timescale.TimeScaleDaily;
+import net.sourceforge.plantuml.ugraphic.UChangeBackColor;
+import net.sourceforge.plantuml.ugraphic.UChangeColor;
+import net.sourceforge.plantuml.ugraphic.UGraphic;
+import net.sourceforge.plantuml.ugraphic.ULine;
+import net.sourceforge.plantuml.ugraphic.URectangle;
+import net.sourceforge.plantuml.ugraphic.UTranslate;
+
+public class TimeHeaderDaily extends TimeHeader {
+
+	private static final int Y_POS_WEEKDAY = 16;
+	private static final int Y_POS_NUMDAY = 28;
+
+	private double getTimeHeaderHeight() {
+		return Y_POS_NUMDAY + 13;
+	}
+
+	private final HtmlColor veryLightGray = new HtmlColorSetSimple().getColorIfValid("#E0E8E8");
+
+	private final GCalendar calendar;
+	private final LoadPlanable defaultPlan;
+	private final Map<DayAsDate, HtmlColor> colorDays;
+	private final Map<DayAsDate, String> nameDays;
+
+	public TimeHeaderDaily(GCalendar calendar, Wink min, Wink max, LoadPlanable defaultPlan,
+			Map<DayAsDate, HtmlColor> colorDays, Map<DayAsDate, String> nameDays, DayAsDate printStart,
+			DayAsDate printEnd) {
+		super(min, max, new TimeScaleDaily(calendar, printStart));
+		this.calendar = calendar;
+		this.defaultPlan = defaultPlan;
+		this.colorDays = colorDays;
+		this.nameDays = nameDays;
+	}
+
+	@Override
+	public void drawTimeHeader(final UGraphic ug, double totalHeight) {
+		drawCalendar(ug, totalHeight);
+		drawHline(ug, 0);
+		drawHline(ug, getFullHeaderHeight());
+
+	}
+
+	private void drawCalendar(final UGraphic ug, double totalHeight) {
+		Month lastMonth = null;
+		double lastChangeMonth = -1;
+		Wink wink = min;
+		while (wink.compareTo(max) <= 0) {
+			final DayAsDate day = calendar.toDayAsDate(wink);
+			final DayOfWeek dayOfWeek = day.getDayOfWeek();
+			final boolean isWorkingDay = defaultPlan.getLoadAt(wink) > 0;
+			final String d1 = "" + day.getDayOfMonth();
+			final TextBlock num = getTextBlock(d1, 10, false);
+			final double x1 = getTimeScale().getStartingPosition(wink);
+			final double x2 = getTimeScale().getEndingPosition(wink);
+			double startingY = getFullHeaderHeight();
+			if (wink.compareTo(max.increment()) < 0) {
+				final TextBlock weekDay = getTextBlock(dayOfWeek.shortName(), 10, false);
+
+				final URectangle rect = new URectangle(x2 - x1 - 1, totalHeight - getFullHeaderHeight());
+				if (isWorkingDay) {
+					final HtmlColor back = colorDays.get(day);
+					if (back != null) {
+						ug.apply(new UChangeColor(null)).apply(new UChangeBackColor(back))
+								.apply(new UTranslate(x1 + 1, getFullHeaderHeight())).draw(rect);
+					}
+					printCentered(ug.apply(new UTranslate(0, Y_POS_WEEKDAY)), weekDay, x1, x2);
+					printCentered(ug.apply(new UTranslate(0, Y_POS_NUMDAY)), num, x1, x2);
+				} else {
+					ug.apply(new UChangeColor(null)).apply(new UChangeBackColor(veryLightGray))
+							.apply(new UTranslate(x1 + 1, getFullHeaderHeight())).draw(rect);
+				}
+				if (lastMonth != day.getMonth()) {
+					startingY = 0;
+					if (lastMonth != null) {
+						printMonth(ug, lastMonth, day.getYear(), lastChangeMonth, x1);
+					}
+					lastChangeMonth = x1;
+					lastMonth = day.getMonth();
+				}
+			}
+			drawVbar(ug, x1, startingY, totalHeight);
+			wink = wink.increment();
+		}
+		final DayAsDate day = calendar.toDayAsDate(max);
+		final double x1 = getTimeScale().getStartingPosition(wink);
+		drawVbar(ug, x1, Y_POS_WEEKDAY, totalHeight);
+		if (x1 > lastChangeMonth) {
+			printMonth(ug, lastMonth, day.getYear(), lastChangeMonth, x1);
+		}
+
+		printNamedDays(ug);
+
+	}
+
+	private void printMonth(UGraphic ug, Month lastMonth, int year, double start, double end) {
+		final TextBlock tiny = getTextBlock(lastMonth.shortName(), 12, true);
+		final TextBlock small = getTextBlock(lastMonth.niceName(), 12, true);
+		final TextBlock big = getTextBlock(lastMonth.niceName() + " " + year, 12, true);
+		printCentered(ug, start, end, tiny, small, big);
+	}
+
+	private void drawVbar(UGraphic ug, double x, double y1, double y2) {
+		final ULine vbar = new ULine(0, y2 - y1);
+		ug.apply(new UChangeColor(HtmlColorUtils.LIGHT_GRAY)).apply(new UTranslate(x, y1)).draw(vbar);
+	}
+
+	private void printNamedDays(final UGraphic ug) {
+		if (nameDays.size() > 0) {
+			String last = null;
+			for (Wink wink = min; wink.compareTo(max.increment()) <= 0; wink = wink.increment()) {
+				final DayAsDate tmpday = calendar.toDayAsDate(wink);
+				final String name = nameDays.get(tmpday);
+				if (name != null && name.equals(last) == false) {
+					final double x1 = getTimeScale().getStartingPosition(wink);
+					final double x2 = getTimeScale().getEndingPosition(wink);
+					final TextBlock label = getTextBlock(name, 12, false);
+					final double h = label.calculateDimension(ug.getStringBounder()).getHeight();
+					double y1 = getTimeHeaderHeight();
+					double y2 = getFullHeaderHeight();
+					label.drawU(ug.apply(new UTranslate(x1, Y_POS_NUMDAY + 11)));
+				}
+				last = name;
+			}
+		}
+	}
+
+	@Override
+	public double getFullHeaderHeight() {
+		return getTimeHeaderHeight() + getHeaderNameDayHeight();
+	}
+
+	private double getHeaderNameDayHeight() {
+		if (nameDays.size() > 0) {
+			return 16;
+		}
+		return 0;
+	}
+
+}
diff --git a/src/net/sourceforge/plantuml/project/draw/TimeHeaderSimple.java b/src/net/sourceforge/plantuml/project/draw/TimeHeaderSimple.java
new file mode 100644
index 000000000..2d07d261d
--- /dev/null
+++ b/src/net/sourceforge/plantuml/project/draw/TimeHeaderSimple.java
@@ -0,0 +1,97 @@
+/* ========================================================================
+ * PlantUML : a free UML diagram generator
+ * ========================================================================
+ *
+ * (C) Copyright 2009-2020, Arnaud Roques
+ *
+ * Project Info:  http://plantuml.com
+ * 
+ * If you like this project or if you find it useful, you can support us at:
+ * 
+ * http://plantuml.com/patreon (only 1$ per month!)
+ * http://plantuml.com/paypal
+ * 
+ * This file is part of PlantUML.
+ *
+ * PlantUML is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * PlantUML distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
+ * USA.
+ *
+ *
+ * Original Author:  Arnaud Roques
+ * 
+ *
+ */
+package net.sourceforge.plantuml.project.draw;
+
+import net.sourceforge.plantuml.SpriteContainerEmpty;
+import net.sourceforge.plantuml.cucadiagram.Display;
+import net.sourceforge.plantuml.graphic.HorizontalAlignment;
+import net.sourceforge.plantuml.graphic.HtmlColorUtils;
+import net.sourceforge.plantuml.graphic.TextBlock;
+import net.sourceforge.plantuml.project.core.Wink;
+import net.sourceforge.plantuml.project.timescale.TimeScale;
+import net.sourceforge.plantuml.project.timescale.TimeScaleWink;
+import net.sourceforge.plantuml.ugraphic.UChangeColor;
+import net.sourceforge.plantuml.ugraphic.UGraphic;
+import net.sourceforge.plantuml.ugraphic.ULine;
+import net.sourceforge.plantuml.ugraphic.UTranslate;
+
+public class TimeHeaderSimple extends TimeHeader {
+
+	public TimeHeaderSimple(Wink min, Wink max) {
+		super(min, max, new TimeScaleWink());
+	}
+
+	@Override
+	public void drawTimeHeader(final UGraphic ug, double totalHeight) {
+		final double xmin = getTimeScale().getStartingPosition(min);
+		final double xmax = getTimeScale().getEndingPosition(max);
+		drawSimpleDayCounter(ug, getTimeScale(), totalHeight);
+		ug.apply(new UChangeColor(HtmlColorUtils.LIGHT_GRAY)).draw(new ULine(xmax - xmin, 0));
+		ug.apply(new UChangeColor(HtmlColorUtils.LIGHT_GRAY)).apply(new UTranslate(0, getFullHeaderHeight() - 3))
+				.draw(new ULine(xmax - xmin, 0));
+
+	}
+
+	private void drawSimpleDayCounter(final UGraphic ug, TimeScale timeScale, double totalHeight) {
+		final ULine vbar = new ULine(0, totalHeight);
+		for (Wink i = min; i.compareTo(max.increment()) <= 0; i = i.increment()) {
+			final TextBlock num = Display.getWithNewlines(i.toShortString()).create(getFontConfiguration(10, false),
+					HorizontalAlignment.LEFT, new SpriteContainerEmpty());
+			final double x1 = timeScale.getStartingPosition(i);
+			final double x2 = timeScale.getEndingPosition(i);
+			final double width = num.calculateDimension(ug.getStringBounder()).getWidth();
+			final double delta = (x2 - x1) - width;
+			if (i.compareTo(max.increment()) < 0) {
+				num.drawU(ug.apply(new UTranslate(x1 + delta / 2, 0)));
+			}
+			ug.apply(new UChangeColor(HtmlColorUtils.LIGHT_GRAY)).apply(new UTranslate(x1, 0)).draw(vbar);
+		}
+	}
+
+	@Override
+	public double getFullHeaderHeight() {
+		return getTimeHeaderHeight() + getHeaderNameDayHeight();
+	}
+
+	private double getTimeHeaderHeight() {
+		return 16;
+	}
+
+	private double getHeaderNameDayHeight() {
+		return 0;
+	}
+
+}
diff --git a/src/net/sourceforge/plantuml/project/draw/TimeHeaderWeekly.java b/src/net/sourceforge/plantuml/project/draw/TimeHeaderWeekly.java
new file mode 100644
index 000000000..d6433515c
--- /dev/null
+++ b/src/net/sourceforge/plantuml/project/draw/TimeHeaderWeekly.java
@@ -0,0 +1,188 @@
+/* ========================================================================
+ * PlantUML : a free UML diagram generator
+ * ========================================================================
+ *
+ * (C) Copyright 2009-2020, Arnaud Roques
+ *
+ * Project Info:  http://plantuml.com
+ * 
+ * If you like this project or if you find it useful, you can support us at:
+ * 
+ * http://plantuml.com/patreon (only 1$ per month!)
+ * http://plantuml.com/paypal
+ * 
+ * This file is part of PlantUML.
+ *
+ * PlantUML is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * PlantUML distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
+ * USA.
+ *
+ *
+ * Original Author:  Arnaud Roques
+ * 
+ *
+ */
+package net.sourceforge.plantuml.project.draw;
+
+import java.util.Map;
+
+import net.sourceforge.plantuml.SpriteContainerEmpty;
+import net.sourceforge.plantuml.cucadiagram.Display;
+import net.sourceforge.plantuml.graphic.HorizontalAlignment;
+import net.sourceforge.plantuml.graphic.HtmlColor;
+import net.sourceforge.plantuml.graphic.HtmlColorSetSimple;
+import net.sourceforge.plantuml.graphic.HtmlColorUtils;
+import net.sourceforge.plantuml.graphic.TextBlock;
+import net.sourceforge.plantuml.project.DayAsDate;
+import net.sourceforge.plantuml.project.DayOfWeek;
+import net.sourceforge.plantuml.project.GCalendar;
+import net.sourceforge.plantuml.project.LoadPlanable;
+import net.sourceforge.plantuml.project.core.Month;
+import net.sourceforge.plantuml.project.core.Wink;
+import net.sourceforge.plantuml.project.timescale.TimeScaleWeekly;
+import net.sourceforge.plantuml.ugraphic.UChangeColor;
+import net.sourceforge.plantuml.ugraphic.UGraphic;
+import net.sourceforge.plantuml.ugraphic.ULine;
+import net.sourceforge.plantuml.ugraphic.UTranslate;
+
+public class TimeHeaderWeekly extends TimeHeader {
+
+	private static final int Y_POS_NUMDAY = 16;
+
+	private double getTimeHeaderHeight() {
+		return Y_POS_NUMDAY + 13;
+	}
+
+	private final HtmlColor veryLightGray = new HtmlColorSetSimple().getColorIfValid("#E0E8E8");
+
+	private final GCalendar calendar;
+	private final LoadPlanable defaultPlan;
+	private final Map<DayAsDate, HtmlColor> colorDays;
+	private final Map<DayAsDate, String> nameDays;
+
+	public TimeHeaderWeekly(GCalendar calendar, Wink min, Wink max, LoadPlanable defaultPlan,
+			Map<DayAsDate, HtmlColor> colorDays, Map<DayAsDate, String> nameDays) {
+		super(min, max, new TimeScaleWeekly(calendar));
+		this.calendar = calendar;
+		this.defaultPlan = defaultPlan;
+		this.colorDays = colorDays;
+		this.nameDays = nameDays;
+	}
+
+	@Override
+	public void drawTimeHeader(final UGraphic ug, double totalHeight) {
+		drawCalendar(ug, totalHeight);
+		drawHline(ug, 0);
+		drawHline(ug, Y_POS_NUMDAY);
+		drawHline(ug, getFullHeaderHeight());
+
+	}
+
+	private void drawCalendar(final UGraphic ug, double totalHeight) {
+		Month lastMonth = null;
+		double lastChangeMonth = -1;
+		Wink wink = min;
+		while (wink.compareTo(max) <= 0) {
+			final DayAsDate day = calendar.toDayAsDate(wink);
+			final DayOfWeek dayOfWeek = day.getDayOfWeek();
+			final String d1 = "" + day.getDayOfMonth();
+			final TextBlock num = getTextBlock(d1, 10, false);
+			final double x1 = getTimeScale().getStartingPosition(wink);
+			final double x2 = getTimeScale().getEndingPosition(wink);
+			double startingY = getFullHeaderHeight();
+			if (wink.compareTo(max.increment()) < 0) {
+				if (dayOfWeek == DayOfWeek.MONDAY) {
+					printLeft(ug.apply(new UTranslate(0, Y_POS_NUMDAY)), num, x1 + 5);
+				}
+				if (lastMonth != day.getMonth()) {
+					startingY = Y_POS_NUMDAY;
+					drawVbar(ug, x1, 0, Y_POS_NUMDAY);
+					if (lastMonth != null) {
+						printMonth(ug, lastMonth, day.getYear(), lastChangeMonth, x1);
+					}
+					lastChangeMonth = x1;
+					lastMonth = day.getMonth();
+				}
+			}
+			if (dayOfWeek == DayOfWeek.MONDAY) {
+				drawVbar(ug, x1, Y_POS_NUMDAY, totalHeight);
+			}
+			wink = wink.increment();
+		}
+		final DayAsDate day = calendar.toDayAsDate(wink);
+		final double x1 = getTimeScale().getStartingPosition(wink);
+		if (x1 > lastChangeMonth) {
+			printMonth(ug, lastMonth, day.getYear(), lastChangeMonth, x1);
+		}
+
+		printNamedDays(ug);
+
+	}
+
+	private void printMonth(UGraphic ug, Month lastMonth, int year, double start, double end) {
+		final TextBlock small = getTextBlock(lastMonth.shortName(), 12, true);
+		final TextBlock big = getTextBlock(lastMonth.shortName() + " " + year, 12, true);
+		printCentered(ug, start, end, small, big);
+	}
+
+	private void drawVbar(UGraphic ug, double x, double y1, double y2) {
+		final ULine vbar = new ULine(0, y2 - y1);
+		ug.apply(new UChangeColor(HtmlColorUtils.LIGHT_GRAY)).apply(new UTranslate(x, y1)).draw(vbar);
+	}
+
+	private void printNamedDays(final UGraphic ug) {
+//		if (nameDays.size() > 0) {
+//			String last = null;
+//			for (Wink wink = min; wink.compareTo(max.increment()) <= 0; wink = wink.increment()) {
+//				final DayAsDate tmpday = calendar.toDayAsDate(wink);
+//				final String name = nameDays.get(tmpday);
+//				if (name != null && name.equals(last) == false) {
+//					final double x1 = getTimeScale().getStartingPosition(wink);
+//					final double x2 = getTimeScale().getEndingPosition(wink);
+//					final TextBlock label = getTextBlock(name, 12, false);
+//					final double h = label.calculateDimension(ug.getStringBounder()).getHeight();
+//					double y1 = getTimeHeaderHeight();
+//					double y2 = getFullHeaderHeight();
+//					label.drawU(ug.apply(new UTranslate(x1, Y_POS_NUMDAY + 11)));
+//				}
+//				last = name;
+//			}
+//		}
+	}
+
+	private void printLeft(UGraphic ug, TextBlock text, double start) {
+		text.drawU(ug.apply(new UTranslate(start, 0)));
+	}
+
+	@Override
+	public double getFullHeaderHeight() {
+		return getTimeHeaderHeight() + getHeaderNameDayHeight();
+	}
+
+	private double getHeaderNameDayHeight() {
+//		if (nameDays.size() > 0) {
+//			return 16;
+//		}
+		return 0;
+	}
+
+//	private void drawCenter(final UGraphic ug, final TextBlock text, final double x1, final double x2) {
+//		final double width = text.calculateDimension(ug.getStringBounder()).getWidth();
+//		final double delta = (x2 - x1) - width;
+//		if (delta < 0) {
+//			return;
+//		}
+//		text.drawU(ug.apply(new UTranslate(x1 + delta / 2, 0)));
+//	}
+}
diff --git a/src/net/sourceforge/plantuml/project3/Complement.java b/src/net/sourceforge/plantuml/project/lang/Complement.java
similarity index 96%
rename from src/net/sourceforge/plantuml/project3/Complement.java
rename to src/net/sourceforge/plantuml/project/lang/Complement.java
index d9731d5f7..b20145e45 100644
--- a/src/net/sourceforge/plantuml/project3/Complement.java
+++ b/src/net/sourceforge/plantuml/project/lang/Complement.java
@@ -33,7 +33,7 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.lang;
 
 
 public interface Complement {
diff --git a/src/net/sourceforge/plantuml/project3/ComplementBeforeOrAfterOrAtTaskStartOrEnd.java b/src/net/sourceforge/plantuml/project/lang/ComplementBeforeOrAfterOrAtTaskStartOrEnd.java
similarity index 88%
rename from src/net/sourceforge/plantuml/project3/ComplementBeforeOrAfterOrAtTaskStartOrEnd.java
rename to src/net/sourceforge/plantuml/project/lang/ComplementBeforeOrAfterOrAtTaskStartOrEnd.java
index 304eea13b..27f31f020 100644
--- a/src/net/sourceforge/plantuml/project3/ComplementBeforeOrAfterOrAtTaskStartOrEnd.java
+++ b/src/net/sourceforge/plantuml/project/lang/ComplementBeforeOrAfterOrAtTaskStartOrEnd.java
@@ -33,11 +33,16 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.lang;
 
 import net.sourceforge.plantuml.command.regex.IRegex;
 import net.sourceforge.plantuml.command.regex.RegexLeaf;
 import net.sourceforge.plantuml.command.regex.RegexResult;
+import net.sourceforge.plantuml.project.Failable;
+import net.sourceforge.plantuml.project.GanttDiagram;
+import net.sourceforge.plantuml.project.core.Moment;
+import net.sourceforge.plantuml.project.core.TaskAttribute;
+import net.sourceforge.plantuml.project.core.TaskInstant;
 
 public class ComplementBeforeOrAfterOrAtTaskStartOrEnd implements ComplementPattern {
 
diff --git a/src/net/sourceforge/plantuml/project3/ComplementClose.java b/src/net/sourceforge/plantuml/project/lang/ComplementClose.java
similarity index 92%
rename from src/net/sourceforge/plantuml/project3/ComplementClose.java
rename to src/net/sourceforge/plantuml/project/lang/ComplementClose.java
index b0059976a..e66247aa5 100644
--- a/src/net/sourceforge/plantuml/project3/ComplementClose.java
+++ b/src/net/sourceforge/plantuml/project/lang/ComplementClose.java
@@ -33,11 +33,13 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.lang;
 
 import net.sourceforge.plantuml.command.regex.IRegex;
 import net.sourceforge.plantuml.command.regex.RegexLeaf;
 import net.sourceforge.plantuml.command.regex.RegexResult;
+import net.sourceforge.plantuml.project.Failable;
+import net.sourceforge.plantuml.project.GanttDiagram;
 
 public class ComplementClose implements ComplementPattern {
 
diff --git a/src/net/sourceforge/plantuml/project3/ComplementColors.java b/src/net/sourceforge/plantuml/project/lang/ComplementColors.java
similarity index 97%
rename from src/net/sourceforge/plantuml/project3/ComplementColors.java
rename to src/net/sourceforge/plantuml/project/lang/ComplementColors.java
index a583fcd93..4bb445c1f 100644
--- a/src/net/sourceforge/plantuml/project3/ComplementColors.java
+++ b/src/net/sourceforge/plantuml/project/lang/ComplementColors.java
@@ -33,7 +33,7 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.lang;
 
 import net.sourceforge.plantuml.graphic.HtmlColor;
 import net.sourceforge.plantuml.ugraphic.UChangeBackColor;
diff --git a/src/net/sourceforge/plantuml/project/lang/ComplementCompleted.java b/src/net/sourceforge/plantuml/project/lang/ComplementCompleted.java
new file mode 100644
index 000000000..e6ff4b7f8
--- /dev/null
+++ b/src/net/sourceforge/plantuml/project/lang/ComplementCompleted.java
@@ -0,0 +1,55 @@
+/* ========================================================================
+ * PlantUML : a free UML diagram generator
+ * ========================================================================
+ *
+ * (C) Copyright 2009-2020, Arnaud Roques
+ *
+ * Project Info:  http://plantuml.com
+ * 
+ * If you like this project or if you find it useful, you can support us at:
+ * 
+ * http://plantuml.com/patreon (only 1$ per month!)
+ * http://plantuml.com/paypal
+ * 
+ * This file is part of PlantUML.
+ *
+ * PlantUML is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * PlantUML distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
+ * USA.
+ *
+ *
+ * Original Author:  Arnaud Roques
+ * 
+ *
+ */
+package net.sourceforge.plantuml.project.lang;
+
+import net.sourceforge.plantuml.command.regex.IRegex;
+import net.sourceforge.plantuml.command.regex.RegexLeaf;
+import net.sourceforge.plantuml.command.regex.RegexResult;
+import net.sourceforge.plantuml.project.Completion;
+import net.sourceforge.plantuml.project.Failable;
+import net.sourceforge.plantuml.project.GanttDiagram;
+
+public class ComplementCompleted implements ComplementPattern {
+
+	public IRegex toRegex(String suffix) {
+		return new RegexLeaf("COMPLEMENT" + suffix, "(\\d+).*completed?");
+	}
+
+	public Failable<Complement> getComplement(GanttDiagram system, RegexResult arg, String suffix) {
+		final String value = arg.get("COMPLEMENT" + suffix, 0);
+		return Failable.<Complement>ok(new Completion(Integer.parseInt(value)));
+	}
+}
diff --git a/src/net/sourceforge/plantuml/project3/ComplementDate.java b/src/net/sourceforge/plantuml/project/lang/ComplementDate.java
similarity index 94%
rename from src/net/sourceforge/plantuml/project3/ComplementDate.java
rename to src/net/sourceforge/plantuml/project/lang/ComplementDate.java
index 5683fabe8..e4cdb55ee 100644
--- a/src/net/sourceforge/plantuml/project3/ComplementDate.java
+++ b/src/net/sourceforge/plantuml/project/lang/ComplementDate.java
@@ -33,13 +33,17 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.lang;
 
 import net.sourceforge.plantuml.command.regex.IRegex;
 import net.sourceforge.plantuml.command.regex.RegexConcat;
 import net.sourceforge.plantuml.command.regex.RegexLeaf;
 import net.sourceforge.plantuml.command.regex.RegexOr;
 import net.sourceforge.plantuml.command.regex.RegexResult;
+import net.sourceforge.plantuml.project.DayAsDate;
+import net.sourceforge.plantuml.project.Failable;
+import net.sourceforge.plantuml.project.GanttDiagram;
+import net.sourceforge.plantuml.project.core.Month;
 
 public class ComplementDate implements ComplementPattern {
 
diff --git a/src/net/sourceforge/plantuml/project3/ComplementDates.java b/src/net/sourceforge/plantuml/project/lang/ComplementDates.java
similarity index 93%
rename from src/net/sourceforge/plantuml/project3/ComplementDates.java
rename to src/net/sourceforge/plantuml/project/lang/ComplementDates.java
index b09f2fad0..7cbb13e74 100644
--- a/src/net/sourceforge/plantuml/project3/ComplementDates.java
+++ b/src/net/sourceforge/plantuml/project/lang/ComplementDates.java
@@ -33,12 +33,16 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.lang;
 
 import net.sourceforge.plantuml.command.regex.IRegex;
 import net.sourceforge.plantuml.command.regex.RegexConcat;
 import net.sourceforge.plantuml.command.regex.RegexLeaf;
 import net.sourceforge.plantuml.command.regex.RegexResult;
+import net.sourceforge.plantuml.project.DayAsDate;
+import net.sourceforge.plantuml.project.DaysAsDates;
+import net.sourceforge.plantuml.project.Failable;
+import net.sourceforge.plantuml.project.GanttDiagram;
 
 public class ComplementDates implements ComplementPattern {
 
diff --git a/src/net/sourceforge/plantuml/project3/ComplementDayOfWeek.java b/src/net/sourceforge/plantuml/project/lang/ComplementDayOfWeek.java
similarity index 90%
rename from src/net/sourceforge/plantuml/project3/ComplementDayOfWeek.java
rename to src/net/sourceforge/plantuml/project/lang/ComplementDayOfWeek.java
index ce2a97577..416352281 100644
--- a/src/net/sourceforge/plantuml/project3/ComplementDayOfWeek.java
+++ b/src/net/sourceforge/plantuml/project/lang/ComplementDayOfWeek.java
@@ -33,12 +33,15 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.lang;
 
 import net.sourceforge.plantuml.command.regex.IRegex;
 import net.sourceforge.plantuml.command.regex.RegexConcat;
 import net.sourceforge.plantuml.command.regex.RegexLeaf;
 import net.sourceforge.plantuml.command.regex.RegexResult;
+import net.sourceforge.plantuml.project.DayOfWeek;
+import net.sourceforge.plantuml.project.Failable;
+import net.sourceforge.plantuml.project.GanttDiagram;
 
 public class ComplementDayOfWeek implements ComplementPattern {
 
diff --git a/src/net/sourceforge/plantuml/project3/ComplementEmpty.java b/src/net/sourceforge/plantuml/project/lang/ComplementEmpty.java
similarity index 91%
rename from src/net/sourceforge/plantuml/project3/ComplementEmpty.java
rename to src/net/sourceforge/plantuml/project/lang/ComplementEmpty.java
index 874804789..ef9a03c4c 100644
--- a/src/net/sourceforge/plantuml/project3/ComplementEmpty.java
+++ b/src/net/sourceforge/plantuml/project/lang/ComplementEmpty.java
@@ -33,11 +33,13 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.lang;
 
 import net.sourceforge.plantuml.command.regex.IRegex;
 import net.sourceforge.plantuml.command.regex.RegexLeaf;
 import net.sourceforge.plantuml.command.regex.RegexResult;
+import net.sourceforge.plantuml.project.Failable;
+import net.sourceforge.plantuml.project.GanttDiagram;
 
 public class ComplementEmpty implements ComplementPattern {
 
diff --git a/src/net/sourceforge/plantuml/project3/ComplementInColors.java b/src/net/sourceforge/plantuml/project/lang/ComplementInColors.java
similarity index 93%
rename from src/net/sourceforge/plantuml/project3/ComplementInColors.java
rename to src/net/sourceforge/plantuml/project/lang/ComplementInColors.java
index b8826e5ae..0c9b5b68c 100644
--- a/src/net/sourceforge/plantuml/project3/ComplementInColors.java
+++ b/src/net/sourceforge/plantuml/project/lang/ComplementInColors.java
@@ -33,12 +33,14 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.lang;
 
 import net.sourceforge.plantuml.command.regex.IRegex;
 import net.sourceforge.plantuml.command.regex.RegexLeaf;
 import net.sourceforge.plantuml.command.regex.RegexResult;
 import net.sourceforge.plantuml.graphic.HtmlColor;
+import net.sourceforge.plantuml.project.Failable;
+import net.sourceforge.plantuml.project.GanttDiagram;
 
 public class ComplementInColors implements ComplementPattern {
 
diff --git a/src/net/sourceforge/plantuml/project3/ComplementInColors2.java b/src/net/sourceforge/plantuml/project/lang/ComplementInColors2.java
similarity index 93%
rename from src/net/sourceforge/plantuml/project3/ComplementInColors2.java
rename to src/net/sourceforge/plantuml/project/lang/ComplementInColors2.java
index 93f6d8a5f..fb1b504c2 100644
--- a/src/net/sourceforge/plantuml/project3/ComplementInColors2.java
+++ b/src/net/sourceforge/plantuml/project/lang/ComplementInColors2.java
@@ -33,12 +33,14 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.lang;
 
 import net.sourceforge.plantuml.command.regex.IRegex;
 import net.sourceforge.plantuml.command.regex.RegexLeaf;
 import net.sourceforge.plantuml.command.regex.RegexResult;
 import net.sourceforge.plantuml.graphic.HtmlColor;
+import net.sourceforge.plantuml.project.Failable;
+import net.sourceforge.plantuml.project.GanttDiagram;
 
 public class ComplementInColors2 implements ComplementPattern {
 
diff --git a/src/net/sourceforge/plantuml/project3/ComplementName.java b/src/net/sourceforge/plantuml/project/lang/ComplementName.java
similarity index 96%
rename from src/net/sourceforge/plantuml/project3/ComplementName.java
rename to src/net/sourceforge/plantuml/project/lang/ComplementName.java
index ed0e8ffee..75cf8997d 100644
--- a/src/net/sourceforge/plantuml/project3/ComplementName.java
+++ b/src/net/sourceforge/plantuml/project/lang/ComplementName.java
@@ -33,7 +33,7 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.lang;
 
 public class ComplementName implements Complement {
 
diff --git a/src/net/sourceforge/plantuml/project3/ComplementNamed.java b/src/net/sourceforge/plantuml/project/lang/ComplementNamed.java
similarity index 92%
rename from src/net/sourceforge/plantuml/project3/ComplementNamed.java
rename to src/net/sourceforge/plantuml/project/lang/ComplementNamed.java
index 0eee95740..55ee6fd6e 100644
--- a/src/net/sourceforge/plantuml/project3/ComplementNamed.java
+++ b/src/net/sourceforge/plantuml/project/lang/ComplementNamed.java
@@ -33,11 +33,13 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.lang;
 
 import net.sourceforge.plantuml.command.regex.IRegex;
 import net.sourceforge.plantuml.command.regex.RegexLeaf;
 import net.sourceforge.plantuml.command.regex.RegexResult;
+import net.sourceforge.plantuml.project.Failable;
+import net.sourceforge.plantuml.project.GanttDiagram;
 
 public class ComplementNamed implements ComplementPattern {
 
diff --git a/src/net/sourceforge/plantuml/project3/ComplementOpen.java b/src/net/sourceforge/plantuml/project/lang/ComplementOpen.java
similarity index 92%
rename from src/net/sourceforge/plantuml/project3/ComplementOpen.java
rename to src/net/sourceforge/plantuml/project/lang/ComplementOpen.java
index 975352f0f..69fd6bec0 100644
--- a/src/net/sourceforge/plantuml/project3/ComplementOpen.java
+++ b/src/net/sourceforge/plantuml/project/lang/ComplementOpen.java
@@ -33,11 +33,13 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.lang;
 
 import net.sourceforge.plantuml.command.regex.IRegex;
 import net.sourceforge.plantuml.command.regex.RegexLeaf;
 import net.sourceforge.plantuml.command.regex.RegexResult;
+import net.sourceforge.plantuml.project.Failable;
+import net.sourceforge.plantuml.project.GanttDiagram;
 
 public class ComplementOpen implements ComplementPattern {
 
diff --git a/src/net/sourceforge/plantuml/project3/ComplementPattern.java b/src/net/sourceforge/plantuml/project/lang/ComplementPattern.java
similarity index 91%
rename from src/net/sourceforge/plantuml/project3/ComplementPattern.java
rename to src/net/sourceforge/plantuml/project/lang/ComplementPattern.java
index 7f429a27f..cf5b0057f 100644
--- a/src/net/sourceforge/plantuml/project3/ComplementPattern.java
+++ b/src/net/sourceforge/plantuml/project/lang/ComplementPattern.java
@@ -33,10 +33,12 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.lang;
 
 import net.sourceforge.plantuml.command.regex.IRegex;
 import net.sourceforge.plantuml.command.regex.RegexResult;
+import net.sourceforge.plantuml.project.Failable;
+import net.sourceforge.plantuml.project.GanttDiagram;
 
 public interface ComplementPattern {
 
diff --git a/src/net/sourceforge/plantuml/project3/ComplementSeveralDays.java b/src/net/sourceforge/plantuml/project/lang/ComplementSeveralDays.java
similarity index 89%
rename from src/net/sourceforge/plantuml/project3/ComplementSeveralDays.java
rename to src/net/sourceforge/plantuml/project/lang/ComplementSeveralDays.java
index 91a1cd021..41a35d4bb 100644
--- a/src/net/sourceforge/plantuml/project3/ComplementSeveralDays.java
+++ b/src/net/sourceforge/plantuml/project/lang/ComplementSeveralDays.java
@@ -33,12 +33,15 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.lang;
 
 import net.sourceforge.plantuml.command.regex.IRegex;
 import net.sourceforge.plantuml.command.regex.RegexConcat;
 import net.sourceforge.plantuml.command.regex.RegexLeaf;
 import net.sourceforge.plantuml.command.regex.RegexResult;
+import net.sourceforge.plantuml.project.Failable;
+import net.sourceforge.plantuml.project.GanttDiagram;
+import net.sourceforge.plantuml.project.Load;
 
 public class ComplementSeveralDays implements ComplementPattern {
 
@@ -52,7 +55,7 @@ public class ComplementSeveralDays implements ComplementPattern {
 		final boolean inWeeks = arg.get("COMPLEMENT" + suffix, 1).startsWith("w");
 		final int factor = inWeeks ? system.daysInWeek() : 1;
 		final int days = Integer.parseInt(number) * factor;
-		return Failable.<Complement> ok(LoadInDays.inDay(days));
+		return Failable.<Complement> ok(Load.inWinks(days));
 	}
 
 }
diff --git a/src/net/sourceforge/plantuml/project3/Subject.java b/src/net/sourceforge/plantuml/project/lang/Subject.java
similarity index 96%
rename from src/net/sourceforge/plantuml/project3/Subject.java
rename to src/net/sourceforge/plantuml/project/lang/Subject.java
index ef1be6430..fba6a654e 100644
--- a/src/net/sourceforge/plantuml/project3/Subject.java
+++ b/src/net/sourceforge/plantuml/project/lang/Subject.java
@@ -33,7 +33,7 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.lang;
 
 
 public interface Subject {
diff --git a/src/net/sourceforge/plantuml/project3/SubjectDayAsDate.java b/src/net/sourceforge/plantuml/project/lang/SubjectDayAsDate.java
similarity index 93%
rename from src/net/sourceforge/plantuml/project3/SubjectDayAsDate.java
rename to src/net/sourceforge/plantuml/project/lang/SubjectDayAsDate.java
index 27737ac72..55992f2b8 100644
--- a/src/net/sourceforge/plantuml/project3/SubjectDayAsDate.java
+++ b/src/net/sourceforge/plantuml/project/lang/SubjectDayAsDate.java
@@ -33,7 +33,7 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.lang;
 
 import java.util.Arrays;
 import java.util.Collection;
@@ -42,6 +42,8 @@ import net.sourceforge.plantuml.command.regex.IRegex;
 import net.sourceforge.plantuml.command.regex.RegexConcat;
 import net.sourceforge.plantuml.command.regex.RegexLeaf;
 import net.sourceforge.plantuml.command.regex.RegexResult;
+import net.sourceforge.plantuml.project.DayAsDate;
+import net.sourceforge.plantuml.project.GanttDiagram;
 
 public class SubjectDayAsDate implements SubjectPattern {
 
diff --git a/src/net/sourceforge/plantuml/project3/SubjectDayOfWeek.java b/src/net/sourceforge/plantuml/project/lang/SubjectDayOfWeek.java
similarity index 92%
rename from src/net/sourceforge/plantuml/project3/SubjectDayOfWeek.java
rename to src/net/sourceforge/plantuml/project/lang/SubjectDayOfWeek.java
index 899d5e3cb..5f2ab8192 100644
--- a/src/net/sourceforge/plantuml/project3/SubjectDayOfWeek.java
+++ b/src/net/sourceforge/plantuml/project/lang/SubjectDayOfWeek.java
@@ -33,7 +33,7 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.lang;
 
 import java.util.Arrays;
 import java.util.Collection;
@@ -41,6 +41,8 @@ import java.util.Collection;
 import net.sourceforge.plantuml.command.regex.IRegex;
 import net.sourceforge.plantuml.command.regex.RegexLeaf;
 import net.sourceforge.plantuml.command.regex.RegexResult;
+import net.sourceforge.plantuml.project.DayOfWeek;
+import net.sourceforge.plantuml.project.GanttDiagram;
 
 public class SubjectDayOfWeek implements SubjectPattern {
 
diff --git a/src/net/sourceforge/plantuml/project3/SubjectDaysAsDates.java b/src/net/sourceforge/plantuml/project/lang/SubjectDaysAsDates.java
similarity index 95%
rename from src/net/sourceforge/plantuml/project3/SubjectDaysAsDates.java
rename to src/net/sourceforge/plantuml/project/lang/SubjectDaysAsDates.java
index ac7d04973..95398e7a5 100644
--- a/src/net/sourceforge/plantuml/project3/SubjectDaysAsDates.java
+++ b/src/net/sourceforge/plantuml/project/lang/SubjectDaysAsDates.java
@@ -33,7 +33,7 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.lang;
 
 import java.util.Arrays;
 import java.util.Collection;
@@ -43,6 +43,9 @@ import net.sourceforge.plantuml.command.regex.RegexConcat;
 import net.sourceforge.plantuml.command.regex.RegexLeaf;
 import net.sourceforge.plantuml.command.regex.RegexOr;
 import net.sourceforge.plantuml.command.regex.RegexResult;
+import net.sourceforge.plantuml.project.DayAsDate;
+import net.sourceforge.plantuml.project.DaysAsDates;
+import net.sourceforge.plantuml.project.GanttDiagram;
 
 public class SubjectDaysAsDates implements SubjectPattern {
 
diff --git a/src/net/sourceforge/plantuml/project3/SubjectPattern.java b/src/net/sourceforge/plantuml/project/lang/SubjectPattern.java
similarity index 93%
rename from src/net/sourceforge/plantuml/project3/SubjectPattern.java
rename to src/net/sourceforge/plantuml/project/lang/SubjectPattern.java
index 99d39c346..155f4719b 100644
--- a/src/net/sourceforge/plantuml/project3/SubjectPattern.java
+++ b/src/net/sourceforge/plantuml/project/lang/SubjectPattern.java
@@ -33,12 +33,13 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.lang;
 
 import java.util.Collection;
 
 import net.sourceforge.plantuml.command.regex.IRegex;
 import net.sourceforge.plantuml.command.regex.RegexResult;
+import net.sourceforge.plantuml.project.GanttDiagram;
 
 public interface SubjectPattern {
 
diff --git a/src/net/sourceforge/plantuml/project3/SubjectProject.java b/src/net/sourceforge/plantuml/project/lang/SubjectProject.java
similarity index 94%
rename from src/net/sourceforge/plantuml/project3/SubjectProject.java
rename to src/net/sourceforge/plantuml/project/lang/SubjectProject.java
index 49e83f809..88d36c2e4 100644
--- a/src/net/sourceforge/plantuml/project3/SubjectProject.java
+++ b/src/net/sourceforge/plantuml/project/lang/SubjectProject.java
@@ -33,7 +33,7 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.lang;
 
 import java.util.Arrays;
 import java.util.Collection;
@@ -41,6 +41,7 @@ import java.util.Collection;
 import net.sourceforge.plantuml.command.regex.IRegex;
 import net.sourceforge.plantuml.command.regex.RegexLeaf;
 import net.sourceforge.plantuml.command.regex.RegexResult;
+import net.sourceforge.plantuml.project.GanttDiagram;
 
 public class SubjectProject implements SubjectPattern {
 
diff --git a/src/net/sourceforge/plantuml/project3/SubjectResource.java b/src/net/sourceforge/plantuml/project/lang/SubjectResource.java
similarity index 95%
rename from src/net/sourceforge/plantuml/project3/SubjectResource.java
rename to src/net/sourceforge/plantuml/project/lang/SubjectResource.java
index 510d074b3..7723325c1 100644
--- a/src/net/sourceforge/plantuml/project3/SubjectResource.java
+++ b/src/net/sourceforge/plantuml/project/lang/SubjectResource.java
@@ -33,7 +33,7 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.lang;
 
 import java.util.Arrays;
 import java.util.Collection;
@@ -42,6 +42,7 @@ import net.sourceforge.plantuml.command.regex.IRegex;
 import net.sourceforge.plantuml.command.regex.RegexConcat;
 import net.sourceforge.plantuml.command.regex.RegexLeaf;
 import net.sourceforge.plantuml.command.regex.RegexResult;
+import net.sourceforge.plantuml.project.GanttDiagram;
 
 public class SubjectResource implements SubjectPattern {
 
diff --git a/src/net/sourceforge/plantuml/project3/SubjectTask.java b/src/net/sourceforge/plantuml/project/lang/SubjectTask.java
similarity index 93%
rename from src/net/sourceforge/plantuml/project3/SubjectTask.java
rename to src/net/sourceforge/plantuml/project/lang/SubjectTask.java
index e570b3e77..2b73cb61f 100644
--- a/src/net/sourceforge/plantuml/project3/SubjectTask.java
+++ b/src/net/sourceforge/plantuml/project/lang/SubjectTask.java
@@ -33,7 +33,7 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.lang;
 
 import java.util.Arrays;
 import java.util.Collection;
@@ -44,6 +44,8 @@ import net.sourceforge.plantuml.command.regex.RegexConcat;
 import net.sourceforge.plantuml.command.regex.RegexLeaf;
 import net.sourceforge.plantuml.command.regex.RegexOptional;
 import net.sourceforge.plantuml.command.regex.RegexResult;
+import net.sourceforge.plantuml.project.GanttDiagram;
+import net.sourceforge.plantuml.project.core.Task;
 
 public class SubjectTask implements SubjectPattern {
 
@@ -51,7 +53,7 @@ public class SubjectTask implements SubjectPattern {
 		return Arrays
 				.<VerbPattern> asList(new VerbLasts(), new VerbTaskStarts(), new VerbTaskStartsAbsolute(),
 						new VerbHappens(), new VerbEnds(), new VerbTaskEndsAbsolute(), new VerbIsColored(),
-						new VerbIsDeleted());
+						new VerbIsDeleted(), new VerbIsForTask());
 	}
 
 	public IRegex toRegex() {
diff --git a/src/net/sourceforge/plantuml/project3/SubjectToday.java b/src/net/sourceforge/plantuml/project/lang/SubjectToday.java
similarity index 92%
rename from src/net/sourceforge/plantuml/project3/SubjectToday.java
rename to src/net/sourceforge/plantuml/project/lang/SubjectToday.java
index 595414566..1e58b7d78 100644
--- a/src/net/sourceforge/plantuml/project3/SubjectToday.java
+++ b/src/net/sourceforge/plantuml/project/lang/SubjectToday.java
@@ -33,7 +33,7 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.lang;
 
 import java.util.Arrays;
 import java.util.Collection;
@@ -42,6 +42,8 @@ import net.sourceforge.plantuml.command.regex.IRegex;
 import net.sourceforge.plantuml.command.regex.RegexConcat;
 import net.sourceforge.plantuml.command.regex.RegexLeaf;
 import net.sourceforge.plantuml.command.regex.RegexResult;
+import net.sourceforge.plantuml.project.GanttDiagram;
+import net.sourceforge.plantuml.project.Today;
 
 public class SubjectToday implements SubjectPattern {
 
diff --git a/src/net/sourceforge/plantuml/project3/Verb.java b/src/net/sourceforge/plantuml/project/lang/Verb.java
similarity index 96%
rename from src/net/sourceforge/plantuml/project3/Verb.java
rename to src/net/sourceforge/plantuml/project/lang/Verb.java
index cd74b50df..dc46160a2 100644
--- a/src/net/sourceforge/plantuml/project3/Verb.java
+++ b/src/net/sourceforge/plantuml/project/lang/Verb.java
@@ -33,7 +33,7 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.lang;
 
 import net.sourceforge.plantuml.command.CommandExecutionResult;
 
diff --git a/src/net/sourceforge/plantuml/project3/VerbAre.java b/src/net/sourceforge/plantuml/project/lang/VerbAre.java
similarity index 93%
rename from src/net/sourceforge/plantuml/project3/VerbAre.java
rename to src/net/sourceforge/plantuml/project/lang/VerbAre.java
index 79ca1ca53..188e25d88 100644
--- a/src/net/sourceforge/plantuml/project3/VerbAre.java
+++ b/src/net/sourceforge/plantuml/project/lang/VerbAre.java
@@ -33,7 +33,7 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.lang;
 
 import java.util.Arrays;
 import java.util.Collection;
@@ -42,6 +42,8 @@ import net.sourceforge.plantuml.command.CommandExecutionResult;
 import net.sourceforge.plantuml.command.regex.IRegex;
 import net.sourceforge.plantuml.command.regex.RegexLeaf;
 import net.sourceforge.plantuml.command.regex.RegexResult;
+import net.sourceforge.plantuml.project.DayOfWeek;
+import net.sourceforge.plantuml.project.GanttDiagram;
 
 public class VerbAre implements VerbPattern {
 
diff --git a/src/net/sourceforge/plantuml/project3/VerbEnds.java b/src/net/sourceforge/plantuml/project/lang/VerbEnds.java
similarity index 87%
rename from src/net/sourceforge/plantuml/project3/VerbEnds.java
rename to src/net/sourceforge/plantuml/project/lang/VerbEnds.java
index 67637111d..37e67b3cf 100644
--- a/src/net/sourceforge/plantuml/project3/VerbEnds.java
+++ b/src/net/sourceforge/plantuml/project/lang/VerbEnds.java
@@ -33,7 +33,7 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.lang;
 
 import java.util.Arrays;
 import java.util.Collection;
@@ -42,6 +42,11 @@ import net.sourceforge.plantuml.command.CommandExecutionResult;
 import net.sourceforge.plantuml.command.regex.IRegex;
 import net.sourceforge.plantuml.command.regex.RegexLeaf;
 import net.sourceforge.plantuml.command.regex.RegexResult;
+import net.sourceforge.plantuml.project.GanttConstraint;
+import net.sourceforge.plantuml.project.GanttDiagram;
+import net.sourceforge.plantuml.project.core.Task;
+import net.sourceforge.plantuml.project.core.TaskAttribute;
+import net.sourceforge.plantuml.project.core.TaskInstant;
 
 public class VerbEnds implements VerbPattern {
 
diff --git a/src/net/sourceforge/plantuml/project3/VerbHappens.java b/src/net/sourceforge/plantuml/project/lang/VerbHappens.java
similarity index 88%
rename from src/net/sourceforge/plantuml/project3/VerbHappens.java
rename to src/net/sourceforge/plantuml/project/lang/VerbHappens.java
index ba647d221..c17a697d8 100644
--- a/src/net/sourceforge/plantuml/project3/VerbHappens.java
+++ b/src/net/sourceforge/plantuml/project/lang/VerbHappens.java
@@ -33,7 +33,7 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.lang;
 
 import java.util.Arrays;
 import java.util.Collection;
@@ -42,6 +42,11 @@ import net.sourceforge.plantuml.command.CommandExecutionResult;
 import net.sourceforge.plantuml.command.regex.IRegex;
 import net.sourceforge.plantuml.command.regex.RegexLeaf;
 import net.sourceforge.plantuml.command.regex.RegexResult;
+import net.sourceforge.plantuml.project.DayAsDate;
+import net.sourceforge.plantuml.project.GanttDiagram;
+import net.sourceforge.plantuml.project.Load;
+import net.sourceforge.plantuml.project.core.Task;
+import net.sourceforge.plantuml.project.core.TaskInstant;
 
 public class VerbHappens implements VerbPattern {
 
@@ -57,7 +62,7 @@ public class VerbHappens implements VerbPattern {
 		return new Verb() {
 			public CommandExecutionResult execute(Subject subject, Complement complement) {
 				final Task task = (Task) subject;
-				task.setLoad(LoadInDays.inDay(1));
+				task.setLoad(Load.inWinks(1));
 				if (complement instanceof DayAsDate) {
 					final DayAsDate start = (DayAsDate) complement;
 					final DayAsDate startingDate = project.getStartingDate();
diff --git a/src/net/sourceforge/plantuml/project3/VerbIsColored.java b/src/net/sourceforge/plantuml/project/lang/VerbIsColored.java
similarity index 93%
rename from src/net/sourceforge/plantuml/project3/VerbIsColored.java
rename to src/net/sourceforge/plantuml/project/lang/VerbIsColored.java
index a067a1345..a20d65314 100644
--- a/src/net/sourceforge/plantuml/project3/VerbIsColored.java
+++ b/src/net/sourceforge/plantuml/project/lang/VerbIsColored.java
@@ -33,7 +33,7 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.lang;
 
 import java.util.Arrays;
 import java.util.Collection;
@@ -42,6 +42,8 @@ import net.sourceforge.plantuml.command.CommandExecutionResult;
 import net.sourceforge.plantuml.command.regex.IRegex;
 import net.sourceforge.plantuml.command.regex.RegexLeaf;
 import net.sourceforge.plantuml.command.regex.RegexResult;
+import net.sourceforge.plantuml.project.GanttDiagram;
+import net.sourceforge.plantuml.project.core.Task;
 
 public class VerbIsColored implements VerbPattern {
 
diff --git a/src/net/sourceforge/plantuml/project3/VerbIsColoredForToday.java b/src/net/sourceforge/plantuml/project/lang/VerbIsColoredForToday.java
similarity index 93%
rename from src/net/sourceforge/plantuml/project3/VerbIsColoredForToday.java
rename to src/net/sourceforge/plantuml/project/lang/VerbIsColoredForToday.java
index fa9d5b39d..db4ba86b4 100644
--- a/src/net/sourceforge/plantuml/project3/VerbIsColoredForToday.java
+++ b/src/net/sourceforge/plantuml/project/lang/VerbIsColoredForToday.java
@@ -33,7 +33,7 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.lang;
 
 import java.util.Arrays;
 import java.util.Collection;
@@ -42,6 +42,8 @@ import net.sourceforge.plantuml.command.CommandExecutionResult;
 import net.sourceforge.plantuml.command.regex.IRegex;
 import net.sourceforge.plantuml.command.regex.RegexLeaf;
 import net.sourceforge.plantuml.command.regex.RegexResult;
+import net.sourceforge.plantuml.project.GanttDiagram;
+import net.sourceforge.plantuml.project.Today;
 
 public class VerbIsColoredForToday implements VerbPattern {
 
diff --git a/src/net/sourceforge/plantuml/project3/VerbIsDeleted.java b/src/net/sourceforge/plantuml/project/lang/VerbIsDeleted.java
similarity index 93%
rename from src/net/sourceforge/plantuml/project3/VerbIsDeleted.java
rename to src/net/sourceforge/plantuml/project/lang/VerbIsDeleted.java
index 6b1830d1b..b17519734 100644
--- a/src/net/sourceforge/plantuml/project3/VerbIsDeleted.java
+++ b/src/net/sourceforge/plantuml/project/lang/VerbIsDeleted.java
@@ -33,7 +33,7 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.lang;
 
 import java.util.Arrays;
 import java.util.Collection;
@@ -42,6 +42,8 @@ import net.sourceforge.plantuml.command.CommandExecutionResult;
 import net.sourceforge.plantuml.command.regex.IRegex;
 import net.sourceforge.plantuml.command.regex.RegexLeaf;
 import net.sourceforge.plantuml.command.regex.RegexResult;
+import net.sourceforge.plantuml.project.GanttDiagram;
+import net.sourceforge.plantuml.project.core.Task;
 
 public class VerbIsDeleted implements VerbPattern {
 
diff --git a/src/net/sourceforge/plantuml/project/lang/VerbIsForTask.java b/src/net/sourceforge/plantuml/project/lang/VerbIsForTask.java
new file mode 100644
index 000000000..800473f5e
--- /dev/null
+++ b/src/net/sourceforge/plantuml/project/lang/VerbIsForTask.java
@@ -0,0 +1,70 @@
+/* ========================================================================
+ * PlantUML : a free UML diagram generator
+ * ========================================================================
+ *
+ * (C) Copyright 2009-2020, Arnaud Roques
+ *
+ * Project Info:  http://plantuml.com
+ * 
+ * If you like this project or if you find it useful, you can support us at:
+ * 
+ * http://plantuml.com/patreon (only 1$ per month!)
+ * http://plantuml.com/paypal
+ * 
+ * This file is part of PlantUML.
+ *
+ * PlantUML is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * PlantUML distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
+ * USA.
+ *
+ *
+ * Original Author:  Arnaud Roques
+ * 
+ *
+ */
+package net.sourceforge.plantuml.project.lang;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+import net.sourceforge.plantuml.command.CommandExecutionResult;
+import net.sourceforge.plantuml.command.regex.IRegex;
+import net.sourceforge.plantuml.command.regex.RegexLeaf;
+import net.sourceforge.plantuml.command.regex.RegexResult;
+import net.sourceforge.plantuml.project.Completion;
+import net.sourceforge.plantuml.project.GanttDiagram;
+import net.sourceforge.plantuml.project.core.Task;
+
+public class VerbIsForTask implements VerbPattern {
+
+	public Collection<ComplementPattern> getComplements() {
+		return Arrays.<ComplementPattern>asList(new ComplementCompleted());
+	}
+
+	public IRegex toRegex() {
+		return new RegexLeaf("is");
+	}
+
+	public Verb getVerb(final GanttDiagram project, RegexResult arg) {
+		return new Verb() {
+			public CommandExecutionResult execute(Subject subject, Complement complement) {
+				final Task task = (Task) subject;
+				final Completion completed = (Completion) complement;
+				task.setCompletion(completed.getCompletion());
+				return CommandExecutionResult.ok();
+			}
+
+		};
+	}
+}
diff --git a/src/net/sourceforge/plantuml/project3/VerbIsForToday.java b/src/net/sourceforge/plantuml/project/lang/VerbIsForToday.java
similarity index 91%
rename from src/net/sourceforge/plantuml/project3/VerbIsForToday.java
rename to src/net/sourceforge/plantuml/project/lang/VerbIsForToday.java
index 61aa0d559..030df6334 100644
--- a/src/net/sourceforge/plantuml/project3/VerbIsForToday.java
+++ b/src/net/sourceforge/plantuml/project/lang/VerbIsForToday.java
@@ -33,7 +33,7 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.lang;
 
 import java.util.Arrays;
 import java.util.Collection;
@@ -42,6 +42,8 @@ import net.sourceforge.plantuml.command.CommandExecutionResult;
 import net.sourceforge.plantuml.command.regex.IRegex;
 import net.sourceforge.plantuml.command.regex.RegexLeaf;
 import net.sourceforge.plantuml.command.regex.RegexResult;
+import net.sourceforge.plantuml.project.DayAsDate;
+import net.sourceforge.plantuml.project.GanttDiagram;
 
 public class VerbIsForToday implements VerbPattern {
 
@@ -56,7 +58,7 @@ public class VerbIsForToday implements VerbPattern {
 	public Verb getVerb(final GanttDiagram project, RegexResult arg) {
 		return new Verb() {
 			public CommandExecutionResult execute(Subject subject, Complement complement) {
-				final Today task = (Today) subject;
+				// final Today task = (Today) subject;
 				final DayAsDate date = (DayAsDate) complement;
 				return project.setToday(date);
 			}
diff --git a/src/net/sourceforge/plantuml/project3/VerbIsOff.java b/src/net/sourceforge/plantuml/project/lang/VerbIsOff.java
similarity index 90%
rename from src/net/sourceforge/plantuml/project3/VerbIsOff.java
rename to src/net/sourceforge/plantuml/project/lang/VerbIsOff.java
index 7ce044d75..285ea496a 100644
--- a/src/net/sourceforge/plantuml/project3/VerbIsOff.java
+++ b/src/net/sourceforge/plantuml/project/lang/VerbIsOff.java
@@ -33,7 +33,7 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.lang;
 
 import java.util.Arrays;
 import java.util.Collection;
@@ -44,6 +44,11 @@ import net.sourceforge.plantuml.command.regex.RegexConcat;
 import net.sourceforge.plantuml.command.regex.RegexLeaf;
 import net.sourceforge.plantuml.command.regex.RegexOr;
 import net.sourceforge.plantuml.command.regex.RegexResult;
+import net.sourceforge.plantuml.project.DayAsDate;
+import net.sourceforge.plantuml.project.DayOfWeek;
+import net.sourceforge.plantuml.project.DaysAsDates;
+import net.sourceforge.plantuml.project.GanttDiagram;
+import net.sourceforge.plantuml.project.core.Resource;
 
 public class VerbIsOff implements VerbPattern {
 
diff --git a/src/net/sourceforge/plantuml/project3/VerbIsOn.java b/src/net/sourceforge/plantuml/project/lang/VerbIsOn.java
similarity index 91%
rename from src/net/sourceforge/plantuml/project3/VerbIsOn.java
rename to src/net/sourceforge/plantuml/project/lang/VerbIsOn.java
index 11e468830..44003c446 100644
--- a/src/net/sourceforge/plantuml/project3/VerbIsOn.java
+++ b/src/net/sourceforge/plantuml/project/lang/VerbIsOn.java
@@ -33,7 +33,7 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.lang;
 
 import java.util.Arrays;
 import java.util.Collection;
@@ -44,6 +44,10 @@ import net.sourceforge.plantuml.command.regex.RegexConcat;
 import net.sourceforge.plantuml.command.regex.RegexLeaf;
 import net.sourceforge.plantuml.command.regex.RegexOr;
 import net.sourceforge.plantuml.command.regex.RegexResult;
+import net.sourceforge.plantuml.project.DayAsDate;
+import net.sourceforge.plantuml.project.DaysAsDates;
+import net.sourceforge.plantuml.project.GanttDiagram;
+import net.sourceforge.plantuml.project.core.Resource;
 
 public class VerbIsOn implements VerbPattern {
 
diff --git a/src/net/sourceforge/plantuml/project3/VerbIsOrAre.java b/src/net/sourceforge/plantuml/project/lang/VerbIsOrAre.java
similarity index 94%
rename from src/net/sourceforge/plantuml/project3/VerbIsOrAre.java
rename to src/net/sourceforge/plantuml/project/lang/VerbIsOrAre.java
index fdc340126..dd639b263 100644
--- a/src/net/sourceforge/plantuml/project3/VerbIsOrAre.java
+++ b/src/net/sourceforge/plantuml/project/lang/VerbIsOrAre.java
@@ -33,7 +33,7 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.lang;
 
 import java.util.Arrays;
 import java.util.Collection;
@@ -43,6 +43,9 @@ import net.sourceforge.plantuml.command.regex.IRegex;
 import net.sourceforge.plantuml.command.regex.RegexLeaf;
 import net.sourceforge.plantuml.command.regex.RegexResult;
 import net.sourceforge.plantuml.graphic.HtmlColor;
+import net.sourceforge.plantuml.project.DayAsDate;
+import net.sourceforge.plantuml.project.DaysAsDates;
+import net.sourceforge.plantuml.project.GanttDiagram;
 
 public class VerbIsOrAre implements VerbPattern {
 
diff --git a/src/net/sourceforge/plantuml/project3/VerbIsOrAreNamed.java b/src/net/sourceforge/plantuml/project/lang/VerbIsOrAreNamed.java
similarity index 91%
rename from src/net/sourceforge/plantuml/project3/VerbIsOrAreNamed.java
rename to src/net/sourceforge/plantuml/project/lang/VerbIsOrAreNamed.java
index 91dcfd623..01062ee21 100644
--- a/src/net/sourceforge/plantuml/project3/VerbIsOrAreNamed.java
+++ b/src/net/sourceforge/plantuml/project/lang/VerbIsOrAreNamed.java
@@ -33,7 +33,7 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.lang;
 
 import java.util.Arrays;
 import java.util.Collection;
@@ -42,6 +42,9 @@ import net.sourceforge.plantuml.command.CommandExecutionResult;
 import net.sourceforge.plantuml.command.regex.IRegex;
 import net.sourceforge.plantuml.command.regex.RegexLeaf;
 import net.sourceforge.plantuml.command.regex.RegexResult;
+import net.sourceforge.plantuml.project.DayAsDate;
+import net.sourceforge.plantuml.project.DaysAsDates;
+import net.sourceforge.plantuml.project.GanttDiagram;
 
 public class VerbIsOrAreNamed implements VerbPattern {
 
diff --git a/src/net/sourceforge/plantuml/project3/VerbLasts.java b/src/net/sourceforge/plantuml/project/lang/VerbLasts.java
similarity index 91%
rename from src/net/sourceforge/plantuml/project3/VerbLasts.java
rename to src/net/sourceforge/plantuml/project/lang/VerbLasts.java
index 2a68a6ab7..71a0705f1 100644
--- a/src/net/sourceforge/plantuml/project3/VerbLasts.java
+++ b/src/net/sourceforge/plantuml/project/lang/VerbLasts.java
@@ -33,7 +33,7 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.lang;
 
 import java.util.Arrays;
 import java.util.Collection;
@@ -42,6 +42,9 @@ import net.sourceforge.plantuml.command.CommandExecutionResult;
 import net.sourceforge.plantuml.command.regex.IRegex;
 import net.sourceforge.plantuml.command.regex.RegexLeaf;
 import net.sourceforge.plantuml.command.regex.RegexResult;
+import net.sourceforge.plantuml.project.GanttDiagram;
+import net.sourceforge.plantuml.project.Load;
+import net.sourceforge.plantuml.project.core.Task;
 
 public class VerbLasts implements VerbPattern {
 
diff --git a/src/net/sourceforge/plantuml/project3/VerbPattern.java b/src/net/sourceforge/plantuml/project/lang/VerbPattern.java
similarity index 93%
rename from src/net/sourceforge/plantuml/project3/VerbPattern.java
rename to src/net/sourceforge/plantuml/project/lang/VerbPattern.java
index c810ceffe..8360b4bb1 100644
--- a/src/net/sourceforge/plantuml/project3/VerbPattern.java
+++ b/src/net/sourceforge/plantuml/project/lang/VerbPattern.java
@@ -33,12 +33,13 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.lang;
 
 import java.util.Collection;
 
 import net.sourceforge.plantuml.command.regex.IRegex;
 import net.sourceforge.plantuml.command.regex.RegexResult;
+import net.sourceforge.plantuml.project.GanttDiagram;
 
 public interface VerbPattern {
 
diff --git a/src/net/sourceforge/plantuml/project3/VerbProjectStarts.java b/src/net/sourceforge/plantuml/project/lang/VerbProjectStarts.java
similarity index 94%
rename from src/net/sourceforge/plantuml/project3/VerbProjectStarts.java
rename to src/net/sourceforge/plantuml/project/lang/VerbProjectStarts.java
index d9bceb122..2d0b047fc 100644
--- a/src/net/sourceforge/plantuml/project3/VerbProjectStarts.java
+++ b/src/net/sourceforge/plantuml/project/lang/VerbProjectStarts.java
@@ -33,7 +33,7 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.lang;
 
 import java.util.Arrays;
 import java.util.Collection;
@@ -45,6 +45,8 @@ import net.sourceforge.plantuml.command.regex.RegexLeaf;
 import net.sourceforge.plantuml.command.regex.RegexOptional;
 import net.sourceforge.plantuml.command.regex.RegexOr;
 import net.sourceforge.plantuml.command.regex.RegexResult;
+import net.sourceforge.plantuml.project.DayAsDate;
+import net.sourceforge.plantuml.project.GanttDiagram;
 
 public class VerbProjectStarts implements VerbPattern {
 
diff --git a/src/net/sourceforge/plantuml/project3/VerbTaskEndsAbsolute.java b/src/net/sourceforge/plantuml/project/lang/VerbTaskEndsAbsolute.java
similarity index 92%
rename from src/net/sourceforge/plantuml/project3/VerbTaskEndsAbsolute.java
rename to src/net/sourceforge/plantuml/project/lang/VerbTaskEndsAbsolute.java
index 1fb86199e..10d44afd4 100644
--- a/src/net/sourceforge/plantuml/project3/VerbTaskEndsAbsolute.java
+++ b/src/net/sourceforge/plantuml/project/lang/VerbTaskEndsAbsolute.java
@@ -33,7 +33,7 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.lang;
 
 import java.util.Arrays;
 import java.util.Collection;
@@ -42,6 +42,9 @@ import net.sourceforge.plantuml.command.CommandExecutionResult;
 import net.sourceforge.plantuml.command.regex.IRegex;
 import net.sourceforge.plantuml.command.regex.RegexLeaf;
 import net.sourceforge.plantuml.command.regex.RegexResult;
+import net.sourceforge.plantuml.project.DayAsDate;
+import net.sourceforge.plantuml.project.GanttDiagram;
+import net.sourceforge.plantuml.project.core.Task;
 
 public class VerbTaskEndsAbsolute implements VerbPattern {
 
diff --git a/src/net/sourceforge/plantuml/project3/VerbTaskStarts.java b/src/net/sourceforge/plantuml/project/lang/VerbTaskStarts.java
similarity index 87%
rename from src/net/sourceforge/plantuml/project3/VerbTaskStarts.java
rename to src/net/sourceforge/plantuml/project/lang/VerbTaskStarts.java
index fc1468755..0418b49d4 100644
--- a/src/net/sourceforge/plantuml/project3/VerbTaskStarts.java
+++ b/src/net/sourceforge/plantuml/project/lang/VerbTaskStarts.java
@@ -33,7 +33,7 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.lang;
 
 import java.util.Arrays;
 import java.util.Collection;
@@ -42,6 +42,11 @@ import net.sourceforge.plantuml.command.CommandExecutionResult;
 import net.sourceforge.plantuml.command.regex.IRegex;
 import net.sourceforge.plantuml.command.regex.RegexLeaf;
 import net.sourceforge.plantuml.command.regex.RegexResult;
+import net.sourceforge.plantuml.project.GanttConstraint;
+import net.sourceforge.plantuml.project.GanttDiagram;
+import net.sourceforge.plantuml.project.core.Task;
+import net.sourceforge.plantuml.project.core.TaskAttribute;
+import net.sourceforge.plantuml.project.core.TaskInstant;
 
 public class VerbTaskStarts implements VerbPattern {
 
diff --git a/src/net/sourceforge/plantuml/project3/VerbTaskStartsAbsolute.java b/src/net/sourceforge/plantuml/project/lang/VerbTaskStartsAbsolute.java
similarity index 92%
rename from src/net/sourceforge/plantuml/project3/VerbTaskStartsAbsolute.java
rename to src/net/sourceforge/plantuml/project/lang/VerbTaskStartsAbsolute.java
index 5f8b4eca3..22dee2089 100644
--- a/src/net/sourceforge/plantuml/project3/VerbTaskStartsAbsolute.java
+++ b/src/net/sourceforge/plantuml/project/lang/VerbTaskStartsAbsolute.java
@@ -33,7 +33,7 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.lang;
 
 import java.util.Arrays;
 import java.util.Collection;
@@ -42,6 +42,9 @@ import net.sourceforge.plantuml.command.CommandExecutionResult;
 import net.sourceforge.plantuml.command.regex.IRegex;
 import net.sourceforge.plantuml.command.regex.RegexLeaf;
 import net.sourceforge.plantuml.command.regex.RegexResult;
+import net.sourceforge.plantuml.project.DayAsDate;
+import net.sourceforge.plantuml.project.GanttDiagram;
+import net.sourceforge.plantuml.project.core.Task;
 
 public class VerbTaskStartsAbsolute implements VerbPattern {
 
diff --git a/src/net/sourceforge/plantuml/project3/TimeScale.java b/src/net/sourceforge/plantuml/project/timescale/TimeScale.java
similarity index 84%
rename from src/net/sourceforge/plantuml/project3/TimeScale.java
rename to src/net/sourceforge/plantuml/project/timescale/TimeScale.java
index 7644d444b..b5baf4ef6 100644
--- a/src/net/sourceforge/plantuml/project3/TimeScale.java
+++ b/src/net/sourceforge/plantuml/project/timescale/TimeScale.java
@@ -33,14 +33,16 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.timescale;
+
+import net.sourceforge.plantuml.project.core.Wink;
 
 public interface TimeScale {
 
-	public double getStartingPosition(Instant instant);
+	public double getStartingPosition(Wink instant);
 
-	public double getEndingPosition(Instant instant);
+	public double getEndingPosition(Wink instant);
 
-	public double getWidth(Instant instant);
+	public double getWidth(Wink instant);
 
 }
diff --git a/src/net/sourceforge/plantuml/project/timescale/TimeScaleDaily.java b/src/net/sourceforge/plantuml/project/timescale/TimeScaleDaily.java
new file mode 100644
index 000000000..e8bafb6cb
--- /dev/null
+++ b/src/net/sourceforge/plantuml/project/timescale/TimeScaleDaily.java
@@ -0,0 +1,69 @@
+/* ========================================================================
+ * PlantUML : a free UML diagram generator
+ * ========================================================================
+ *
+ * (C) Copyright 2009-2020, Arnaud Roques
+ *
+ * Project Info:  http://plantuml.com
+ * 
+ * If you like this project or if you find it useful, you can support us at:
+ * 
+ * http://plantuml.com/patreon (only 1$ per month!)
+ * http://plantuml.com/paypal
+ * 
+ * This file is part of PlantUML.
+ *
+ * PlantUML is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * PlantUML distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
+ * USA.
+ *
+ *
+ * Original Author:  Arnaud Roques
+ * 
+ *
+ */
+package net.sourceforge.plantuml.project.timescale;
+
+import net.sourceforge.plantuml.project.DayAsDate;
+import net.sourceforge.plantuml.project.GCalendar;
+import net.sourceforge.plantuml.project.core.Wink;
+
+public final class TimeScaleDaily implements TimeScale {
+
+	private final TimeScaleWink basic;
+	private final double delta;
+
+	public TimeScaleDaily(GCalendar calendar, DayAsDate zeroDay) {
+		this.basic = new TimeScaleWink();
+		if (zeroDay == null) {
+			this.delta = 0;
+		} else {
+			this.delta = basic.getStartingPosition(calendar.fromDayAsDate(zeroDay));
+		}
+
+	}
+
+	public double getStartingPosition(Wink instant) {
+		return basic.getStartingPosition(instant) - delta;
+	}
+
+	public double getEndingPosition(Wink instant) {
+		return basic.getEndingPosition(instant) - delta;
+	}
+
+	public double getWidth(Wink instant) {
+		return basic.getWidth(instant);
+	}
+
+}
diff --git a/src/net/sourceforge/plantuml/project3/LoadInDays.java b/src/net/sourceforge/plantuml/project/timescale/TimeScaleWeekly.java
similarity index 65%
rename from src/net/sourceforge/plantuml/project3/LoadInDays.java
rename to src/net/sourceforge/plantuml/project/timescale/TimeScaleWeekly.java
index 7ef804869..c06dc0780 100644
--- a/src/net/sourceforge/plantuml/project3/LoadInDays.java
+++ b/src/net/sourceforge/plantuml/project/timescale/TimeScaleWeekly.java
@@ -33,28 +33,30 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.timescale;
 
-public class LoadInDays implements Load {
+import net.sourceforge.plantuml.project.GCalendar;
+import net.sourceforge.plantuml.project.core.Wink;
 
-	private final int days;
-	private final int loadPerDay;
+public class TimeScaleWeekly implements TimeScale {
 
-	private LoadInDays(int days, int loadPerDay) {
-		this.days = days;
-		this.loadPerDay = loadPerDay;
+	private static final int COMPRESS = 4;
+	private final TimeScale daily;
+
+	public TimeScaleWeekly(GCalendar calendar) {
+		this.daily = new TimeScaleDaily(calendar, null);
 	}
 
-	public static LoadInDays inDay(int days) {
-		return new LoadInDays(days, 100);
+	public double getStartingPosition(Wink instant) {
+		return daily.getStartingPosition(instant) / COMPRESS;
 	}
 
-	public int getFullLoad() {
-		return days * loadPerDay;
+	public double getEndingPosition(Wink instant) {
+		return daily.getEndingPosition(instant) / COMPRESS;
 	}
 
-	public int getLoadAt(Instant instant) {
-		return loadPerDay;
+	public double getWidth(Wink instant) {
+		return daily.getWidth(instant) / COMPRESS;
 	}
 
 }
diff --git a/src/net/sourceforge/plantuml/project3/TimeScaleBasic.java b/src/net/sourceforge/plantuml/project/timescale/TimeScaleWink.java
similarity index 79%
rename from src/net/sourceforge/plantuml/project3/TimeScaleBasic.java
rename to src/net/sourceforge/plantuml/project/timescale/TimeScaleWink.java
index 85d5d9f3a..118642613 100644
--- a/src/net/sourceforge/plantuml/project3/TimeScaleBasic.java
+++ b/src/net/sourceforge/plantuml/project/timescale/TimeScaleWink.java
@@ -33,22 +33,24 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.timescale;
 
-public class TimeScaleBasic implements TimeScale {
+import net.sourceforge.plantuml.project.core.Wink;
+
+public class TimeScaleWink implements TimeScale {
 
 	private final double scale = 16.0;
 
-	public double getStartingPosition(Instant instant) {
-		final int day = ((InstantDay) instant).getNumDay();
-		return day * scale;
+	public double getStartingPosition(Wink instant) {
+		final int wink = instant.getWink();
+		return wink * scale;
 	}
 
-	public double getEndingPosition(Instant instant) {
+	public double getEndingPosition(Wink instant) {
 		return getStartingPosition(instant) + getWidth(instant);
 	}
 
-	public double getWidth(Instant instant) {
+	public double getWidth(Wink instant) {
 		return scale;
 	}
 
diff --git a/src/net/sourceforge/plantuml/project3/TimeScaleWithoutWeekEnd.java b/src/net/sourceforge/plantuml/project/timescale/UnusedTimeScaleWithoutWeekEnd.java
similarity index 72%
rename from src/net/sourceforge/plantuml/project3/TimeScaleWithoutWeekEnd.java
rename to src/net/sourceforge/plantuml/project/timescale/UnusedTimeScaleWithoutWeekEnd.java
index 9ef521c4c..da6e677e5 100644
--- a/src/net/sourceforge/plantuml/project3/TimeScaleWithoutWeekEnd.java
+++ b/src/net/sourceforge/plantuml/project/timescale/UnusedTimeScaleWithoutWeekEnd.java
@@ -33,32 +33,37 @@
  * 
  *
  */
-package net.sourceforge.plantuml.project3;
+package net.sourceforge.plantuml.project.timescale;
 
-public class TimeScaleWithoutWeekEnd implements TimeScale {
+import net.sourceforge.plantuml.project.DayAsDate;
+import net.sourceforge.plantuml.project.DayOfWeek;
+import net.sourceforge.plantuml.project.GCalendar;
+import net.sourceforge.plantuml.project.core.Wink;
+
+public class UnusedTimeScaleWithoutWeekEnd implements TimeScale {
 
 	private final double scale = 16.0;
 	private final GCalendar calendar;
 
-	public TimeScaleWithoutWeekEnd(GCalendar calendar) {
+	public UnusedTimeScaleWithoutWeekEnd(GCalendar calendar) {
 		if (calendar == null) {
 			throw new IllegalArgumentException();
 		}
 		this.calendar = calendar;
 	}
 
-	public double getStartingPosition(Instant instant) {
+	public double getStartingPosition(Wink instant) {
 		double result = 0;
-		InstantDay current = (InstantDay) instant;
-		while (current.getNumDay() > 0) {
+		Wink current = (Wink) instant;
+		while (current.getWink() > 0) {
 			current = current.decrement();
 			result += getWidth(current);
 		}
 		return result;
 	}
 
-	public double getWidth(Instant instant) {
-		final DayAsDate day = calendar.toDayAsDate((InstantDay) instant);
+	public double getWidth(Wink instant) {
+		final DayAsDate day = calendar.toDayAsDate((Wink) instant);
 		final DayOfWeek dayOfWeek = day.getDayOfWeek();
 		if (dayOfWeek == DayOfWeek.SATURDAY || dayOfWeek == DayOfWeek.SUNDAY) {
 			return 1;
@@ -66,7 +71,7 @@ public class TimeScaleWithoutWeekEnd implements TimeScale {
 		return scale;
 	}
 
-	public double getEndingPosition(Instant instant) {
+	public double getEndingPosition(Wink instant) {
 		throw new UnsupportedOperationException();
 	}
 
diff --git a/src/net/sourceforge/plantuml/project3/TimeScaleBasic2.java b/src/net/sourceforge/plantuml/project3/TimeScaleBasic2.java
deleted file mode 100644
index 5341ef0a1..000000000
--- a/src/net/sourceforge/plantuml/project3/TimeScaleBasic2.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/* ========================================================================
- * PlantUML : a free UML diagram generator
- * ========================================================================
- *
- * (C) Copyright 2009-2020, Arnaud Roques
- *
- * Project Info:  http://plantuml.com
- * 
- * If you like this project or if you find it useful, you can support us at:
- * 
- * http://plantuml.com/patreon (only 1$ per month!)
- * http://plantuml.com/paypal
- * 
- * This file is part of PlantUML.
- *
- * PlantUML is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * PlantUML distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
- * License for more details.
- *
- * You should have received a copy of the GNU General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
- * USA.
- *
- *
- * Original Author:  Arnaud Roques
- * 
- *
- */
-package net.sourceforge.plantuml.project3;
-
-import java.util.Map;
-import java.util.TreeMap;
-
-public class TimeScaleBasic2 implements TimeScale {
-
-	private final GCalendar calendar;
-	private final GCalendar calendarAllOpen;
-	private final TimeScaleBasic basic = new TimeScaleBasic();
-	private final Map<Instant, Instant> cache = new TreeMap<Instant, Instant>();
-
-	public TimeScaleBasic2(GCalendarSimple calendar) {
-		this.calendar = calendar;
-		this.calendarAllOpen = calendar;
-	}
-
-	private Instant changeInstantSlow(Instant instant) {
-		final DayAsDate day = calendar.toDayAsDate((InstantDay) instant);
-		return calendarAllOpen.fromDayAsDate(day);
-	}
-
-	private Instant changeInstant(Instant instant) {
-		Instant result = cache.get(instant);
-		if (result == null) {
-			result = changeInstantSlow(instant);
-			cache.put(instant, result);
-		}
-		return result;
-	}
-
-	public double getStartingPosition(Instant instant) {
-		return basic.getStartingPosition(changeInstant(instant));
-	}
-
-	public double getEndingPosition(Instant instant) {
-		return basic.getEndingPosition(changeInstant(instant));
-	}
-
-	public double getWidth(Instant instant) {
-		return basic.getWidth(changeInstant(instant));
-	}
-
-}
diff --git a/src/net/sourceforge/plantuml/salt/PSystemSalt.java b/src/net/sourceforge/plantuml/salt/PSystemSalt.java
index e7fc050b6..9ade106b7 100644
--- a/src/net/sourceforge/plantuml/salt/PSystemSalt.java
+++ b/src/net/sourceforge/plantuml/salt/PSystemSalt.java
@@ -54,7 +54,7 @@ import net.sourceforge.plantuml.api.ImageDataSimple;
 import net.sourceforge.plantuml.command.BlocLines;
 import net.sourceforge.plantuml.command.Command;
 import net.sourceforge.plantuml.command.CommandExecutionResult;
-import net.sourceforge.plantuml.command.FactorySpriteCommand;
+import net.sourceforge.plantuml.command.CommandFactorySprite;
 import net.sourceforge.plantuml.core.DiagramDescription;
 import net.sourceforge.plantuml.core.ImageData;
 import net.sourceforge.plantuml.graphic.HtmlColorUtils;
@@ -143,8 +143,7 @@ public class PSystemSalt extends AbstractPSystem implements WithSprite {
 
 	private List<String> manageSprite() {
 
-		final FactorySpriteCommand factorySpriteCommand = new FactorySpriteCommand();
-		Command<WithSprite> cmd = factorySpriteCommand.createMultiLine(false);
+		final Command<WithSprite> cmd = new CommandFactorySprite().createMultiLine(false);
 
 		final List<String> result = new ArrayList<String>();
 		for (Iterator<String> it = data.iterator(); it.hasNext();) {
diff --git a/src/net/sourceforge/plantuml/sequencediagram/graphic/SequenceDiagramFileMakerPuma2.java b/src/net/sourceforge/plantuml/sequencediagram/graphic/SequenceDiagramFileMakerPuma2.java
index f76166c40..5c9ea175d 100644
--- a/src/net/sourceforge/plantuml/sequencediagram/graphic/SequenceDiagramFileMakerPuma2.java
+++ b/src/net/sourceforge/plantuml/sequencediagram/graphic/SequenceDiagramFileMakerPuma2.java
@@ -166,7 +166,7 @@ public class SequenceDiagramFileMakerPuma2 implements FileMaker {
 						diagram.getSkinParam());
 			} else {
 				compTitle = TextBlockUtils.withMargin(TextBlockUtils.title(
-						new FontConfiguration(drawableSet.getSkinParam(), FontParam.SEQUENCE_TITLE, null),
+						new FontConfiguration(drawableSet.getSkinParam(), FontParam.TITLE, null),
 						page.getTitle(), drawableSet.getSkinParam()), 7, 7);
 			}
 			final Dimension2D dimTitle = compTitle.calculateDimension(stringBounder);
diff --git a/src/net/sourceforge/plantuml/sequencediagram/teoz/SequenceDiagramFileMakerTeoz.java b/src/net/sourceforge/plantuml/sequencediagram/teoz/SequenceDiagramFileMakerTeoz.java
index 4a1825953..f6dbe4135 100644
--- a/src/net/sourceforge/plantuml/sequencediagram/teoz/SequenceDiagramFileMakerTeoz.java
+++ b/src/net/sourceforge/plantuml/sequencediagram/teoz/SequenceDiagramFileMakerTeoz.java
@@ -229,7 +229,7 @@ public class SequenceDiagramFileMakerTeoz implements FileMaker {
 					.getIHtmlColorSet(), diagram.getSkinParam());
 			return compTitle;
 		} else {
-			compTitle = TextBlockUtils.title(new FontConfiguration(getSkinParam(), FontParam.SEQUENCE_TITLE, null),
+			compTitle = TextBlockUtils.title(new FontConfiguration(getSkinParam(), FontParam.TITLE, null),
 					diagram.getTitle().getDisplay(), getSkinParam());
 			return TextBlockUtils.withMargin(compTitle, 7, 7);
 		}
diff --git a/src/net/sourceforge/plantuml/sprite/CommandStdlib.java b/src/net/sourceforge/plantuml/sprite/CommandStdlib.java
new file mode 100644
index 000000000..3f862c965
--- /dev/null
+++ b/src/net/sourceforge/plantuml/sprite/CommandStdlib.java
@@ -0,0 +1,67 @@
+/* ========================================================================
+ * PlantUML : a free UML diagram generator
+ * ========================================================================
+ *
+ * (C) Copyright 2009-2020, Arnaud Roques
+ *
+ * Project Info:  http://plantuml.com
+ * 
+ * If you like this project or if you find it useful, you can support us at:
+ * 
+ * http://plantuml.com/patreon (only 1$ per month!)
+ * http://plantuml.com/paypal
+ * 
+ * This file is part of PlantUML.
+ *
+ * PlantUML is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * PlantUML distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
+ * USA.
+ *
+ *
+ * Original Author:  Arnaud Roques
+ * 
+ *
+ */
+package net.sourceforge.plantuml.sprite;
+
+import net.sourceforge.plantuml.LineLocation;
+import net.sourceforge.plantuml.command.CommandExecutionResult;
+import net.sourceforge.plantuml.command.SingleLineCommand2;
+import net.sourceforge.plantuml.command.regex.IRegex;
+import net.sourceforge.plantuml.command.regex.RegexConcat;
+import net.sourceforge.plantuml.command.regex.RegexLeaf;
+import net.sourceforge.plantuml.command.regex.RegexResult;
+
+public class CommandStdlib extends SingleLineCommand2<StdlibDiagram> {
+
+	public CommandStdlib() {
+		super(getRegexConcat());
+	}
+
+	private static IRegex getRegexConcat() {
+		return RegexConcat.build(CommandStdlib.class.getName(), RegexLeaf.start(), //
+				new RegexLeaf("stdlib"), //
+				RegexLeaf.spaceOneOrMore(), //
+				new RegexLeaf("NAME", "([-\\w]+)"), //
+				RegexLeaf.end());
+	}
+
+	@Override
+	protected CommandExecutionResult executeArg(StdlibDiagram system, LineLocation location, RegexResult arg) {
+		final String name = arg.get("NAME", 0);
+		system.setStdlibName(name);
+		return CommandExecutionResult.ok();
+	}
+
+}
diff --git a/src/net/sourceforge/plantuml/sprite/StdlibDiagram.java b/src/net/sourceforge/plantuml/sprite/StdlibDiagram.java
new file mode 100644
index 000000000..0edd374b0
--- /dev/null
+++ b/src/net/sourceforge/plantuml/sprite/StdlibDiagram.java
@@ -0,0 +1,164 @@
+/* ========================================================================
+ * PlantUML : a free UML diagram generator
+ * ========================================================================
+ *
+ * (C) Copyright 2009-2020, Arnaud Roques
+ *
+ * Project Info:  http://plantuml.com
+ * 
+ * If you like this project or if you find it useful, you can support us at:
+ * 
+ * http://plantuml.com/patreon (only 1$ per month!)
+ * http://plantuml.com/paypal
+ * 
+ * This file is part of PlantUML.
+ *
+ * PlantUML is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * PlantUML distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
+ * USA.
+ *
+ *
+ * Original Author:  Arnaud Roques
+ *
+ *
+ */
+package net.sourceforge.plantuml.sprite;
+
+import java.awt.geom.Dimension2D;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.List;
+
+import net.sourceforge.plantuml.Dimension2DDouble;
+import net.sourceforge.plantuml.FileFormatOption;
+import net.sourceforge.plantuml.ISkinSimple;
+import net.sourceforge.plantuml.UmlDiagram;
+import net.sourceforge.plantuml.UmlDiagramType;
+import net.sourceforge.plantuml.WithSprite;
+import net.sourceforge.plantuml.command.BlocLines;
+import net.sourceforge.plantuml.command.Command;
+import net.sourceforge.plantuml.command.CommandFactorySprite;
+import net.sourceforge.plantuml.core.DiagramDescription;
+import net.sourceforge.plantuml.core.ImageData;
+import net.sourceforge.plantuml.cucadiagram.Display;
+import net.sourceforge.plantuml.graphic.AbstractTextBlock;
+import net.sourceforge.plantuml.graphic.FontConfiguration;
+import net.sourceforge.plantuml.graphic.HorizontalAlignment;
+import net.sourceforge.plantuml.graphic.HtmlColorUtils;
+import net.sourceforge.plantuml.graphic.StringBounder;
+import net.sourceforge.plantuml.graphic.TextBlock;
+import net.sourceforge.plantuml.graphic.TextBlockUtils;
+import net.sourceforge.plantuml.preproc.Stdlib;
+import net.sourceforge.plantuml.ugraphic.ImageBuilder;
+import net.sourceforge.plantuml.ugraphic.UFont;
+import net.sourceforge.plantuml.ugraphic.UGraphic;
+import net.sourceforge.plantuml.ugraphic.UTranslate;
+
+public class StdlibDiagram extends UmlDiagram {
+
+	private static final int WIDTH = 1800;
+	private String name;
+
+	public StdlibDiagram(ISkinSimple skinParam) {
+		super(skinParam);
+	}
+
+	public DiagramDescription getDescription() {
+		return new DiagramDescription("(Sprites)");
+	}
+
+	@Override
+	public UmlDiagramType getUmlDiagramType() {
+		return UmlDiagramType.HELP;
+	}
+
+	@Override
+	protected ImageData exportDiagramInternal(OutputStream os, int index, FileFormatOption fileFormatOption)
+			throws IOException {
+
+		final TextBlock result = getTable();
+		final double margin = 10;
+		final double dpiFactor = 1;
+
+		final ImageBuilder imageBuilder = new ImageBuilder(getSkinParam(), dpiFactor,
+				fileFormatOption.isWithMetadata() ? getMetadata() : null, getWarningOrError(), margin, margin,
+				getAnimation());
+		imageBuilder.setUDrawable(result);
+
+		return imageBuilder.writeImageTOBEMOVED(fileFormatOption, seed(), os);
+	}
+
+	private TextBlock getTable() {
+		return new AbstractTextBlock() {
+
+			public void drawU(UGraphic ug) {
+				try {
+					drawInternal(ug);
+				} catch (IOException e) {
+					e.printStackTrace();
+				}
+			}
+
+			public Dimension2D calculateDimension(StringBounder stringBounder) {
+				return new Dimension2DDouble(WIDTH, 4096);
+			}
+		};
+	}
+
+	public void setStdlibName(String name) {
+		this.name = name;
+	}
+
+	private void drawInternal(UGraphic ug) throws IOException {
+		double x = 0;
+		double y = 0;
+		double rawHeight = 0;
+		final Stdlib folder = Stdlib.retrieve(name);
+
+		final CommandFactorySprite factorySpriteCommand = new CommandFactorySprite();
+
+		Command<WithSprite> cmd = factorySpriteCommand.createMultiLine(false);
+
+		final List<String> all = folder.extractAllSprites();
+		int nb = 0;
+		for (String s : all) {
+			// System.err.println("s="+s);
+			final BlocLines bloc = BlocLines.fromArray(s.split("\n"));
+			cmd.execute(this, bloc);
+//			System.err.println("nb=" + nb);
+			nb++;
+		}
+
+		for (String n : getSkinParam().getAllSpriteNames()) {
+			final Sprite sprite = getSkinParam().getSprite(n);
+			TextBlock blockName = Display.create(n).create(FontConfiguration.blackBlueTrue(UFont.sansSerif(14)),
+					HorizontalAlignment.LEFT, getSkinParam());
+			TextBlock tb = sprite.asTextBlock(HtmlColorUtils.BLACK, 1.0);
+			tb = TextBlockUtils.mergeTB(tb, blockName, HorizontalAlignment.CENTER);
+			tb.drawU(ug.apply(new UTranslate(x, y)));
+			final Dimension2D dim = tb.calculateDimension(ug.getStringBounder());
+			rawHeight = Math.max(rawHeight, dim.getHeight());
+			x += dim.getWidth();
+			x += 30;
+			if (x > WIDTH) {
+				x = 0;
+				y += rawHeight + 50;
+				rawHeight = 0;
+				if (y > 1024) {
+//					break;
+				}
+			}
+		}
+	}
+}
diff --git a/src/net/sourceforge/plantuml/sprite/StdlibDiagramFactory.java b/src/net/sourceforge/plantuml/sprite/StdlibDiagramFactory.java
new file mode 100644
index 000000000..b2d427689
--- /dev/null
+++ b/src/net/sourceforge/plantuml/sprite/StdlibDiagramFactory.java
@@ -0,0 +1,66 @@
+/* ========================================================================
+ * PlantUML : a free UML diagram generator
+ * ========================================================================
+ *
+ * (C) Copyright 2009-2020, Arnaud Roques
+ *
+ * Project Info:  http://plantuml.com
+ * 
+ * If you like this project or if you find it useful, you can support us at:
+ * 
+ * http://plantuml.com/patreon (only 1$ per month!)
+ * http://plantuml.com/paypal
+ * 
+ * This file is part of PlantUML.
+ *
+ * PlantUML is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * PlantUML distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
+ * USA.
+ *
+ *
+ * Original Author:  Arnaud Roques
+ *
+ *
+ */
+package net.sourceforge.plantuml.sprite;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import net.sourceforge.plantuml.ISkinSimple;
+import net.sourceforge.plantuml.command.Command;
+import net.sourceforge.plantuml.command.UmlDiagramFactory;
+
+public class StdlibDiagramFactory extends UmlDiagramFactory {
+
+	private final ISkinSimple skinParam;
+
+	public StdlibDiagramFactory(ISkinSimple skinParam) {
+		this.skinParam = skinParam;
+	}
+
+	@Override
+	protected List<Command> createCommands() {
+
+		final List<Command> cmds = new ArrayList<Command>();
+		cmds.add(new CommandStdlib());
+		return cmds;
+	}
+
+	@Override
+	public StdlibDiagram createEmptyDiagram() {
+		return new StdlibDiagram(skinParam);
+	}
+
+}
diff --git a/src/net/sourceforge/plantuml/statediagram/StateDiagramFactory.java b/src/net/sourceforge/plantuml/statediagram/StateDiagramFactory.java
index 654c05526..0f54ec44b 100644
--- a/src/net/sourceforge/plantuml/statediagram/StateDiagramFactory.java
+++ b/src/net/sourceforge/plantuml/statediagram/StateDiagramFactory.java
@@ -47,9 +47,9 @@ import net.sourceforge.plantuml.command.Command;
 import net.sourceforge.plantuml.command.CommandFootboxIgnored;
 import net.sourceforge.plantuml.command.CommandRankDir;
 import net.sourceforge.plantuml.command.UmlDiagramFactory;
-import net.sourceforge.plantuml.command.note.FactoryNoteCommand;
-import net.sourceforge.plantuml.command.note.FactoryNoteOnEntityCommand;
-import net.sourceforge.plantuml.command.note.FactoryNoteOnLinkCommand;
+import net.sourceforge.plantuml.command.note.CommandFactoryNote;
+import net.sourceforge.plantuml.command.note.CommandFactoryNoteOnEntity;
+import net.sourceforge.plantuml.command.note.CommandFactoryNoteOnLink;
 import net.sourceforge.plantuml.command.regex.RegexLeaf;
 import net.sourceforge.plantuml.command.regex.RegexOr;
 import net.sourceforge.plantuml.statediagram.command.CommandAddField;
@@ -85,7 +85,7 @@ public class StateDiagramFactory extends UmlDiagramFactory {
 		cmds.add(new CommandAddField());
 		cmds.add(new CommandConcurrentState());
 
-		final FactoryNoteOnEntityCommand factoryNoteOnEntityCommand = new FactoryNoteOnEntityCommand("state",
+		final CommandFactoryNoteOnEntity factoryNoteOnEntityCommand = new CommandFactoryNoteOnEntity("state",
 				new RegexOr("ENTITY", new RegexLeaf("[\\p{L}0-9_.]+"), //
 						new RegexLeaf("[%g][^%g]+[%g]") //
 				));
@@ -93,12 +93,12 @@ public class StateDiagramFactory extends UmlDiagramFactory {
 		cmds.add(factoryNoteOnEntityCommand.createMultiLine(false));
 
 		cmds.add(factoryNoteOnEntityCommand.createSingleLine());
-		final FactoryNoteOnLinkCommand factoryNoteOnLinkCommand = new FactoryNoteOnLinkCommand();
+		final CommandFactoryNoteOnLink factoryNoteOnLinkCommand = new CommandFactoryNoteOnLink();
 		cmds.add(factoryNoteOnLinkCommand.createSingleLine());
 		cmds.add(factoryNoteOnLinkCommand.createMultiLine(false));
 		cmds.add(new CommandUrl());
 
-		final FactoryNoteCommand factoryNoteCommand = new FactoryNoteCommand();
+		final CommandFactoryNote factoryNoteCommand = new CommandFactoryNote();
 		cmds.add(factoryNoteCommand.createSingleLine());
 		cmds.add(factoryNoteCommand.createMultiLine(false));
 
diff --git a/src/net/sourceforge/plantuml/svek/Bibliotekon.java b/src/net/sourceforge/plantuml/svek/Bibliotekon.java
index 0ddcc33c0..7122bf6ca 100644
--- a/src/net/sourceforge/plantuml/svek/Bibliotekon.java
+++ b/src/net/sourceforge/plantuml/svek/Bibliotekon.java
@@ -48,24 +48,27 @@ import net.sourceforge.plantuml.cucadiagram.IEntity;
 import net.sourceforge.plantuml.cucadiagram.IGroup;
 import net.sourceforge.plantuml.cucadiagram.ILeaf;
 import net.sourceforge.plantuml.cucadiagram.Link;
+import net.sourceforge.plantuml.graphic.StringBounder;
 
 public class Bibliotekon {
 
 	private final List<Cluster> allCluster = new ArrayList<Cluster>();
 
-	private final Map<ILeaf, Shape> shapeMap = new LinkedHashMap<ILeaf, Shape>();;
+	private final Map<ILeaf, Node> nodeMap = new LinkedHashMap<ILeaf, Node>();;
 
 	private final List<Line> lines0 = new ArrayList<Line>();
 	private final List<Line> lines1 = new ArrayList<Line>();
 	private final List<Line> allLines = new ArrayList<Line>();
 
-	public void putShape(ILeaf ent, Shape shape) {
-		shapeMap.put(ent, shape);
+	public Node createNode(ILeaf ent, IEntityImage image, ColorSequence colorSequence, StringBounder stringBounder) {
+		final Node node = new Node(ent, image, colorSequence, stringBounder);
+		nodeMap.put(ent, node);
+		return node;
 	}
 
 	public Cluster getCluster(IGroup ent) {
 		for (Cluster cl : allCluster) {
-			if (cl.getGroup() == ent) {
+			if (cl.getGroups().contains(ent)) {
 				return cl;
 			}
 		}
@@ -105,12 +108,12 @@ public class Bibliotekon {
 		allCluster.add(current);
 	}
 
-	public Shape getShape(IEntity ent) {
-		return shapeMap.get(ent);
+	public Node getNode(IEntity ent) {
+		return nodeMap.get(ent);
 	}
 
-	public String getShapeUid(ILeaf ent) {
-		final Shape result = getShape(ent);
+	public String getNodeUid(ILeaf ent) {
+		final Node result = getNode(ent);
 		if (result != null) {
 			String uid = result.getUid();
 			if (result.isShielded()) {
@@ -120,9 +123,9 @@ public class Bibliotekon {
 		}
 		assert result == null;
 		if (ent.isGroup()) {
-			for (IEntity i : shapeMap.keySet()) {
+			for (IEntity i : nodeMap.keySet()) {
 				if (ent.getCodeGetName().equals(i.getCodeGetName())) {
-					return getShape(i).getUid();
+					return getNode(i).getUid();
 				}
 			}
 			return Cluster.getSpecialPointId(ent);
@@ -132,8 +135,8 @@ public class Bibliotekon {
 
 	public String getWarningOrError(int warningOrError) {
 		final StringBuilder sb = new StringBuilder();
-		for (Map.Entry<ILeaf, Shape> ent : shapeMap.entrySet()) {
-			final Shape sh = ent.getValue();
+		for (Map.Entry<ILeaf, Node> ent : nodeMap.entrySet()) {
+			final Node sh = ent.getValue();
 			final double maxX = sh.getMinX() + sh.getWidth();
 			if (maxX > warningOrError) {
 				final IEntity entity = ent.getKey();
@@ -147,8 +150,8 @@ public class Bibliotekon {
 
 	public Map<String, Double> getMaxX() {
 		final Map<String, Double> result = new HashMap<String, Double>();
-		for (Map.Entry<ILeaf, Shape> ent : shapeMap.entrySet()) {
-			final Shape sh = ent.getValue();
+		for (Map.Entry<ILeaf, Node> ent : nodeMap.entrySet()) {
+			final Node sh = ent.getValue();
 			final double maxX = sh.getMinX() + sh.getWidth();
 			final IEntity entity = ent.getKey();
 			result.put(entity.getCodeGetName(), maxX);
@@ -172,8 +175,8 @@ public class Bibliotekon {
 		return Collections.unmodifiableList(allCluster);
 	}
 
-	public Collection<Shape> allShapes() {
-		return Collections.unmodifiableCollection(shapeMap.values());
+	public Collection<Node> allNodes() {
+		return Collections.unmodifiableCollection(nodeMap.values());
 	}
 
 	public List<Line> getAllLineConnectedTo(IEntity leaf) {
@@ -205,9 +208,9 @@ public class Bibliotekon {
 		return null;
 	}
 
-	public ILeaf getLeaf(Shape shape) {
-		for (Map.Entry<ILeaf, Shape> ent : shapeMap.entrySet()) {
-			if (ent.getValue() == shape) {
+	public ILeaf getLeaf(Node node) {
+		for (Map.Entry<ILeaf, Node> ent : nodeMap.entrySet()) {
+			if (ent.getValue() == node) {
 				return ent.getKey();
 			}
 		}
diff --git a/src/net/sourceforge/plantuml/svek/Cluster.java b/src/net/sourceforge/plantuml/svek/Cluster.java
index bec6e4ec4..ab5681e45 100644
--- a/src/net/sourceforge/plantuml/svek/Cluster.java
+++ b/src/net/sourceforge/plantuml/svek/Cluster.java
@@ -96,9 +96,9 @@ import net.sourceforge.plantuml.utils.UniqueSequence;
 
 public class Cluster implements Moveable {
 
-	private final Cluster parent;
+	private final Cluster parentCluster;
 	private final IGroup group;
-	private final List<Shape> shapes = new ArrayList<Shape>();
+	private final List<Node> nodes = new ArrayList<Node>();
 	private final List<Cluster> children = new ArrayList<Cluster>();
 	private final int color;
 	private final int colorTitle;
@@ -128,7 +128,7 @@ public class Cluster implements Moveable {
 	}
 
 	private boolean hasEntryOrExitPoint() {
-		for (Shape sh : shapes) {
+		for (Node sh : nodes) {
 			if (sh.getEntityPosition() != EntityPosition.NORMAL) {
 				return true;
 			}
@@ -137,16 +137,16 @@ public class Cluster implements Moveable {
 	}
 
 	public Cluster(ColorSequence colorSequence, ISkinParam skinParam, IGroup root) {
-		this(null, root, colorSequence, skinParam);
+		this(null, colorSequence, skinParam, root);
 	}
 
 	private ColorParam border;
 
-	private Cluster(Cluster parent, IGroup group, ColorSequence colorSequence, ISkinParam skinParam) {
+	private Cluster(Cluster parentCluster, ColorSequence colorSequence, ISkinParam skinParam, IGroup group) {
 		if (group == null) {
 			throw new IllegalStateException();
 		}
-		this.parent = parent;
+		this.parentCluster = parentCluster;
 		this.group = group;
 		if (group.getUSymbol() != null) {
 			border = group.getUSymbol().getColorParamBorder();
@@ -161,46 +161,46 @@ public class Cluster implements Moveable {
 		return super.toString() + " " + group;
 	}
 
-	public final Cluster getParent() {
-		return parent;
+	public final Cluster getParentCluster() {
+		return parentCluster;
 	}
 
-	public void addShape(Shape sh) {
-		if (sh == null) {
+	public void addNode(Node node) {
+		if (node == null) {
 			throw new IllegalArgumentException();
 		}
-		this.shapes.add(sh);
-		sh.setCluster(this);
+		this.nodes.add(node);
+		node.setCluster(this);
 	}
 
-	public final List<Shape> getShapes() {
-		return Collections.unmodifiableList(shapes);
+	public final List<Node> getNodes() {
+		return Collections.unmodifiableList(nodes);
 	}
 
-	private List<Shape> getShapesOrderedTop(Collection<Line> lines) {
-		final List<Shape> firsts = new ArrayList<Shape>();
+	private List<Node> getNodesOrderedTop(Collection<Line> lines) {
+		final List<Node> firsts = new ArrayList<Node>();
 		final Set<String> tops = new HashSet<String>();
-		final Map<String, Shape> shs = new HashMap<String, Shape>();
+		final Map<String, Node> shs = new HashMap<String, Node>();
 
-		for (final Iterator<Shape> it = shapes.iterator(); it.hasNext();) {
-			final Shape sh = it.next();
-			shs.put(sh.getUid(), sh);
-			if (sh.isTop() && sh.getEntityPosition() == EntityPosition.NORMAL) {
-				firsts.add(sh);
-				tops.add(sh.getUid());
+		for (final Iterator<Node> it = nodes.iterator(); it.hasNext();) {
+			final Node node = it.next();
+			shs.put(node.getUid(), node);
+			if (node.isTop() && node.getEntityPosition() == EntityPosition.NORMAL) {
+				firsts.add(node);
+				tops.add(node.getUid());
 			}
 		}
 
 		for (Line l : lines) {
 			if (tops.contains(l.getStartUidPrefix())) {
-				final Shape sh = shs.get(l.getEndUidPrefix());
+				final Node sh = shs.get(l.getEndUidPrefix());
 				if (sh != null && sh.getEntityPosition() == EntityPosition.NORMAL) {
 					firsts.add(0, sh);
 				}
 			}
 
 			if (l.isInverted()) {
-				final Shape sh = shs.get(l.getStartUidPrefix());
+				final Node sh = shs.get(l.getStartUidPrefix());
 				if (sh != null && sh.getEntityPosition() == EntityPosition.NORMAL) {
 					firsts.add(0, sh);
 				}
@@ -210,11 +210,11 @@ public class Cluster implements Moveable {
 		return firsts;
 	}
 
-	private List<Shape> getShapesEntryExit(EnumSet<EntityPosition> positions) {
-		final List<Shape> result = new ArrayList<Shape>();
+	private List<Node> getNodesEntryExit(EnumSet<EntityPosition> positions) {
+		final List<Node> result = new ArrayList<Node>();
 
-		for (final Iterator<Shape> it = shapes.iterator(); it.hasNext();) {
-			final Shape sh = it.next();
+		for (final Iterator<Node> it = nodes.iterator(); it.hasNext();) {
+			final Node sh = it.next();
 			if (positions.contains(sh.getEntityPosition())) {
 				result.add(sh);
 			}
@@ -222,13 +222,13 @@ public class Cluster implements Moveable {
 		return result;
 	}
 
-	private List<Shape> getShapesOrderedWithoutTop(Collection<Line> lines) {
-		final List<Shape> all = new ArrayList<Shape>(shapes);
+	private List<Node> getNodesOrderedWithoutTop(Collection<Line> lines) {
+		final List<Node> all = new ArrayList<Node>(nodes);
 		final Set<String> tops = new HashSet<String>();
-		final Map<String, Shape> shs = new HashMap<String, Shape>();
+		final Map<String, Node> shs = new HashMap<String, Node>();
 
-		for (final Iterator<Shape> it = all.iterator(); it.hasNext();) {
-			final Shape sh = it.next();
+		for (final Iterator<Node> it = all.iterator(); it.hasNext();) {
+			final Node sh = it.next();
 			if (sh.getEntityPosition() != EntityPosition.NORMAL) {
 				it.remove();
 				continue;
@@ -242,14 +242,14 @@ public class Cluster implements Moveable {
 
 		for (Line l : lines) {
 			if (tops.contains(l.getStartUidPrefix())) {
-				final Shape sh = shs.get(l.getEndUidPrefix());
+				final Node sh = shs.get(l.getEndUidPrefix());
 				if (sh != null) {
 					all.remove(sh);
 				}
 			}
 
 			if (l.isInverted()) {
-				final Shape sh = shs.get(l.getStartUidPrefix());
+				final Node sh = shs.get(l.getStartUidPrefix());
 				if (sh != null) {
 					all.remove(sh);
 				}
@@ -263,9 +263,9 @@ public class Cluster implements Moveable {
 		return Collections.unmodifiableList(children);
 	}
 
-	public Cluster createChild(IGroup g, int titleAndAttributeWidth, int titleAndAttributeHeight, TextBlock title,
-			TextBlock stereo, ColorSequence colorSequence, ISkinParam skinParam) {
-		final Cluster child = new Cluster(this, g, colorSequence, skinParam);
+	public Cluster createChild(int titleAndAttributeWidth, int titleAndAttributeHeight, TextBlock title,
+			TextBlock stereo, ColorSequence colorSequence, ISkinParam skinParam, IGroup g) {
+		final Cluster child = new Cluster(this, colorSequence, skinParam, g);
 		child.titleAndAttributeWidth = titleAndAttributeWidth;
 		child.titleAndAttributeHeight = titleAndAttributeHeight;
 		child.ztitle = title;
@@ -274,8 +274,8 @@ public class Cluster implements Moveable {
 		return child;
 	}
 
-	public final IGroup getGroup() {
-		return group;
+	public final Set<IGroup> getGroups() {
+		return Collections.singleton(group);
 	}
 
 	public final int getTitleAndAttributeWidth() {
@@ -385,8 +385,8 @@ public class Cluster implements Moveable {
 			HtmlColor backColor = getBackColor(umlDiagramType);
 			backColor = getBackColor(backColor, skinParam2, group.getStereotype());
 			if (ztitle != null || zstereo != null) {
-				final double roundCorner = group.getUSymbol() == null ? 0 : group.getUSymbol().getSkinParameter()
-						.getRoundCorner(skinParam, stereotype);
+				final double roundCorner = group.getUSymbol() == null ? 0
+						: group.getUSymbol().getSkinParameter().getRoundCorner(skinParam, stereotype);
 
 				final UStroke stroke2 = getStrokeInternal(skinParam2);
 				final ClusterDecoration decoration = new ClusterDecoration(packageStyle, group.getUSymbol(), ztitle,
@@ -427,7 +427,7 @@ public class Cluster implements Moveable {
 	public void manageEntryExitPoint(StringBounder stringBounder) {
 		final Collection<ClusterPosition> insides = new ArrayList<ClusterPosition>();
 		final List<Point2D> points = new ArrayList<Point2D>();
-		for (Shape sh : shapes) {
+		for (Node sh : nodes) {
 			if (sh.getEntityPosition() == EntityPosition.NORMAL) {
 				insides.add(sh.getClusterPosition());
 			} else {
@@ -485,8 +485,9 @@ public class Cluster implements Moveable {
 		final HtmlColor background = getColor(skinParam2, ColorParam.background, null);
 		final TextBlockWidth attribute = getTextBlockAttribute(skinParam2);
 		final double attributeHeight = attribute.calculateDimension(ug.getStringBounder()).getHeight();
-		final RoundedContainer r = new RoundedContainer(total, suppY, attributeHeight
-				+ (attributeHeight > 0 ? IEntityImage.MARGIN : 0), borderColor, stateBack, background, stroke);
+		final RoundedContainer r = new RoundedContainer(total, suppY,
+				attributeHeight + (attributeHeight > 0 ? IEntityImage.MARGIN : 0), borderColor, stateBack, background,
+				stroke);
 		r.drawU(ug.apply(new UTranslate(minX, minY)), skinParam2.shadowing(group.getStereotype()));
 
 		if (ztitle != null) {
@@ -536,12 +537,12 @@ public class Cluster implements Moveable {
 	}
 
 	public void printCluster1(StringBuilder sb, Collection<Line> lines, StringBounder stringBounder) {
-		for (Shape sh : getShapesOrderedTop(lines)) {
-			sh.appendShape(sb, stringBounder);
+		for (Node node : getNodesOrderedTop(lines)) {
+			node.appendShape(sb, stringBounder);
 		}
 	}
 
-	private List<IShapePseudo> addProtection(List<Shape> entries, double width) {
+	private List<IShapePseudo> addProtection(List<Node> entries, double width) {
 		final List<IShapePseudo> result = new ArrayList<IShapePseudo>();
 		result.add(entries.get(0));
 		for (int i = 1; i < entries.size(); i++) {
@@ -551,10 +552,10 @@ public class Cluster implements Moveable {
 		return result;
 	}
 
-	private double getMaxWidthFromLabelForEntryExit(List<Shape> entries, StringBounder stringBounder) {
+	private double getMaxWidthFromLabelForEntryExit(List<Node> entries, StringBounder stringBounder) {
 		double result = -Double.MAX_VALUE;
-		for (Shape shape : entries) {
-			final double w = getMaxWidthFromLabelForEntryExit(shape, stringBounder);
+		for (Node node : entries) {
+			final double w = getMaxWidthFromLabelForEntryExit(node, stringBounder);
 			if (w > result) {
 				result = w;
 			}
@@ -562,19 +563,19 @@ public class Cluster implements Moveable {
 		return result;
 	}
 
-	private double getMaxWidthFromLabelForEntryExit(Shape shape, StringBounder stringBounder) {
-		return shape.getMaxWidthFromLabelForEntryExit(stringBounder);
+	private double getMaxWidthFromLabelForEntryExit(Node node, StringBounder stringBounder) {
+		return node.getMaxWidthFromLabelForEntryExit(stringBounder);
 	}
 
 	public void printClusterEntryExit(StringBuilder sb, StringBounder stringBounder) {
-		final List<Shape> shapesEntryExitList = getShapesEntryExit(EntityPosition.getInputs());
-		final double maxWith = getMaxWidthFromLabelForEntryExit(shapesEntryExitList, stringBounder);
+		final List<Node> nodesEntryExitList = getNodesEntryExit(EntityPosition.getInputs());
+		final double maxWith = getMaxWidthFromLabelForEntryExit(nodesEntryExitList, stringBounder);
 		final double naturalSpace = 70;
 		final List<? extends IShapePseudo> entries;
 		if (maxWith > naturalSpace) {
-			entries = addProtection(shapesEntryExitList, maxWith - naturalSpace);
+			entries = addProtection(nodesEntryExitList, maxWith - naturalSpace);
 		} else {
-			entries = shapesEntryExitList;
+			entries = nodesEntryExitList;
 		}
 		if (entries.size() > 0) {
 			sb.append("{rank=source;");
@@ -586,26 +587,26 @@ public class Cluster implements Moveable {
 				sh.appendShape(sb, stringBounder);
 			}
 		}
-		final List<Shape> exits = getShapesEntryExit(EntityPosition.getOutputs());
+		final List<Node> exits = getNodesEntryExit(EntityPosition.getOutputs());
 		if (exits.size() > 0) {
 			sb.append("{rank=sink;");
-			for (Shape sh : exits) {
+			for (Node sh : exits) {
 				sb.append(sh.getUid() + ";");
 			}
 			sb.append("}");
-			for (Shape sh : exits) {
+			for (Node sh : exits) {
 				sh.appendShape(sb, stringBounder);
 			}
 		}
 	}
 
-	public boolean printCluster2(StringBuilder sb, Collection<Line> lines, StringBounder stringBounder,
-			DotMode dotMode, GraphvizVersion graphvizVersion, UmlDiagramType type) {
+	public boolean printCluster2(StringBuilder sb, Collection<Line> lines, StringBounder stringBounder, DotMode dotMode,
+			GraphvizVersion graphvizVersion, UmlDiagramType type) {
 		// Log.println("Cluster::printCluster " + this);
 
 		boolean added = false;
-		for (Shape sh : getShapesOrderedWithoutTop(lines)) {
-			sh.appendShape(sb, stringBounder);
+		for (Node node : getNodesOrderedWithoutTop(lines)) {
+			node.appendShape(sb, stringBounder);
 			added = true;
 		}
 
@@ -647,7 +648,7 @@ public class Cluster implements Moveable {
 	}
 
 	public void fillRankMin(Set<String> rankMin) {
-		for (Shape sh : getShapes()) {
+		for (Node sh : getNodes()) {
 			if (sh.isTop()) {
 				rankMin.add(sh.getUid());
 			}
@@ -659,8 +660,8 @@ public class Cluster implements Moveable {
 	}
 
 	private boolean isInCluster(String uid) {
-		for (Shape sh : shapes) {
-			if (sh.getUid().equals(uid)) {
+		for (Node node : nodes) {
+			if (node.getUid().equals(uid)) {
 				return true;
 			}
 		}
@@ -865,16 +866,16 @@ public class Cluster implements Moveable {
 		}
 		final Stereotype stereo = group.getStereotype();
 		final USymbol sym = group.getUSymbol() == null ? USymbol.PACKAGE : group.getUSymbol();
-		final ColorParam backparam = umlDiagramType == UmlDiagramType.ACTIVITY ? ColorParam.partitionBackground : sym
-				.getColorParamBack();
+		final ColorParam backparam = umlDiagramType == UmlDiagramType.ACTIVITY ? ColorParam.partitionBackground
+				: sym.getColorParamBack();
 		final HtmlColor c1 = skinParam.getHtmlColor(backparam, stereo, false);
 		if (c1 != null) {
 			return c1;
 		}
-		if (parent == null) {
+		if (parentCluster == null) {
 			return null;
 		}
-		return parent.getBackColor(umlDiagramType);
+		return parentCluster.getBackColor(umlDiagramType);
 	}
 
 	public boolean isClusterOf(IEntity ent) {
diff --git a/src/net/sourceforge/plantuml/svek/CucaDiagramFileMakerSvek.java b/src/net/sourceforge/plantuml/svek/CucaDiagramFileMakerSvek.java
index f649e5bfd..276944096 100644
--- a/src/net/sourceforge/plantuml/svek/CucaDiagramFileMakerSvek.java
+++ b/src/net/sourceforge/plantuml/svek/CucaDiagramFileMakerSvek.java
@@ -78,13 +78,15 @@ public final class CucaDiagramFileMakerSvek implements CucaDiagramFileMaker {
 	private GeneralImageBuilder createDotDataImageBuilder(DotMode dotMode, StringBounder stringBounder) {
 		final DotData dotData = new DotData(diagram.getEntityFactory().getRootGroup(), getOrderedLinks(),
 				diagram.getLeafsvalues(), diagram.getUmlDiagramType(), diagram.getSkinParam(), diagram, diagram,
-				diagram.getColorMapper(), diagram.getEntityFactory(), diagram.isHideEmptyDescriptionForState(),
-				dotMode, diagram.getNamespaceSeparator(), diagram.getPragma());
-		return new GeneralImageBuilder(dotData, diagram.getEntityFactory(), diagram.getSource(), diagram.getPragma(),
-				stringBounder);
+				diagram.getColorMapper(), diagram.getEntityFactory(), diagram.isHideEmptyDescriptionForState(), dotMode,
+				diagram.getNamespaceSeparator(), diagram.getPragma());
+		final boolean intricated = diagram.mergeIntricated();
+		return new GeneralImageBuilder(intricated, dotData, diagram.getEntityFactory(), diagram.getSource(),
+				diagram.getPragma(), stringBounder);
 
 	}
 
+
 	private ImageData createFileInternal(OutputStream os, List<String> dotStrings, FileFormatOption fileFormatOption)
 			throws IOException, InterruptedException {
 		if (diagram.getUmlDiagramType() == UmlDiagramType.ACTIVITY) {
diff --git a/src/net/sourceforge/plantuml/svek/DotStringFactory.java b/src/net/sourceforge/plantuml/svek/DotStringFactory.java
index f94c68d24..5d1fad9d4 100644
--- a/src/net/sourceforge/plantuml/svek/DotStringFactory.java
+++ b/src/net/sourceforge/plantuml/svek/DotStringFactory.java
@@ -60,6 +60,7 @@ import net.sourceforge.plantuml.cucadiagram.dot.GraphvizUtils;
 import net.sourceforge.plantuml.cucadiagram.dot.GraphvizVersion;
 import net.sourceforge.plantuml.cucadiagram.dot.GraphvizVersions;
 import net.sourceforge.plantuml.cucadiagram.dot.ProcessState;
+import net.sourceforge.plantuml.cucadiagram.entity.EntityFactory;
 import net.sourceforge.plantuml.graphic.StringBounder;
 import net.sourceforge.plantuml.graphic.TextBlock;
 import net.sourceforge.plantuml.posimo.Moveable;
@@ -104,8 +105,8 @@ public class DotStringFactory implements Moveable {
 		this.current = root;
 	}
 
-	public void addShape(Shape shape) {
-		current.addShape(shape);
+	public void addNode(Node node) {
+		current.addNode(node);
 	}
 
 	private void printMinRanking(StringBuilder sb) {
@@ -343,7 +344,8 @@ public class DotStringFactory implements Moveable {
 		return graphviz.getDotExe();
 	}
 
-	public ClusterPosition solve(final String svg) throws IOException, InterruptedException {
+	public ClusterPosition solve(boolean mergeIntricated, EntityFactory entityFactory, final String svg)
+			throws IOException, InterruptedException {
 		if (svg.length() == 0) {
 			throw new EmptySvgException();
 		}
@@ -360,18 +362,18 @@ public class DotStringFactory implements Moveable {
 
 		final Point2DFunction move = new YDelta(fullHeight);
 		final SvgResult svgResult = new SvgResult(svg, move);
-		for (Shape sh : bibliotekon.allShapes()) {
-			int idx = svg.indexOf("<title>" + sh.getUid() + "</title>");
-			if (sh.getType() == ShapeType.RECTANGLE || sh.getType() == ShapeType.RECTANGLE_HTML_FOR_PORTS
-					|| sh.getType() == ShapeType.RECTANGLE_WITH_CIRCLE_INSIDE || sh.getType() == ShapeType.FOLDER
-					|| sh.getType() == ShapeType.DIAMOND) {
+		for (Node node : bibliotekon.allNodes()) {
+			int idx = svg.indexOf("<title>" + node.getUid() + "</title>");
+			if (node.getType() == ShapeType.RECTANGLE || node.getType() == ShapeType.RECTANGLE_HTML_FOR_PORTS
+					|| node.getType() == ShapeType.RECTANGLE_WITH_CIRCLE_INSIDE || node.getType() == ShapeType.FOLDER
+					|| node.getType() == ShapeType.DIAMOND) {
 				final List<Point2D.Double> points = svgResult.substring(idx).extractList(SvgResult.POINTS_EQUALS);
 				final double minY = SvekUtils.getMinY(points);
-				final double overscanX = sh.getOverscanX(stringBounder);
+				final double overscanX = node.getOverscanX(stringBounder);
 				final double minX = SvekUtils.getMinX(points);
 				corner1.manage(minX - overscanX, minY);
-				sh.moveSvek(minX, minY);
-			} else if (sh.getType() == ShapeType.ROUND_RECTANGLE) {
+				node.moveSvek(minX, minY);
+			} else if (node.getType() == ShapeType.ROUND_RECTANGLE) {
 				final int idx2 = svg.indexOf("d=\"", idx + 1);
 				idx = svg.indexOf("points=\"", idx + 1);
 				final List<Point2D.Double> points;
@@ -388,29 +390,35 @@ public class DotStringFactory implements Moveable {
 				final double minX = SvekUtils.getMinX(points);
 				final double minY = SvekUtils.getMinY(points);
 				corner1.manage(minX, minY);
-				sh.moveSvek(minX, minY);
-			} else if (sh.getType() == ShapeType.OCTAGON) {
+				node.moveSvek(minX, minY);
+			} else if (node.getType() == ShapeType.OCTAGON) {
 				idx = svg.indexOf("points=\"", idx + 1);
 				final int starting = idx;
 				final List<Point2D.Double> points = svgResult.substring(starting).extractList(SvgResult.POINTS_EQUALS);
 				final double minX = SvekUtils.getMinX(points);
 				final double minY = SvekUtils.getMinY(points);
 				corner1.manage(minX, minY);
-				sh.moveSvek(minX, minY);
-				sh.setOctagon(minX, minY, points);
-			} else if (sh.getType() == ShapeType.CIRCLE || sh.getType() == ShapeType.CIRCLE_IN_RECT
-					|| sh.getType() == ShapeType.OVAL) {
+				node.moveSvek(minX, minY);
+				node.setOctagon(minX, minY, points);
+			} else if (node.getType() == ShapeType.CIRCLE || node.getType() == ShapeType.CIRCLE_IN_RECT
+					|| node.getType() == ShapeType.OVAL) {
 				final double cx = SvekUtils.getValue(svg, idx, "cx");
 				final double cy = SvekUtils.getValue(svg, idx, "cy") + fullHeight;
 				final double rx = SvekUtils.getValue(svg, idx, "rx");
 				final double ry = SvekUtils.getValue(svg, idx, "ry");
-				sh.moveSvek(cx - rx, cy - ry);
+				node.moveSvek(cx - rx, cy - ry);
 			} else {
-				throw new IllegalStateException(sh.getType().toString() + " " + sh.getUid());
+				throw new IllegalStateException(node.getType().toString() + " " + node.getUid());
 			}
 		}
 
 		for (Cluster cluster : bibliotekon.allCluster()) {
+			if (mergeIntricated) {
+				final IGroup group = cluster.getGroups().iterator().next();
+				if (entityFactory.isIntricated(group) != null) {
+					continue;
+				}
+			}
 			int idx = getClusterIndex(svg, cluster.getColor());
 			final int starting = idx;
 			final List<Point2D.Double> points = svgResult.substring(starting).extractList(SvgResult.POINTS_EQUALS);
@@ -438,7 +446,7 @@ public class DotStringFactory implements Moveable {
 		}
 
 		for (Line line : bibliotekon.allLines()) {
-			line.manageCollision(bibliotekon.allShapes());
+			line.manageCollision(bibliotekon.allNodes());
 		}
 		corner1.manage(0, 0);
 		return new ClusterPosition(corner1.getMinX(), corner1.getMinY(), fullWidth, fullHeight);
@@ -459,22 +467,22 @@ public class DotStringFactory implements Moveable {
 		return idx;
 	}
 
-	public void openCluster(IGroup g, int titleAndAttributeWidth, int titleAndAttributeHeight, TextBlock title,
-			TextBlock stereo) {
-		this.current = current.createChild(g, titleAndAttributeWidth, titleAndAttributeHeight, title, stereo,
-				colorSequence, skinParam);
+	public void openCluster(int titleAndAttributeWidth, int titleAndAttributeHeight, TextBlock title, TextBlock stereo,
+			IGroup g) {
+		this.current = current.createChild(titleAndAttributeWidth, titleAndAttributeHeight, title, stereo,
+				colorSequence, skinParam, g);
 		bibliotekon.addCluster(this.current);
 	}
 
 	public void closeCluster() {
-		if (current.getParent() == null) {
+		if (current.getParentCluster() == null) {
 			throw new IllegalStateException();
 		}
-		this.current = current.getParent();
+		this.current = current.getParentCluster();
 	}
 
 	public void moveSvek(double deltaX, double deltaY) {
-		for (Shape sh : bibliotekon.allShapes()) {
+		for (Node sh : bibliotekon.allNodes()) {
 			sh.moveSvek(deltaX, deltaY);
 		}
 		for (Line line : bibliotekon.allLines()) {
diff --git a/src/net/sourceforge/plantuml/svek/GeneralImageBuilder.java b/src/net/sourceforge/plantuml/svek/GeneralImageBuilder.java
index c04b8dffe..a0025f320 100644
--- a/src/net/sourceforge/plantuml/svek/GeneralImageBuilder.java
+++ b/src/net/sourceforge/plantuml/svek/GeneralImageBuilder.java
@@ -84,6 +84,7 @@ import net.sourceforge.plantuml.cucadiagram.dot.ExeState;
 import net.sourceforge.plantuml.cucadiagram.dot.GraphvizVersion;
 import net.sourceforge.plantuml.cucadiagram.dot.Neighborhood;
 import net.sourceforge.plantuml.cucadiagram.entity.EntityFactory;
+import net.sourceforge.plantuml.cucadiagram.entity.EntityImpl;
 import net.sourceforge.plantuml.descdiagram.EntityImageDesignedDomain;
 import net.sourceforge.plantuml.descdiagram.EntityImageDomain;
 import net.sourceforge.plantuml.descdiagram.EntityImageMachine;
@@ -226,8 +227,8 @@ public final class GeneralImageBuilder {
 		if (leaf.getLeafType() == LeafType.EMPTY_PACKAGE) {
 			if (leaf.getUSymbol() != null) {
 				// final HtmlColor black = HtmlColorUtils.BLACK;
-				final HtmlColor black = SkinParamUtils.getColor(skinParam, leaf.getStereotype(), leaf.getUSymbol()
-						.getColorParamBorder());
+				final HtmlColor black = SkinParamUtils.getColor(skinParam, leaf.getStereotype(),
+						leaf.getUSymbol().getColorParamBorder());
 				return new EntityImageDescription(leaf, new SkinParamForecolored(skinParam, black), portionShower,
 						links);
 			}
@@ -274,15 +275,17 @@ public final class GeneralImageBuilder {
 	private Map<String, Double> maxX;
 
 	private final StringBounder stringBounder;
+	private final boolean mergeIntricated;
 
-	public GeneralImageBuilder(DotData dotData, EntityFactory entityFactory, UmlSource source, Pragma pragma,
-			StringBounder stringBounder) {
+	public GeneralImageBuilder(boolean mergeIntricated, DotData dotData, EntityFactory entityFactory, UmlSource source,
+			Pragma pragma, StringBounder stringBounder) {
 		this.dotData = dotData;
 		this.entityFactory = entityFactory;
 		this.source = source;
 		this.pragma = pragma;
 		this.stringBounder = stringBounder;
 		this.strictUmlStyle = dotData.getSkinParam().strictUmlStyle();
+		this.mergeIntricated = mergeIntricated;
 	}
 
 	final public StyleSignature getDefaultStyleDefinitionArrow() {
@@ -362,8 +365,8 @@ public final class GeneralImageBuilder {
 				final ISkinParam skinParam = dotData.getSkinParam();
 				final FontConfiguration labelFont;
 				if (SkinParam.USE_STYLES()) {
-					final Style style = getDefaultStyleDefinitionArrow().getMergedStyle(
-							skinParam.getCurrentStyleBuilder());
+					final Style style = getDefaultStyleDefinitionArrow()
+							.getMergedStyle(skinParam.getCurrentStyleBuilder());
 					labelFont = style.getFontConfiguration(skinParam.getIHtmlColorSet());
 				} else {
 					labelFont = new FontConfiguration(skinParam, FontParam.ARROW, null);
@@ -375,17 +378,17 @@ public final class GeneralImageBuilder {
 				dotStringFactory.getBibliotekon().addLine(line);
 
 				if (isOpalisable(link.getEntity1())) {
-					final Shape shape = dotStringFactory.getBibliotekon().getShape(link.getEntity1());
-					final Shape other = dotStringFactory.getBibliotekon().getShape(link.getEntity2());
+					final Node node = dotStringFactory.getBibliotekon().getNode(link.getEntity1());
+					final Node other = dotStringFactory.getBibliotekon().getNode(link.getEntity2());
 					if (other != null) {
-						((EntityImageNote) shape.getImage()).setOpaleLine(line, shape, other);
+						((EntityImageNote) node.getImage()).setOpaleLine(line, node, other);
 						line.setOpale(true);
 					}
 				} else if (isOpalisable(link.getEntity2())) {
-					final Shape shape = dotStringFactory.getBibliotekon().getShape(link.getEntity2());
-					final Shape other = dotStringFactory.getBibliotekon().getShape(link.getEntity1());
+					final Node node = dotStringFactory.getBibliotekon().getNode(link.getEntity2());
+					final Node other = dotStringFactory.getBibliotekon().getNode(link.getEntity1());
 					if (other != null) {
-						((EntityImageNote) shape.getImage()).setOpaleLine(line, shape, other);
+						((EntityImageNote) node.getImage()).setOpaleLine(line, node, other);
 						line.setOpale(true);
 					}
 				}
@@ -413,7 +416,8 @@ public final class GeneralImageBuilder {
 		}
 		final String graphvizVersion = extractGraphvizVersion(svg);
 		try {
-			final ClusterPosition position = dotStringFactory.solve(svg).delta(10, 10);
+			final ClusterPosition position = dotStringFactory.solve(mergeIntricated, dotData.getEntityFactory(), svg)
+					.delta(10, 10);
 			final double minY = position.getMinY();
 			final double minX = position.getMinX();
 			if (minX > 0 || minY > 0) {
@@ -493,12 +497,8 @@ public final class GeneralImageBuilder {
 			throw new IllegalStateException();
 		}
 		final IEntityImage image = printEntityInternal(dotStringFactory, ent);
-		final Dimension2D dim = image.calculateDimension(stringBounder);
-		final Shape shape = new Shape(image, image.getShapeType(), dim.getWidth(), dim.getHeight(),
-				dotStringFactory.getColorSequence(), ent.isTop(), image.getShield(stringBounder),
-				ent.getEntityPosition());
-		dotStringFactory.addShape(shape);
-		dotStringFactory.getBibliotekon().putShape(ent, shape);
+		final Node node = dotStringFactory.getBibliotekon().createNode(ent, image, dotStringFactory.getColorSequence(), stringBounder);
+		dotStringFactory.addNode(node);
 	}
 
 	private IEntityImage printEntityInternal(DotStringFactory dotStringFactory, ILeaf ent) {
@@ -546,29 +546,15 @@ public final class GeneralImageBuilder {
 	}
 
 	private void printGroups(DotStringFactory dotStringFactory, IGroup parent) {
-		for (IGroup g : dotData.getGroupHierarchy().getChildrenGroups(parent)) {
+		final Collection<IGroup> groups = dotData.getGroupHierarchy().getChildrenGroups(parent);
+		for (IGroup g : groups) {
 			if (g.isRemoved()) {
 				continue;
 			}
 			if (dotData.isEmpty(g) && g.getGroupType() == GroupType.PACKAGE) {
+				final ISkinParam skinParam = dotData.getSkinParam();
 				entityFactory.thisIsGoingToBeALeaf(g.getIdent());
-				final ILeaf folder = entityFactory.createLeaf(g.getIdent(), g.getCode(), g.getDisplay(),
-						LeafType.EMPTY_PACKAGE, g.getParentContainer(), null, dotData.getNamespaceSeparator());
-				final USymbol symbol = g.getUSymbol();
-				folder.setUSymbol(symbol);
-				folder.setStereotype(g.getStereotype());
-				if (g.getUrl99() != null) {
-					folder.addUrl(g.getUrl99());
-				}
-				if (g.getColors(dotData.getSkinParam()).getColor(ColorType.BACK) == null) {
-					final ColorParam param = symbol == null ? ColorParam.packageBackground : symbol.getColorParamBack();
-					final HtmlColor c1 = dotData.getSkinParam().getHtmlColor(param, g.getStereotype(), false);
-					folder.setSpecificColorTOBEREMOVED(ColorType.BACK, c1 == null ? dotData.getSkinParam()
-							.getBackgroundColor() : c1);
-				} else {
-					folder.setSpecificColorTOBEREMOVED(ColorType.BACK,
-							g.getColors(dotData.getSkinParam()).getColor(ColorType.BACK));
-				}
+				final ILeaf folder = entityFactory.createLeafForEmptyGroup(g, skinParam);
 				printEntity(dotStringFactory, folder);
 			} else {
 				printGroup(dotStringFactory, g);
@@ -580,6 +566,13 @@ public final class GeneralImageBuilder {
 		if (g.getGroupType() == GroupType.CONCURRENT_STATE) {
 			return;
 		}
+		if (mergeIntricated) {
+			final IGroup intricated = dotData.getEntityFactory().isIntricated(g);
+			if (intricated != null) {
+				printGroup(dotStringFactory, intricated);
+				return;
+			}
+		}
 		int titleAndAttributeWidth = 0;
 		int titleAndAttributeHeight = 0;
 
@@ -605,10 +598,11 @@ public final class GeneralImageBuilder {
 			final int suppWidthBecauseOfShape = uSymbol == null ? 0 : uSymbol.suppWidthBecauseOfShape();
 
 			titleAndAttributeWidth = (int) Math.max(dimLabel.getWidth(), attributeWidth) + suppWidthBecauseOfShape;
-			titleAndAttributeHeight = (int) (dimLabel.getHeight() + attributeHeight + marginForFields + suppHeightBecauseOfShape);
+			titleAndAttributeHeight = (int) (dimLabel.getHeight() + attributeHeight + marginForFields
+					+ suppHeightBecauseOfShape);
 		}
 
-		dotStringFactory.openCluster(g, titleAndAttributeWidth, titleAndAttributeHeight, title, stereo);
+		dotStringFactory.openCluster(titleAndAttributeWidth, titleAndAttributeHeight, title, stereo, g);
 		this.printEntities(dotStringFactory, g.getLeafsDirect());
 
 		printGroups(dotStringFactory, g);
diff --git a/src/net/sourceforge/plantuml/svek/GroupPngMakerActivity.java b/src/net/sourceforge/plantuml/svek/GroupPngMakerActivity.java
index 3f34446a1..4ac245131 100644
--- a/src/net/sourceforge/plantuml/svek/GroupPngMakerActivity.java
+++ b/src/net/sourceforge/plantuml/svek/GroupPngMakerActivity.java
@@ -39,6 +39,7 @@ import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
+import java.util.Set;
 
 import net.sourceforge.plantuml.ColorParam;
 import net.sourceforge.plantuml.ISkinParam;
@@ -51,6 +52,7 @@ import net.sourceforge.plantuml.cucadiagram.IEntity;
 import net.sourceforge.plantuml.cucadiagram.IGroup;
 import net.sourceforge.plantuml.cucadiagram.Link;
 import net.sourceforge.plantuml.cucadiagram.Stereotype;
+import net.sourceforge.plantuml.cucadiagram.SuperGroup;
 import net.sourceforge.plantuml.cucadiagram.dot.DotData;
 import net.sourceforge.plantuml.graphic.FontConfiguration;
 import net.sourceforge.plantuml.graphic.HtmlColor;
@@ -71,6 +73,18 @@ public final class GroupPngMakerActivity {
 
 	class InnerGroupHierarchy implements GroupHierarchy {
 
+		public Set<SuperGroup> getAllSuperGroups() {
+			throw new UnsupportedOperationException();
+		}
+
+		public IGroup getRootGroup() {
+			throw new UnsupportedOperationException();
+		}
+		
+		public SuperGroup getRootSuperGroup() {
+			throw new UnsupportedOperationException();
+		}
+		
 		public Collection<IGroup> getChildrenGroups(IGroup parent) {
 			if (EntityUtils.groupRoot(parent)) {
 				return diagram.getChildrenGroups(group);
@@ -82,6 +96,8 @@ public final class GroupPngMakerActivity {
 			return diagram.isEmpty(g);
 		}
 
+
+
 	}
 
 	public GroupPngMakerActivity(CucaDiagram diagram, IGroup group, StringBounder stringBounder) {
@@ -118,18 +134,18 @@ public final class GroupPngMakerActivity {
 				skinParam, new InnerGroupHierarchy(), diagram.getColorMapper(), diagram.getEntityFactory(), false,
 				DotMode.NORMAL, diagram.getNamespaceSeparator(), diagram.getPragma());
 
-		final GeneralImageBuilder svek2 = new GeneralImageBuilder(dotData, diagram.getEntityFactory(),
+		final GeneralImageBuilder svek2 = new GeneralImageBuilder(false, dotData, diagram.getEntityFactory(),
 				diagram.getSource(), diagram.getPragma(), stringBounder);
 
 		if (group.getGroupType() == GroupType.INNER_ACTIVITY) {
 			final Stereotype stereo = group.getStereotype();
 			final HtmlColor borderColor = getColor(ColorParam.activityBorder, stereo);
-			final HtmlColor backColor = group.getColors(skinParam).getColor(ColorType.BACK) == null ? getColor(
-					ColorParam.background, stereo) : group.getColors(skinParam).getColor(ColorType.BACK);
+			final HtmlColor backColor = group.getColors(skinParam).getColor(ColorType.BACK) == null
+					? getColor(ColorParam.background, stereo)
+					: group.getColors(skinParam).getColor(ColorType.BACK);
 			final double shadowing;
 			if (SkinParam.USE_STYLES()) {
-				final Style style = getDefaultStyleDefinitionGroup().getMergedStyle(
-						skinParam.getCurrentStyleBuilder());
+				final Style style = getDefaultStyleDefinitionGroup().getMergedStyle(skinParam.getCurrentStyleBuilder());
 				shadowing = style.value(PName.Shadowing).asDouble();
 			} else {
 				shadowing = skinParam.shadowing(group.getStereotype()) ? 4 : 0;
diff --git a/src/net/sourceforge/plantuml/svek/GroupPngMakerState.java b/src/net/sourceforge/plantuml/svek/GroupPngMakerState.java
index 529f3dcd0..5368d1e04 100644
--- a/src/net/sourceforge/plantuml/svek/GroupPngMakerState.java
+++ b/src/net/sourceforge/plantuml/svek/GroupPngMakerState.java
@@ -38,6 +38,7 @@ package net.sourceforge.plantuml.svek;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
+import java.util.Set;
 
 import net.sourceforge.plantuml.ColorParam;
 import net.sourceforge.plantuml.FontParam;
@@ -53,6 +54,7 @@ import net.sourceforge.plantuml.cucadiagram.ILeaf;
 import net.sourceforge.plantuml.cucadiagram.LeafType;
 import net.sourceforge.plantuml.cucadiagram.Link;
 import net.sourceforge.plantuml.cucadiagram.Stereotype;
+import net.sourceforge.plantuml.cucadiagram.SuperGroup;
 import net.sourceforge.plantuml.cucadiagram.dot.DotData;
 import net.sourceforge.plantuml.graphic.FontConfiguration;
 import net.sourceforge.plantuml.graphic.HorizontalAlignment;
@@ -75,6 +77,18 @@ public final class GroupPngMakerState {
 
 	class InnerGroupHierarchy implements GroupHierarchy {
 
+		public Set<SuperGroup> getAllSuperGroups() {
+			throw new UnsupportedOperationException();
+		}
+		
+		public IGroup getRootGroup() {
+			throw new UnsupportedOperationException();
+		}
+
+		public SuperGroup getRootSuperGroup() {
+			throw new UnsupportedOperationException();
+		}
+
 		public Collection<IGroup> getChildrenGroups(IGroup parent) {
 			if (EntityUtils.groupRoot(parent)) {
 				return diagram.getChildrenGroups(group);
@@ -110,9 +124,8 @@ public final class GroupPngMakerState {
 	public IEntityImage getImage() {
 		final Display display = group.getDisplay();
 		final ISkinParam skinParam = diagram.getSkinParam();
-		final TextBlock title = display.create(
-				new FontConfiguration(skinParam, FontParam.STATE, group.getStereotype()), HorizontalAlignment.CENTER,
-				diagram.getSkinParam());
+		final TextBlock title = display.create(new FontConfiguration(skinParam, FontParam.STATE, group.getStereotype()),
+				HorizontalAlignment.CENTER, diagram.getSkinParam());
 
 		if (group.size() == 0 && group.getChildren().size() == 0) {
 			return new EntityImageState(group, diagram.getSkinParam());
@@ -124,7 +137,7 @@ public final class GroupPngMakerState {
 				diagram.isHideEmptyDescriptionForState(), DotMode.NORMAL, diagram.getNamespaceSeparator(),
 				diagram.getPragma());
 
-		final GeneralImageBuilder svek2 = new GeneralImageBuilder(dotData, diagram.getEntityFactory(),
+		final GeneralImageBuilder svek2 = new GeneralImageBuilder(false, dotData, diagram.getEntityFactory(),
 				diagram.getSource(), diagram.getPragma(), stringBounder);
 
 		if (group.getGroupType() == GroupType.CONCURRENT_STATE) {
@@ -141,22 +154,23 @@ public final class GroupPngMakerState {
 			borderColor = getColor(ColorParam.stateBorder, group.getStereotype());
 		}
 		final Stereotype stereo = group.getStereotype();
-		final HtmlColor backColor = group.getColors(skinParam).getColor(ColorType.BACK) == null ? getColor(
-				ColorParam.stateBackground, stereo) : group.getColors(skinParam).getColor(ColorType.BACK);
+		final HtmlColor backColor = group.getColors(skinParam).getColor(ColorType.BACK) == null
+				? getColor(ColorParam.stateBackground, stereo)
+				: group.getColors(skinParam).getColor(ColorType.BACK);
 		final TextBlockWidth attribute = getAttributes(skinParam);
 
 		final Stereotype stereotype = group.getStereotype();
 		final boolean withSymbol = stereotype != null && stereotype.isWithOOSymbol();
 
 		final boolean containsOnlyConcurrentStates = containsOnlyConcurrentStates(dotData);
-		final IEntityImage image = containsOnlyConcurrentStates ? buildImageForConcurrentState(dotData) : svek2
-				.buildImage(null, new String[0]);
+		final IEntityImage image = containsOnlyConcurrentStates ? buildImageForConcurrentState(dotData)
+				: svek2.buildImage(null, new String[0]);
 		UStroke stroke = group.getColors(skinParam).getSpecificLineStroke();
 		if (stroke == null) {
 			stroke = new UStroke(1.5);
 		}
-		return new InnerStateAutonom(image, title, attribute, borderColor, backColor, skinParam.shadowing(group
-				.getStereotype()), group.getUrl99(), withSymbol, stroke);
+		return new InnerStateAutonom(image, title, attribute, borderColor, backColor,
+				skinParam.shadowing(group.getStereotype()), group.getUrl99(), withSymbol, stroke);
 
 	}
 
diff --git a/src/net/sourceforge/plantuml/svek/Line.java b/src/net/sourceforge/plantuml/svek/Line.java
index d4fc07a24..9e321e755 100644
--- a/src/net/sourceforge/plantuml/svek/Line.java
+++ b/src/net/sourceforge/plantuml/svek/Line.java
@@ -37,6 +37,7 @@ package net.sourceforge.plantuml.svek;
 
 import java.awt.geom.Dimension2D;
 import java.awt.geom.Point2D;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 import java.util.Set;
@@ -97,11 +98,13 @@ import net.sourceforge.plantuml.ugraphic.UChangeColor;
 import net.sourceforge.plantuml.ugraphic.UGraphic;
 import net.sourceforge.plantuml.ugraphic.ULine;
 import net.sourceforge.plantuml.ugraphic.UPolygon;
+import net.sourceforge.plantuml.ugraphic.URectangle;
 import net.sourceforge.plantuml.ugraphic.UStroke;
 import net.sourceforge.plantuml.ugraphic.UTranslate;
 
 public class Line implements Moveable, Hideable {
 
+	private static final Dimension2DDouble CONSTRAINT_SPOT = new Dimension2DDouble(10, 10);
 	private final Cluster ltail;
 	private final Cluster lhead;
 	private final Link link;
@@ -142,6 +145,7 @@ public class Line implements Moveable, Hideable {
 	private final boolean useRankSame;
 	private final UStroke defaultThickness;
 	private HtmlColor arrowLollipopColor;
+	private final ISkinParam skinParam;
 
 	// private final UmlDiagramType umlType;
 
@@ -212,7 +216,7 @@ public class Line implements Moveable, Hideable {
 
 	private Cluster getCluster2(Bibliotekon bibliotekon, IEntity entityMutable) {
 		for (Cluster cl : bibliotekon.allCluster()) {
-			if (entityMutable == cl.getGroup()) {
+			if (cl.getGroups().contains(entityMutable)) {
 				return cl;
 			}
 		}
@@ -225,6 +229,7 @@ public class Line implements Moveable, Hideable {
 		if (link == null) {
 			throw new IllegalArgumentException();
 		}
+		this.skinParam = skinParam;
 		// this.umlType = link.getUmlDiagramType();
 		this.useRankSame = skinParam.useRankSame();
 		this.startUid = link.getEntityPort1(bibliotekon);
@@ -341,7 +346,8 @@ public class Line implements Moveable, Hideable {
 		final VisibilityModifier visibilityModifier = link.getVisibilityModifier();
 		if (visibilityModifier != null) {
 			final Rose rose = new Rose();
-			// final HtmlColor back = visibilityModifier.getBackground() == null ? null : rose.getHtmlColor(skinParam,
+			// final HtmlColor back = visibilityModifier.getBackground() == null ? null :
+			// rose.getHtmlColor(skinParam,
 			// visibilityModifier.getBackground());
 			final HtmlColor fore = rose.getHtmlColor(skinParam, visibilityModifier.getForeground());
 			TextBlock visibility = visibilityModifier.getUBlock(skinParam.classAttributeIconSize(), fore, null, false);
@@ -402,17 +408,17 @@ public class Line implements Moveable, Hideable {
 			sb.append(",");
 		}
 		sb.append("color=\"" + StringUtils.getAsHtml(lineColor) + "\"");
-		if (labelText != null) {
+		if (labelText != null || link.getLinkConstraint() != null) {
 			sb.append(",");
 			if (graphvizVersion.useXLabelInsteadOfLabel() || dotMode == DotMode.NO_LEFT_RIGHT_AND_XLABEL) {
 				sb.append("xlabel=<");
 			} else {
 				sb.append("label=<");
 			}
-			appendTable(sb, eventuallyDivideByTwo(labelText.calculateDimension(stringBounder)), noteLabelColor,
-					graphvizVersion);
+			final Dimension2D dimNote = labelText == null ? CONSTRAINT_SPOT
+					: labelText.calculateDimension(stringBounder);
+			appendTable(sb, eventuallyDivideByTwo(dimNote), noteLabelColor, graphvizVersion);
 			sb.append(">");
-			// sb.append(",labelfloat=true");
 		}
 
 		if (startTailText != null) {
@@ -420,14 +426,12 @@ public class Line implements Moveable, Hideable {
 			sb.append("taillabel=<");
 			appendTable(sb, startTailText.calculateDimension(stringBounder), startTailColor, graphvizVersion);
 			sb.append(">");
-			// sb.append(",labelangle=0");
 		}
 		if (endHeadText != null) {
 			sb.append(",");
 			sb.append("headlabel=<");
 			appendTable(sb, endHeadText.calculateDimension(stringBounder), endHeadColor, graphvizVersion);
 			sb.append(">");
-			// sb.append(",labelangle=0");
 		}
 
 		if (link.isInvis()) {
@@ -495,7 +499,7 @@ public class Line implements Moveable, Hideable {
 	}
 
 	private UDrawable getExtremity(LinkDecor decor, PointListIterator pointListIterator, final Point2D center,
-			double angle, Cluster cluster, Shape shapeContact) {
+			double angle, Cluster cluster, Node nodeContact) {
 		final ExtremityFactory extremityFactory = decor.getExtremityFactory(backgroundColor);
 
 		if (cluster != null) {
@@ -518,8 +522,8 @@ public class Line implements Moveable, Hideable {
 			final Point2D p1 = points.get(1);
 			final Point2D p2 = points.get(2);
 			Side side = null;
-			if (shapeContact != null) {
-				side = shapeContact.getClusterPosition().getClosestSide(p1);
+			if (nodeContact != null) {
+				side = nodeContact.getClusterPosition().getClosestSide(p1);
 			}
 			return extremityFactory.createUDrawable(p0, p1, p2, side);
 		} else if (decor == LinkDecor.NONE) {
@@ -565,9 +569,11 @@ public class Line implements Moveable, Hideable {
 		dotPath = new DotPath(path);
 
 		if (projectionCluster != null) {
-			// System.err.println("Line::solveLine1 projectionCluster=" + projectionCluster.getClusterPosition());
+			// System.err.println("Line::solveLine1 projectionCluster=" +
+			// projectionCluster.getClusterPosition());
 			projectionCluster.manageEntryExitPoint(stringBounder);
-			// System.err.println("Line::solveLine2 projectionCluster=" + projectionCluster.getClusterPosition());
+			// System.err.println("Line::solveLine2 projectionCluster=" +
+			// projectionCluster.getClusterPosition());
 			// if (lhead != null)
 			// System.err.println("Line::solveLine ltail=" + lhead.getClusterPosition());
 			// if (ltail != null)
@@ -580,15 +586,15 @@ public class Line implements Moveable, Hideable {
 
 		final LinkType linkType = link.getType();
 		this.extremity1 = getExtremity(linkType.getDecor2(), pointListIterator, dotPath.getStartPoint(),
-				dotPath.getStartAngle() + Math.PI, ltail, bibliotekon.getShape(link.getEntity1()));
+				dotPath.getStartAngle() + Math.PI, ltail, bibliotekon.getNode(link.getEntity1()));
 		this.extremity2 = getExtremity(linkType.getDecor1(), pointListIterator, dotPath.getEndPoint(),
-				dotPath.getEndAngle(), lhead, bibliotekon.getShape(link.getEntity2()));
+				dotPath.getEndAngle(), lhead, bibliotekon.getNode(link.getEntity2()));
 
 		if (link.getEntity1().getLeafType() == LeafType.LOLLIPOP_HALF) {
-			bibliotekon.getShape(link.getEntity1()).addImpact(dotPath.getStartAngle() + Math.PI);
+			bibliotekon.getNode(link.getEntity1()).addImpact(dotPath.getStartAngle() + Math.PI);
 		}
 		if (link.getEntity2().getLeafType() == LeafType.LOLLIPOP_HALF) {
-			bibliotekon.getShape(link.getEntity2()).addImpact(dotPath.getEndAngle());
+			bibliotekon.getNode(link.getEntity2()).addImpact(dotPath.getEndAngle());
 		}
 
 		if (extremity1 instanceof Extremity && extremity2 instanceof Extremity) {
@@ -603,19 +609,20 @@ public class Line implements Moveable, Hideable {
 				if (dist1start > dist1end && dist2end > dist2start) {
 					pointListIterator = lineSvg.getPointsWithThisColor(lineColor);
 					this.extremity2 = getExtremity(linkType.getDecor1(), pointListIterator, dotPath.getEndPoint(),
-							dotPath.getEndAngle(), lhead, bibliotekon.getShape(link.getEntity2()));
+							dotPath.getEndAngle(), lhead, bibliotekon.getNode(link.getEntity2()));
 					this.extremity1 = getExtremity(linkType.getDecor2(), pointListIterator, dotPath.getStartPoint(),
-							dotPath.getStartAngle() + Math.PI, ltail, bibliotekon.getShape(link.getEntity1()));
+							dotPath.getStartAngle() + Math.PI, ltail, bibliotekon.getNode(link.getEntity1()));
 				}
 			}
 
 		}
 
-		if (this.labelText != null) {
+		if (this.labelText != null || link.getLinkConstraint() != null) {
 			final Point2D pos = getXY(fullSvg, this.noteLabelColor);
 			if (pos != null) {
 				corner1.manage(pos);
-				this.labelXY = TextBlockUtils.asPositionable(labelText, stringBounder, pos);
+				this.labelXY = labelText == null ? TextBlockUtils.asPositionable(CONSTRAINT_SPOT, stringBounder, pos)
+						: TextBlockUtils.asPositionable(labelText, stringBounder, pos);
 			}
 		}
 
@@ -708,8 +715,8 @@ public class Line implements Moveable, Hideable {
 		if (link.getEntity2().isGroup() && link.getEntity2().getUSymbol() instanceof USymbolFolder) {
 			final Cluster endCluster = bibliotekon.getCluster((IGroup) link.getEntity2());
 			if (endCluster != null) {
-				final double deltaFolderH = endCluster
-						.checkFolderPosition(dotPath.getEndPoint(), ug.getStringBounder());
+				final double deltaFolderH = endCluster.checkFolderPosition(dotPath.getEndPoint(),
+						ug.getStringBounder());
 				todraw = new DotPath(dotPath);
 				todraw.moveEndPoint(0, deltaFolderH);
 				// moveEndY = deltaFolderH;
@@ -739,16 +746,17 @@ public class Line implements Moveable, Hideable {
 
 		if (this.labelText != null && this.labelXY != null
 				&& link.getNoteLinkStrategy() != NoteLinkStrategy.HALF_NOT_PRINTED) {
-			this.labelText.drawU(ug.apply(new UTranslate(x + this.labelXY.getPosition().getX(), y
-					+ this.labelXY.getPosition().getY())));
+			this.labelText.drawU(ug.apply(
+					new UTranslate(x + this.labelXY.getPosition().getX(), y + this.labelXY.getPosition().getY())));
 		}
-		if (this.startTailText != null && this.startTailLabelXY != null && this.startTailLabelXY.getPosition() != null) {
-			this.startTailText.drawU(ug.apply(new UTranslate(x + this.startTailLabelXY.getPosition().getX(), y
-					+ this.startTailLabelXY.getPosition().getY())));
+		if (this.startTailText != null && this.startTailLabelXY != null
+				&& this.startTailLabelXY.getPosition() != null) {
+			this.startTailText.drawU(ug.apply(new UTranslate(x + this.startTailLabelXY.getPosition().getX(),
+					y + this.startTailLabelXY.getPosition().getY())));
 		}
 		if (this.endHeadText != null && this.endHeadLabelXY != null && this.endHeadLabelXY.getPosition() != null) {
-			this.endHeadText.drawU(ug.apply(new UTranslate(x + this.endHeadLabelXY.getPosition().getX(), y
-					+ this.endHeadLabelXY.getPosition().getY())));
+			this.endHeadText.drawU(ug.apply(new UTranslate(x + this.endHeadLabelXY.getPosition().getX(),
+					y + this.endHeadLabelXY.getPosition().getY())));
 		}
 
 		if (linkType.getMiddleDecor() != LinkMiddleDecor.NONE) {
@@ -763,6 +771,40 @@ public class Line implements Moveable, Hideable {
 		if (url != null) {
 			ug.closeAction();
 		}
+
+		if (link.getLinkConstraint() != null) {
+			final double xConstraint = x + this.labelXY.getPosition().getX();
+			final double yConstraint = y + this.labelXY.getPosition().getY();
+//			ug.apply(new UTranslate(xConstraint, yConstraint)).draw(new URectangle(10, 10));
+			final List<Point2D> square = getSquare(xConstraint, yConstraint);
+			final Set<Point2D> bez = dotPath.sample();
+			Point2D minPt = null;
+			double minDist = Double.MAX_VALUE;
+			for (Point2D pt : square) {
+				for (Point2D pt2 : bez) {
+					final double distance = pt2.distance(pt);
+					if (minPt == null || distance < minDist) {
+						minPt = pt;
+						minDist = distance;
+					}
+				}
+			}
+			link.getLinkConstraint().setPosition(link, minPt);
+			link.getLinkConstraint().drawMe(ug, skinParam);
+		}
+	}
+
+	private List<Point2D> getSquare(double x, double y) {
+		final List<Point2D> result = new ArrayList<Point2D>();
+		result.add(new Point2D.Double(x, y));
+		result.add(new Point2D.Double(x + 5, y));
+		result.add(new Point2D.Double(x + 10, y));
+		result.add(new Point2D.Double(x, y + 5));
+		result.add(new Point2D.Double(x + 10, y + 5));
+		result.add(new Point2D.Double(x, y + 10));
+		result.add(new Point2D.Double(x + 5, y + 10));
+		result.add(new Point2D.Double(x + 10, y + 10));
+		return result;
 	}
 
 	private String uniq(final Set<String> ids, final String comment) {
@@ -871,11 +913,12 @@ public class Line implements Moveable, Hideable {
 		return strategy.getResult() + getDecorDzeta();
 	}
 
-	public void manageCollision(Collection<Shape> allShapes) {
+	public void manageCollision(Collection<Node> allNodes) {
 
-		for (Shape sh : allShapes) {
+		for (Node sh : allNodes) {
 			final Positionable cl = PositionableUtils.addMargin(sh, 8, 8);
-			if (startTailText != null && startTailLabelXY != null && PositionableUtils.intersect(cl, startTailLabelXY)) {
+			if (startTailText != null && startTailLabelXY != null
+					&& PositionableUtils.intersect(cl, startTailLabelXY)) {
 				startTailLabelXY = PositionableUtils.moveAwayFrom(cl, startTailLabelXY);
 			}
 			if (endHeadText != null && endHeadLabelXY != null && PositionableUtils.intersect(cl, endHeadLabelXY)) {
@@ -903,7 +946,7 @@ public class Line implements Moveable, Hideable {
 
 	}
 
-	private void avoid(Point2D.Double move, Positionable pos, Shape sh) {
+	private void avoid(Point2D.Double move, Positionable pos, Node sh) {
 		final Oscillator oscillator = new Oscillator();
 		final Point2D.Double orig = new Point2D.Double(move.x, move.y);
 		while (cut(pos, sh)) {
@@ -912,7 +955,7 @@ public class Line implements Moveable, Hideable {
 		}
 	}
 
-	private boolean cut(Positionable pos, Shape sh) {
+	private boolean cut(Positionable pos, Node sh) {
 		return BezierUtils.intersect(pos, sh) || tooClose(pos);
 	}
 
diff --git a/src/net/sourceforge/plantuml/svek/Shape.java b/src/net/sourceforge/plantuml/svek/Node.java
similarity index 91%
rename from src/net/sourceforge/plantuml/svek/Shape.java
rename to src/net/sourceforge/plantuml/svek/Node.java
index da602d413..06fc8ec28 100644
--- a/src/net/sourceforge/plantuml/svek/Shape.java
+++ b/src/net/sourceforge/plantuml/svek/Node.java
@@ -44,6 +44,9 @@ import net.sourceforge.plantuml.Dimension2DDouble;
 import net.sourceforge.plantuml.Hideable;
 import net.sourceforge.plantuml.StringUtils;
 import net.sourceforge.plantuml.cucadiagram.EntityPosition;
+import net.sourceforge.plantuml.cucadiagram.IGroup;
+import net.sourceforge.plantuml.cucadiagram.ILeaf;
+import net.sourceforge.plantuml.cucadiagram.entity.EntityImpl;
 import net.sourceforge.plantuml.graphic.StringBounder;
 import net.sourceforge.plantuml.posimo.Positionable;
 import net.sourceforge.plantuml.svek.image.EntityImageDescription;
@@ -52,7 +55,7 @@ import net.sourceforge.plantuml.svek.image.EntityImageStateBorder;
 import net.sourceforge.plantuml.ugraphic.Shadowable;
 import net.sourceforge.plantuml.ugraphic.UPolygon;
 
-public class Shape implements Positionable, IShapePseudo, Hideable {
+public class Node implements Positionable, IShapePseudo, Hideable {
 
 	private final ShapeType type;
 	private final double width;
@@ -89,21 +92,32 @@ public class Shape implements Positionable, IShapePseudo, Hideable {
 		return super.toString() + " " + image + " " + type;
 	}
 
-	public Shape(IEntityImage image, ShapeType type, double width, double height, ColorSequence colorSequence,
-			boolean top, Margins shield, EntityPosition entityPosition) {
-		this.entityPosition = entityPosition;
+	private final ILeaf leaf;
+	private final IGroup group;
+
+	Node(ILeaf ent, IEntityImage image, ColorSequence colorSequence, StringBounder stringBounder) {
+		final Dimension2D dim = image.calculateDimension(stringBounder);
+		this.entityPosition = ent.getEntityPosition();
 		this.image = image;
-		this.top = top;
-		this.type = type;
-		this.width = width;
-		this.height = height;
+		this.top = ent.isTop();
+		this.type = image.getShapeType();
+		this.width = dim.getWidth();
+		this.height = dim.getHeight();
 		this.color = colorSequence.getValue();
 		this.uid = String.format("sh%04d", color);
-		this.shield = shield;
+		this.shield = image.getShield(stringBounder);
 		if (shield.isZero() == false && type != ShapeType.RECTANGLE && type != ShapeType.RECTANGLE_HTML_FOR_PORTS
 				&& type != ShapeType.RECTANGLE_WITH_CIRCLE_INSIDE) {
 			throw new IllegalArgumentException();
 		}
+
+		if (((EntityImpl) ent).getOriginalGroup() == null) {
+			this.group = null;
+			this.leaf = ent;
+		} else {
+			this.group = ((EntityImpl) ent).getOriginalGroup();
+			this.leaf = null;
+		}
 	}
 
 	public final ShapeType getType() {
diff --git a/src/net/sourceforge/plantuml/svek/SvekResult.java b/src/net/sourceforge/plantuml/svek/SvekResult.java
index 7d84de7bb..da89ae854 100644
--- a/src/net/sourceforge/plantuml/svek/SvekResult.java
+++ b/src/net/sourceforge/plantuml/svek/SvekResult.java
@@ -86,11 +86,11 @@ public final class SvekResult extends AbstractTextBlock implements IEntityImage,
 		}
 		color = HtmlColorUtils.noGradient(color);
 
-		for (Shape shape : dotStringFactory.getBibliotekon().allShapes()) {
-			final double minX = shape.getMinX();
-			final double minY = shape.getMinY();
-			final UGraphic ug2 = shape.isHidden() ? ug.apply(UHidden.HIDDEN) : ug;
-			final IEntityImage image = shape.getImage();
+		for (Node node : dotStringFactory.getBibliotekon().allNodes()) {
+			final double minX = node.getMinX();
+			final double minY = node.getMinY();
+			final UGraphic ug2 = node.isHidden() ? ug.apply(UHidden.HIDDEN) : ug;
+			final IEntityImage image = node.getImage();
 			image.drawU(ug2.apply(new UTranslate(minX, minY)));
 			if (image instanceof Untranslated) {
 				((Untranslated) image).drawUntranslated(ug.apply(new UChangeColor(color)), minX, minY);
diff --git a/src/net/sourceforge/plantuml/svek/image/EntityImageActivity.java b/src/net/sourceforge/plantuml/svek/image/EntityImageActivity.java
index 76f29aaa2..3fd49b074 100644
--- a/src/net/sourceforge/plantuml/svek/image/EntityImageActivity.java
+++ b/src/net/sourceforge/plantuml/svek/image/EntityImageActivity.java
@@ -58,7 +58,7 @@ import net.sourceforge.plantuml.style.Style;
 import net.sourceforge.plantuml.style.StyleSignature;
 import net.sourceforge.plantuml.svek.AbstractEntityImage;
 import net.sourceforge.plantuml.svek.Bibliotekon;
-import net.sourceforge.plantuml.svek.Shape;
+import net.sourceforge.plantuml.svek.Node;
 import net.sourceforge.plantuml.svek.ShapeType;
 import net.sourceforge.plantuml.ugraphic.Shadowable;
 import net.sourceforge.plantuml.ugraphic.UChangeBackColor;
@@ -122,8 +122,8 @@ public class EntityImageActivity extends AbstractEntityImage {
 	}
 
 	private UGraphic drawOctagon(UGraphic ug) {
-		final Shape shape = bibliotekon.getShape(getEntity());
-		final Shadowable octagon = shape.getOctagon();
+		final Node node = bibliotekon.getNode(getEntity());
+		final Shadowable octagon = node.getOctagon();
 		octagon.setDeltaShadow(shadowing);
 		ug = applyColors(ug);
 		ug.apply(new UStroke(1.5)).draw(octagon);
diff --git a/src/net/sourceforge/plantuml/svek/image/EntityImageLollipopInterfaceEye1.java b/src/net/sourceforge/plantuml/svek/image/EntityImageLollipopInterfaceEye1.java
index 563b1a929..dfc585561 100644
--- a/src/net/sourceforge/plantuml/svek/image/EntityImageLollipopInterfaceEye1.java
+++ b/src/net/sourceforge/plantuml/svek/image/EntityImageLollipopInterfaceEye1.java
@@ -99,7 +99,7 @@ public class EntityImageLollipopInterfaceEye1 extends AbstractEntityImage {
 		ug.apply(new UStroke(1.5)).apply(new UTranslate(diff, diff)).draw(circle1);
 		ug = ug.apply(new UChangeBackColor(null));
 
-		Point2D pos = bibliotekon.getShape(getEntity()).getPosition();
+		Point2D pos = bibliotekon.getNode(getEntity()).getPosition();
 
 		final List<Line> lines = bibliotekon.getAllLineConnectedTo(getEntity());
 		final UTranslate reverse = new UTranslate(pos).reverse();
diff --git a/src/net/sourceforge/plantuml/svek/image/EntityImageNote.java b/src/net/sourceforge/plantuml/svek/image/EntityImageNote.java
index d0010698c..6ea5297ea 100644
--- a/src/net/sourceforge/plantuml/svek/image/EntityImageNote.java
+++ b/src/net/sourceforge/plantuml/svek/image/EntityImageNote.java
@@ -73,7 +73,7 @@ import net.sourceforge.plantuml.style.Style;
 import net.sourceforge.plantuml.style.StyleSignature;
 import net.sourceforge.plantuml.svek.AbstractEntityImage;
 import net.sourceforge.plantuml.svek.Line;
-import net.sourceforge.plantuml.svek.Shape;
+import net.sourceforge.plantuml.svek.Node;
 import net.sourceforge.plantuml.svek.ShapeType;
 import net.sourceforge.plantuml.ugraphic.UChangeBackColor;
 import net.sourceforge.plantuml.ugraphic.UChangeColor;
@@ -211,7 +211,7 @@ public class EntityImageNote extends AbstractEntityImage implements Stencil {
 		} else {
 			final StringBounder stringBounder = ug.getStringBounder();
 			DotPath path = opaleLine.getDotPath();
-			path.moveSvek(-shape.getMinX(), -shape.getMinY());
+			path.moveSvek(-node.getMinX(), -node.getMinY());
 			Point2D p1 = path.getStartPoint();
 			Point2D p2 = path.getEndPoint();
 			final double textWidth = getTextWidth(stringBounder);
@@ -225,9 +225,9 @@ public class EntityImageNote extends AbstractEntityImage implements Stencil {
 			final Direction strategy = getOpaleStrategy(textWidth, textHeight, p1);
 			final Point2D pp1 = path.getStartPoint();
 			final Point2D pp2 = path.getEndPoint();
-			final Point2D newRefpp2 = move(pp2, shape.getMinX(), shape.getMinY());
-			final Point2D projection = move(other.projection(newRefpp2, stringBounder), -shape.getMinX(),
-					-shape.getMinY());
+			final Point2D newRefpp2 = move(pp2, node.getMinX(), node.getMinY());
+			final Point2D projection = move(other.projection(newRefpp2, stringBounder), -node.getMinX(),
+					-node.getMinY());
 			final Opale opale = new Opale(shadowing, borderColor, noteBackgroundColor, textBlock, true);
 			opale.setRoundCorner(getRoundCorner());
 			opale.setOpale(strategy, pp1, projection);
@@ -296,15 +296,15 @@ public class EntityImageNote extends AbstractEntityImage implements Stencil {
 	}
 
 	private Line opaleLine;
-	private Shape shape;
-	private Shape other;
+	private Node node;
+	private Node other;
 
-	public void setOpaleLine(Line line, Shape shape, Shape other) {
+	public void setOpaleLine(Line line, Node node, Node other) {
 		if (other == null) {
 			throw new IllegalArgumentException();
 		}
 		this.opaleLine = line;
-		this.shape = shape;
+		this.node = node;
 		this.other = other;
 	}
 
diff --git a/src/net/sourceforge/plantuml/svek/image/EntityImageStateBorder.java b/src/net/sourceforge/plantuml/svek/image/EntityImageStateBorder.java
index 840e2d9fc..e9b8fd36c 100644
--- a/src/net/sourceforge/plantuml/svek/image/EntityImageStateBorder.java
+++ b/src/net/sourceforge/plantuml/svek/image/EntityImageStateBorder.java
@@ -55,7 +55,7 @@ import net.sourceforge.plantuml.graphic.color.ColorType;
 import net.sourceforge.plantuml.svek.AbstractEntityImage;
 import net.sourceforge.plantuml.svek.Bibliotekon;
 import net.sourceforge.plantuml.svek.Cluster;
-import net.sourceforge.plantuml.svek.Shape;
+import net.sourceforge.plantuml.svek.Node;
 import net.sourceforge.plantuml.svek.ShapeType;
 import net.sourceforge.plantuml.ugraphic.UChangeBackColor;
 import net.sourceforge.plantuml.ugraphic.UChangeColor;
@@ -89,8 +89,8 @@ public class EntityImageStateBorder extends AbstractEntityImage {
 
 	private boolean upPosition() {
 		final Point2D clusterCenter = stateParent.getClusterPosition().getPointCenter();
-		final Shape sh = bibliotekon.getShape(getEntity());
-		return sh.getMinY() < clusterCenter.getY();
+		final Node node = bibliotekon.getNode(getEntity());
+		return node.getMinY() < clusterCenter.getY();
 	}
 
 	public Dimension2D calculateDimension(StringBounder stringBounder) {
diff --git a/src/net/sourceforge/plantuml/svek/image/EntityImageTips.java b/src/net/sourceforge/plantuml/svek/image/EntityImageTips.java
index 8825e7267..60d17a2fc 100644
--- a/src/net/sourceforge/plantuml/svek/image/EntityImageTips.java
+++ b/src/net/sourceforge/plantuml/svek/image/EntityImageTips.java
@@ -61,7 +61,7 @@ import net.sourceforge.plantuml.graphic.color.ColorType;
 import net.sourceforge.plantuml.skin.rose.Rose;
 import net.sourceforge.plantuml.svek.AbstractEntityImage;
 import net.sourceforge.plantuml.svek.Bibliotekon;
-import net.sourceforge.plantuml.svek.Shape;
+import net.sourceforge.plantuml.svek.Node;
 import net.sourceforge.plantuml.svek.ShapeType;
 import net.sourceforge.plantuml.ugraphic.UGraphic;
 import net.sourceforge.plantuml.ugraphic.UTranslate;
@@ -120,17 +120,17 @@ public class EntityImageTips extends AbstractEntityImage {
 
 		final IEntity other = bibliotekon.getOnlyOther(getEntity());
 
-		final Shape shapeMe = bibliotekon.getShape(getEntity());
-		final Shape shapeOther = bibliotekon.getShape(other);
-		final Point2D positionMe = shapeMe.getPosition();
-		final Point2D positionOther = shapeOther.getPosition();
-		bibliotekon.getShape(getEntity());
+		final Node nodeMe = bibliotekon.getNode(getEntity());
+		final Node nodeOther = bibliotekon.getNode(other);
+		final Point2D positionMe = nodeMe.getPosition();
+		final Point2D positionOther = nodeOther.getPosition();
+		bibliotekon.getNode(getEntity());
 		final Position position = getPosition();
 		Direction direction = position.reverseDirection();
 		double height = 0;
 		for (Map.Entry<String, Display> ent : getEntity().getTips().entrySet()) {
 			final Display display = ent.getValue();
-			final Rectangle2D memberPosition = shapeOther.getImage().getInnerPosition(ent.getKey(), stringBounder,
+			final Rectangle2D memberPosition = nodeOther.getImage().getInnerPosition(ent.getKey(), stringBounder,
 					InnerStrategy.STRICT);
 			if (memberPosition == null) {
 				return;
diff --git a/src/net/sourceforge/plantuml/svg/SvgGraphics.java b/src/net/sourceforge/plantuml/svg/SvgGraphics.java
index 7b4c221b9..3ff140f66 100644
--- a/src/net/sourceforge/plantuml/svg/SvgGraphics.java
+++ b/src/net/sourceforge/plantuml/svg/SvgGraphics.java
@@ -538,13 +538,13 @@ public class SvgGraphics {
 	private Transformer getTransformer() throws TransformerException {
 		// Get a TransformerFactory object.
 		TransformerFactory xformFactory = null;
-		try {
-			final Class<?> factoryClass = Class
-					.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl");
-			xformFactory = (TransformerFactory) factoryClass.newInstance();
-		} catch (Exception e) {
+//		try {
+//			final Class<?> factoryClass = Class
+//					.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl");
+//			xformFactory = (TransformerFactory) factoryClass.newInstance();
+//		} catch (Exception e) {
 			xformFactory = TransformerFactory.newInstance();
-		}
+//		}
 		Log.info("TransformerFactory=" + xformFactory.getClass());
 
 		// Get an XSL Transformer object.
diff --git a/src/net/sourceforge/plantuml/tikz/TikzGraphics.java b/src/net/sourceforge/plantuml/tikz/TikzGraphics.java
index a7ce8e804..8898591df 100644
--- a/src/net/sourceforge/plantuml/tikz/TikzGraphics.java
+++ b/src/net/sourceforge/plantuml/tikz/TikzGraphics.java
@@ -135,8 +135,7 @@ public class TikzGraphics {
 			out(os, "                \\p2=(sourcenode.south east),");
 			out(os, "                \\n1={\\x2-\\x1},");
 			out(os, "                \\n2={\\y2-\\y1} in");
-			out(os,
-					"            node [inner sep=0pt, outer sep=0pt,anchor=north west,at=(\\p1)] {\\href{#1}{\\XeTeXLinkBox{\\phantom{\\rule{\\n1}{\\n2}}}}}");
+			out(os, "            node [inner sep=0pt, outer sep=0pt,anchor=north west,at=(\\p1)] {\\href{#1}{\\XeTeXLinkBox{\\phantom{\\rule{\\n1}{\\n2}}}}}");
 			out(os, "                    %xelatex needs \\XeTeXLinkBox, won't create a link unless it");
 			out(os, "                    %finds text --- rules don't work without \\XeTeXLinkBox.");
 			out(os, "                    %Still builds correctly with pdflatex and lualatex");
@@ -151,8 +150,7 @@ public class TikzGraphics {
 			out(os, "                \\p2=(sourcenode.south east),");
 			out(os, "                \\n1={\\x2-\\x1},");
 			out(os, "                \\n2={\\y2-\\y1} in");
-			out(os,
-					"            node [inner sep=0pt, outer sep=0pt,anchor=north west,at=(\\p1)] {\\hyperref [#1]{\\XeTeXLinkBox{\\phantom{\\rule{\\n1}{\\n2}}}}}");
+			out(os, "            node [inner sep=0pt, outer sep=0pt,anchor=north west,at=(\\p1)] {\\hyperref [#1]{\\XeTeXLinkBox{\\phantom{\\rule{\\n1}{\\n2}}}}}");
 			out(os, "                    %xelatex needs \\XeTeXLinkBox, won't create a link unless it");
 			out(os, "                    %finds text --- rules don't work without \\XeTeXLinkBox.");
 			out(os, "                    %Still builds correctly with pdflatex and lualatex");
@@ -318,6 +316,9 @@ public class TikzGraphics {
 	}
 
 	public void appendRaw(double x, double y, String formula) {
+		if (formula == null) {
+			throw new IllegalArgumentException();
+		}
 		final StringBuilder sb = new StringBuilder("\\node at " + couple(x, y));
 		sb.append("[below right");
 		sb.append("]{");
@@ -487,7 +488,8 @@ public class TikzGraphics {
 			} else if (type == USegmentType.SEG_QUADTO) {
 				throw new UnsupportedOperationException();
 			} else if (type == USegmentType.SEG_CUBICTO) {
-				// curvetoNoMacro(coord[0] + x, coord[1] + y, coord[2] + x, coord[3] + y, coord[4] + x, coord[5] + y);
+				// curvetoNoMacro(coord[0] + x, coord[1] + y, coord[2] + x, coord[3] + y,
+				// coord[4] + x, coord[5] + y);
 				sb.append(" ..controls ");
 				sb.append(couple(coord[0] + x, coord[1] + y));
 				sb.append(" and ");
@@ -543,8 +545,8 @@ public class TikzGraphics {
 	}
 
 	public void drawPathIterator(double x, double y, PathIterator path) {
-		final StringBuilder sb = new StringBuilder("\\draw[color=" + getColorName(color) + ",fill="
-				+ getColorName(color) + "] ");
+		final StringBuilder sb = new StringBuilder(
+				"\\draw[color=" + getColorName(color) + ",fill=" + getColorName(color) + "] ");
 		final double coord[] = new double[6];
 		while (path.isDone() == false) {
 			final int code = path.currentSegment(coord);
diff --git a/src/net/sourceforge/plantuml/ugraphic/UImage.java b/src/net/sourceforge/plantuml/ugraphic/UImage.java
index 569cff812..261855154 100644
--- a/src/net/sourceforge/plantuml/ugraphic/UImage.java
+++ b/src/net/sourceforge/plantuml/ugraphic/UImage.java
@@ -46,14 +46,24 @@ public class UImage implements UShape {
 
 	private final BufferedImage image;
 	private final String formula;
+	private final String rawFileName;
 
-	public UImage(BufferedImage image) {
-		this(image, null);
+	public String getRawFileName() {
+		return rawFileName;
 	}
 
-	public UImage(BufferedImage image, String formula) {
+	public UImage(BufferedImage image) {
+		this(null, image, null);
+	}
+
+	public UImage(String rawFileName, BufferedImage image) {
+		this(rawFileName, image, null);
+	}
+
+	public UImage(String rawFileName, BufferedImage image, String formula) {
 		this.image = image;
 		this.formula = formula;
+		this.rawFileName = rawFileName;
 	}
 
 	public UImage scale(double scale) {
@@ -74,7 +84,7 @@ public class UImage implements UShape {
 		final AffineTransform at = new AffineTransform();
 		at.scale(scale, scale);
 		final AffineTransformOp scaleOp = new AffineTransformOp(at, type);
-		return new UImage(scaleOp.filter(image, after), formula);
+		return new UImage(rawFileName, scaleOp.filter(image, after), formula);
 	}
 
 	public final BufferedImage getImage() {
@@ -109,7 +119,7 @@ public class UImage implements UShape {
 				}
 			}
 		}
-		return new UImage(copy, formula);
+		return new UImage(rawFileName, copy, formula);
 	}
 
 	public UImage muteTransparentColor(Color newColor) {
@@ -127,7 +137,7 @@ public class UImage implements UShape {
 				}
 			}
 		}
-		return new UImage(copy, formula);
+		return new UImage(rawFileName, copy, formula);
 	}
 
 	private int getDarkerRgb() {
@@ -135,7 +145,8 @@ public class UImage implements UShape {
 		for (int i = 0; i < image.getWidth(); i++) {
 			for (int j = 0; j < image.getHeight(); j++) {
 				final int color = image.getRGB(i, j);
-				// System.err.println("i="+i+" j="+j+" "+Integer.toHexString(color)+" "+isTransparent(color));
+				// System.err.println("i="+i+" j="+j+" "+Integer.toHexString(color)+"
+				// "+isTransparent(color));
 				final int rgb = getRgb(color);
 				final int a = getA(color);
 				if (a != mask_a__) {
@@ -171,7 +182,8 @@ public class UImage implements UShape {
 	// return true;
 	// }
 
-	// From https://stackoverflow.com/questions/3514158/how-do-you-clone-a-bufferedimage
+	// From
+	// https://stackoverflow.com/questions/3514158/how-do-you-clone-a-bufferedimage
 	private static BufferedImage deepCopyOld(BufferedImage bi) {
 		final ColorModel cm = bi.getColorModel();
 		final boolean isAlphaPremultiplied = cm.isAlphaPremultiplied();
@@ -180,7 +192,8 @@ public class UImage implements UShape {
 	}
 
 	private BufferedImage deepCopy2() {
-		final BufferedImage result = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB);
+		final BufferedImage result = new BufferedImage(image.getWidth(), image.getHeight(),
+				BufferedImage.TYPE_INT_ARGB);
 		for (int i = 0; i < this.image.getWidth(); i++) {
 			for (int j = 0; j < this.image.getHeight(); j++) {
 				result.setRGB(i, j, image.getRGB(i, j));
diff --git a/src/net/sourceforge/plantuml/ugraphic/tikz/DriverImageTikz.java b/src/net/sourceforge/plantuml/ugraphic/tikz/DriverImageTikz.java
index 0ad98c118..5ee37cedf 100644
--- a/src/net/sourceforge/plantuml/ugraphic/tikz/DriverImageTikz.java
+++ b/src/net/sourceforge/plantuml/ugraphic/tikz/DriverImageTikz.java
@@ -45,6 +45,15 @@ public class DriverImageTikz implements UDriver<TikzGraphics> {
 
 	public void draw(UShape ushape, double x, double y, ColorMapper mapper, UParam param, TikzGraphics tikz) {
 		final UImage shape = (UImage) ushape;
-		tikz.appendRaw(x, y, shape.getFormula());
+		final String rawFileName = shape.getRawFileName();
+		if (rawFileName != null) {
+			final String raw = "\\includegraphics{" + rawFileName + "}";
+			tikz.appendRaw(x, y, raw);
+			return;
+		}
+		final String formula = shape.getFormula();
+		if (formula != null) {
+			tikz.appendRaw(x, y, formula);
+		}
 	}
 }
diff --git a/src/net/sourceforge/plantuml/version/Version.java b/src/net/sourceforge/plantuml/version/Version.java
index f56c78261..b7321e099 100644
--- a/src/net/sourceforge/plantuml/version/Version.java
+++ b/src/net/sourceforge/plantuml/version/Version.java
@@ -43,7 +43,7 @@ public class Version {
 	private static final int MAJOR_SEPARATOR = 1000000;
 
 	public static int version() {
-		return 1202000;
+		return 1202001;
 	}
 
 	public static int versionPatched() {
@@ -59,12 +59,11 @@ public class Version {
 		}
 		return dotted(version());
 	}
-	
+
 	public static String fullDescription() {
 		return "PlantUML version " + Version.versionString() + " (" + Version.compileTimeString() + ")";
 	}
 
-
 	private static String dotted(int nb) {
 		final String minor = "" + nb % MAJOR_SEPARATOR;
 		final String major = "" + nb / MAJOR_SEPARATOR;
@@ -93,7 +92,7 @@ public class Version {
 	}
 
 	public static long compileTime() {
-		return 1578745853861L;
+		return 1581874832922L;
 	}
 
 	public static String compileTimeString() {
diff --git a/src/net/sourceforge/plantuml/wire/Block.java b/src/net/sourceforge/plantuml/wire/Block.java
new file mode 100644
index 000000000..32eb6c3cf
--- /dev/null
+++ b/src/net/sourceforge/plantuml/wire/Block.java
@@ -0,0 +1,208 @@
+/* ========================================================================
+ * PlantUML : a free UML diagram generator
+ * ========================================================================
+ *
+ * (C) Copyright 2009-2020, Arnaud Roques
+ *
+ * Project Info:  http://plantuml.com
+ * 
+ * If you like this project or if you find it useful, you can support us at:
+ * 
+ * http://plantuml.com/patreon (only 1$ per month!)
+ * http://plantuml.com/paypal
+ * 
+ * This file is part of PlantUML.
+ *
+ * PlantUML is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * PlantUML distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
+ * USA.
+ *
+ *
+ * Original Author:  Arnaud Roques
+ *
+ *
+ */
+package net.sourceforge.plantuml.wire;
+
+import java.awt.geom.Dimension2D;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import net.sourceforge.plantuml.Dimension2DDouble;
+import net.sourceforge.plantuml.FontParam;
+import net.sourceforge.plantuml.ISkinParam;
+import net.sourceforge.plantuml.command.Position;
+import net.sourceforge.plantuml.cucadiagram.Display;
+import net.sourceforge.plantuml.graphic.AbstractTextBlock;
+import net.sourceforge.plantuml.graphic.FontConfiguration;
+import net.sourceforge.plantuml.graphic.HorizontalAlignment;
+import net.sourceforge.plantuml.graphic.HtmlColorUtils;
+import net.sourceforge.plantuml.graphic.StringBounder;
+import net.sourceforge.plantuml.graphic.TextBlock;
+import net.sourceforge.plantuml.ugraphic.MinMax;
+import net.sourceforge.plantuml.ugraphic.UChangeColor;
+import net.sourceforge.plantuml.ugraphic.UEllipse;
+import net.sourceforge.plantuml.ugraphic.UGraphic;
+import net.sourceforge.plantuml.ugraphic.URectangle;
+import net.sourceforge.plantuml.ugraphic.UTranslate;
+
+public class Block extends AbstractTextBlock {
+
+	static class Pos {
+		final double x;
+		final double y;
+
+		public Pos(double x, double y) {
+			this.x = x;
+			this.y = y;
+		}
+
+		UGraphic move(UGraphic ug) {
+			return ug.apply(new UTranslate(x, y));
+		}
+	}
+
+	private final Map<Block, Pos> children = new LinkedHashMap<Block, Pos>();
+	private final Display display;
+	private final Dimension2DDouble fixedDim;
+	private final ISkinParam skinParam;
+
+	private final List<String> left = new ArrayList<String>();
+	private final List<String> right = new ArrayList<String>();
+	private final List<String> top = new ArrayList<String>();
+	private final List<String> bottom = new ArrayList<String>();
+
+	private double x = 10;
+	private double y = 10;
+
+	private MinMax minMax = MinMax.getEmpty(true);
+	private Block parent;
+
+	public Block(ISkinParam skinParam) {
+		this(skinParam, Display.empty(), null);
+	}
+
+	private Block(ISkinParam skinParam, Display display, Dimension2DDouble fixedDim) {
+		this.skinParam = skinParam;
+		this.display = display;
+		this.fixedDim = fixedDim;
+
+	}
+
+	private List<String> getPins(Position position) {
+		switch (position) {
+		case LEFT:
+			return left;
+		case RIGHT:
+			return right;
+		case TOP:
+			return top;
+		case BOTTOM:
+			return bottom;
+		}
+		throw new IllegalArgumentException();
+	}
+
+	public Dimension2D calculateDimension(StringBounder stringBounder) {
+		if (fixedDim == null) {
+			return minMax.getDimension();
+		}
+		return fixedDim;
+	}
+
+	public void drawU(UGraphic ug) {
+		ug = ug.apply(new UChangeColor(HtmlColorUtils.BLACK));
+		if (children.size() == 0) {
+			final TextBlock label = display.create(new FontConfiguration(skinParam, FontParam.COMPONENT, null),
+					HorizontalAlignment.CENTER, skinParam);
+			label.drawU(ug.apply(new UTranslate(10, 10)));
+		} else {
+			for (Entry<Block, Pos> ent : children.entrySet()) {
+				ent.getKey().drawU(ent.getValue().move(ug));
+			}
+		}
+		ug.draw(new URectangle(calculateDimension(ug.getStringBounder())));
+
+		drawPins(Position.BOTTOM, ug);
+		drawPins(Position.TOP, ug);
+		drawPins(Position.LEFT, ug);
+		drawPins(Position.RIGHT, ug);
+
+	}
+
+	private void drawPins(Position pos, UGraphic ug) {
+		double px = -2;
+		double py = 10;
+		if (pos == Position.RIGHT) {
+			px = calculateDimension(ug.getStringBounder()).getWidth() - 2;
+		}
+		if (pos == Position.TOP) {
+			px = 10;
+			py = -2;
+		}
+		if (pos == Position.BOTTOM) {
+			px = 10;
+			py = calculateDimension(ug.getStringBounder()).getHeight() - 2;
+		}
+		for (String pin : getPins(pos)) {
+			ug.apply(new UTranslate(px, py)).draw(new UEllipse(4, 4));
+			if (pos == Position.LEFT || pos == Position.RIGHT) {
+				py += 15;
+			} else {
+				px += 15;
+			}
+		}
+	}
+
+	public Block componentEnd() {
+		parent.minMax = parent.minMax.addPoint(parent.x + this.minMax.getMaxX() + 10,
+				parent.y + this.minMax.getMaxY() + 10);
+		parent.x += this.minMax.getMaxX() + 10;
+		return parent;
+	}
+
+	public Block addNewBlock(String name, int width, int height) {
+		final Dimension2DDouble dim = new Dimension2DDouble(width, height);
+		final Block child = new Block(skinParam, Display.create(name), dim);
+		children.put(child, new Pos(x, y));
+		y += dim.getHeight() + 10;
+		minMax = minMax.addPoint(x + dim.getWidth() + 10, y);
+		return child;
+	}
+
+	public Block createContainer(String name) {
+		final Block result = new Block(skinParam);
+		result.parent = this;
+		children.put(result, new Pos(x, y));
+		return result;
+	}
+
+	public void vspace(int vspace) {
+		y += vspace - 10;
+		minMax = minMax.addPoint(x, y);
+	}
+
+	public void newColumn() {
+		this.x = minMax.getMaxX();
+		this.y = 10;
+	}
+
+	public void addPin(Position position, String pin) {
+		getPins(position).add(pin);
+	}
+
+}
diff --git a/src/net/sourceforge/plantuml/wire/CommandComponent.java b/src/net/sourceforge/plantuml/wire/CommandComponent.java
new file mode 100644
index 000000000..a9bf9d4e9
--- /dev/null
+++ b/src/net/sourceforge/plantuml/wire/CommandComponent.java
@@ -0,0 +1,82 @@
+/* ========================================================================
+ * PlantUML : a free UML diagram generator
+ * ========================================================================
+ *
+ * (C) Copyright 2009-2020, Arnaud Roques
+ *
+ * Project Info:  http://plantuml.com
+ * 
+ * If you like this project or if you find it useful, you can support us at:
+ * 
+ * http://plantuml.com/patreon (only 1$ per month!)
+ * http://plantuml.com/paypal
+ * 
+ * This file is part of PlantUML.
+ *
+ * PlantUML is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * PlantUML distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
+ * USA.
+ *
+ *
+ * Original Author:  Arnaud Roques
+ * 
+ *
+ */
+package net.sourceforge.plantuml.wire;
+
+import net.sourceforge.plantuml.LineLocation;
+import net.sourceforge.plantuml.command.CommandExecutionResult;
+import net.sourceforge.plantuml.command.SingleLineCommand2;
+import net.sourceforge.plantuml.command.regex.IRegex;
+import net.sourceforge.plantuml.command.regex.RegexConcat;
+import net.sourceforge.plantuml.command.regex.RegexLeaf;
+import net.sourceforge.plantuml.command.regex.RegexOptional;
+import net.sourceforge.plantuml.command.regex.RegexResult;
+
+public class CommandComponent extends SingleLineCommand2<WireDiagram> {
+
+	public CommandComponent() {
+		super(false, getRegexConcat());
+	}
+
+	static IRegex getRegexConcat() {
+		return RegexConcat.build(CommandComponent.class.getName(), RegexLeaf.start(), //
+				RegexLeaf.spaceZeroOrMore(), //
+				new RegexLeaf("TYPE", "component"), //
+				RegexLeaf.spaceOneOrMore(), //
+				new RegexLeaf("NAME", "([\\w]+)"), //
+				new RegexOptional(new RegexConcat( //
+						RegexLeaf.spaceOneOrMore(), //
+						new RegexLeaf("\\["), //
+						new RegexLeaf("WIDTH", "([\\d]+)"), //
+						new RegexLeaf("[x*]"), //
+						new RegexLeaf("HEIGHT", "([\\d]+)"), //
+						new RegexLeaf("\\]")) //
+				), //
+				RegexLeaf.end());
+	}
+
+	@Override
+	protected CommandExecutionResult executeArg(WireDiagram diagram, LineLocation location, RegexResult arg) {
+		final String name = arg.get("NAME", 0);
+		final String width = arg.get("WIDTH", 0);
+		final String height = arg.get("HEIGHT", 0);
+		if (width != null) {
+			return diagram.addComponent(name, Integer.parseInt(width), Integer.parseInt(height));
+		} else {
+			return diagram.addComponent(name);
+		}
+	}
+
+}
diff --git a/src/net/sourceforge/plantuml/wire/CommandContainer.java b/src/net/sourceforge/plantuml/wire/CommandContainer.java
new file mode 100644
index 000000000..2c1ce3692
--- /dev/null
+++ b/src/net/sourceforge/plantuml/wire/CommandContainer.java
@@ -0,0 +1,70 @@
+/* ========================================================================
+ * PlantUML : a free UML diagram generator
+ * ========================================================================
+ *
+ * (C) Copyright 2009-2020, Arnaud Roques
+ *
+ * Project Info:  http://plantuml.com
+ * 
+ * If you like this project or if you find it useful, you can support us at:
+ * 
+ * http://plantuml.com/patreon (only 1$ per month!)
+ * http://plantuml.com/paypal
+ * 
+ * This file is part of PlantUML.
+ *
+ * PlantUML is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * PlantUML distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
+ * USA.
+ *
+ *
+ * Original Author:  Arnaud Roques
+ * 
+ *
+ */
+package net.sourceforge.plantuml.wire;
+
+import net.sourceforge.plantuml.LineLocation;
+import net.sourceforge.plantuml.command.CommandExecutionResult;
+import net.sourceforge.plantuml.command.SingleLineCommand2;
+import net.sourceforge.plantuml.command.regex.IRegex;
+import net.sourceforge.plantuml.command.regex.RegexConcat;
+import net.sourceforge.plantuml.command.regex.RegexLeaf;
+import net.sourceforge.plantuml.command.regex.RegexOptional;
+import net.sourceforge.plantuml.command.regex.RegexResult;
+
+public class CommandContainer extends SingleLineCommand2<WireDiagram> {
+
+	public CommandContainer() {
+		super(false, getRegexConcat());
+	}
+
+	static IRegex getRegexConcat() {
+		return RegexConcat.build(CommandContainer.class.getName(), RegexLeaf.start(), //
+				RegexLeaf.spaceZeroOrMore(), //
+				new RegexLeaf("TYPE", "component"), //
+				RegexLeaf.spaceOneOrMore(), //
+				new RegexLeaf("NAME", "([\\w]+)"), //
+				RegexLeaf.spaceZeroOrMore(), //
+				new RegexLeaf("\\{"), //
+				RegexLeaf.end());
+	}
+
+	@Override
+	protected CommandExecutionResult executeArg(WireDiagram diagram, LineLocation location, RegexResult arg) {
+		final String name = arg.get("NAME", 0);
+		return diagram.addStartContainer(name);
+	}
+
+}
diff --git a/src/net/sourceforge/plantuml/wire/CommandContainerEnd.java b/src/net/sourceforge/plantuml/wire/CommandContainerEnd.java
new file mode 100644
index 000000000..3fc90ce4f
--- /dev/null
+++ b/src/net/sourceforge/plantuml/wire/CommandContainerEnd.java
@@ -0,0 +1,65 @@
+/* ========================================================================
+ * PlantUML : a free UML diagram generator
+ * ========================================================================
+ *
+ * (C) Copyright 2009-2020, Arnaud Roques
+ *
+ * Project Info:  http://plantuml.com
+ * 
+ * If you like this project or if you find it useful, you can support us at:
+ * 
+ * http://plantuml.com/patreon (only 1$ per month!)
+ * http://plantuml.com/paypal
+ * 
+ * This file is part of PlantUML.
+ *
+ * PlantUML is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * PlantUML distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
+ * USA.
+ *
+ *
+ * Original Author:  Arnaud Roques
+ * 
+ *
+ */
+package net.sourceforge.plantuml.wire;
+
+import net.sourceforge.plantuml.LineLocation;
+import net.sourceforge.plantuml.command.CommandExecutionResult;
+import net.sourceforge.plantuml.command.SingleLineCommand2;
+import net.sourceforge.plantuml.command.regex.IRegex;
+import net.sourceforge.plantuml.command.regex.RegexConcat;
+import net.sourceforge.plantuml.command.regex.RegexLeaf;
+import net.sourceforge.plantuml.command.regex.RegexOptional;
+import net.sourceforge.plantuml.command.regex.RegexResult;
+
+public class CommandContainerEnd extends SingleLineCommand2<WireDiagram> {
+
+	public CommandContainerEnd() {
+		super(false, getRegexConcat());
+	}
+
+	static IRegex getRegexConcat() {
+		return RegexConcat.build(CommandContainerEnd.class.getName(), RegexLeaf.start(), //
+				RegexLeaf.spaceZeroOrMore(), //
+				new RegexLeaf("\\}"), //
+				RegexLeaf.end());
+	}
+
+	@Override
+	protected CommandExecutionResult executeArg(WireDiagram diagram, LineLocation location, RegexResult arg) {
+		return diagram.componentEnd();
+	}
+
+}
diff --git a/src/net/sourceforge/plantuml/wire/CommandNewColumn.java b/src/net/sourceforge/plantuml/wire/CommandNewColumn.java
new file mode 100644
index 000000000..650e5543c
--- /dev/null
+++ b/src/net/sourceforge/plantuml/wire/CommandNewColumn.java
@@ -0,0 +1,65 @@
+/* ========================================================================
+ * PlantUML : a free UML diagram generator
+ * ========================================================================
+ *
+ * (C) Copyright 2009-2020, Arnaud Roques
+ *
+ * Project Info:  http://plantuml.com
+ * 
+ * If you like this project or if you find it useful, you can support us at:
+ * 
+ * http://plantuml.com/patreon (only 1$ per month!)
+ * http://plantuml.com/paypal
+ * 
+ * This file is part of PlantUML.
+ *
+ * PlantUML is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * PlantUML distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
+ * USA.
+ *
+ *
+ * Original Author:  Arnaud Roques
+ * 
+ *
+ */
+package net.sourceforge.plantuml.wire;
+
+import net.sourceforge.plantuml.LineLocation;
+import net.sourceforge.plantuml.command.CommandExecutionResult;
+import net.sourceforge.plantuml.command.SingleLineCommand2;
+import net.sourceforge.plantuml.command.regex.IRegex;
+import net.sourceforge.plantuml.command.regex.RegexConcat;
+import net.sourceforge.plantuml.command.regex.RegexLeaf;
+import net.sourceforge.plantuml.command.regex.RegexOptional;
+import net.sourceforge.plantuml.command.regex.RegexResult;
+
+public class CommandNewColumn extends SingleLineCommand2<WireDiagram> {
+
+	public CommandNewColumn() {
+		super(false, getRegexConcat());
+	}
+
+	static IRegex getRegexConcat() {
+		return RegexConcat.build(CommandNewColumn.class.getName(), RegexLeaf.start(), //
+				RegexLeaf.spaceZeroOrMore(), //
+				new RegexLeaf("-+"), //
+				RegexLeaf.end());
+	}
+
+	@Override
+	protected CommandExecutionResult executeArg(WireDiagram diagram, LineLocation location, RegexResult arg) {
+		return diagram.newColumn();
+	}
+
+}
diff --git a/src/net/sourceforge/plantuml/wire/CommandPin.java b/src/net/sourceforge/plantuml/wire/CommandPin.java
new file mode 100644
index 000000000..c346cd8b5
--- /dev/null
+++ b/src/net/sourceforge/plantuml/wire/CommandPin.java
@@ -0,0 +1,79 @@
+/* ========================================================================
+ * PlantUML : a free UML diagram generator
+ * ========================================================================
+ *
+ * (C) Copyright 2009-2020, Arnaud Roques
+ *
+ * Project Info:  http://plantuml.com
+ * 
+ * If you like this project or if you find it useful, you can support us at:
+ * 
+ * http://plantuml.com/patreon (only 1$ per month!)
+ * http://plantuml.com/paypal
+ * 
+ * This file is part of PlantUML.
+ *
+ * PlantUML is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * PlantUML distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
+ * USA.
+ *
+ *
+ * Original Author:  Arnaud Roques
+ * 
+ *
+ */
+package net.sourceforge.plantuml.wire;
+
+import net.sourceforge.plantuml.LineLocation;
+import net.sourceforge.plantuml.command.CommandExecutionResult;
+import net.sourceforge.plantuml.command.Position;
+import net.sourceforge.plantuml.command.SingleLineCommand2;
+import net.sourceforge.plantuml.command.regex.IRegex;
+import net.sourceforge.plantuml.command.regex.RegexConcat;
+import net.sourceforge.plantuml.command.regex.RegexLeaf;
+import net.sourceforge.plantuml.command.regex.RegexOr;
+import net.sourceforge.plantuml.command.regex.RegexResult;
+
+public class CommandPin extends SingleLineCommand2<WireDiagram> {
+
+	public CommandPin() {
+		super(false, getRegexConcat());
+	}
+
+	static IRegex getRegexConcat() {
+		return RegexConcat.build(CommandPin.class.getName(), RegexLeaf.start(), //
+				RegexLeaf.spaceZeroOrMore(), //
+				new RegexOr("POSITION", //
+						new RegexLeaf("top"), //
+						new RegexLeaf("bottom"), //
+						new RegexLeaf("left"), //
+						new RegexLeaf("right")), //
+				new RegexLeaf(":"), //
+				new RegexLeaf("PINS", "(.*)"), //
+				RegexLeaf.end());
+	}
+
+	@Override
+	protected CommandExecutionResult executeArg(WireDiagram diagram, LineLocation location, RegexResult arg) {
+		final Position position = Position.fromString(arg.get("POSITION", 0));
+		final String pins = arg.get("PINS", 0);
+
+		for (String s : pins.split(",")) {
+			diagram.addPin(position, s.trim());
+		}
+
+		return CommandExecutionResult.ok();
+	}
+
+}
diff --git a/src/net/sourceforge/plantuml/wire/CommandPinSpace.java b/src/net/sourceforge/plantuml/wire/CommandPinSpace.java
new file mode 100644
index 000000000..ce213ab7c
--- /dev/null
+++ b/src/net/sourceforge/plantuml/wire/CommandPinSpace.java
@@ -0,0 +1,72 @@
+/* ========================================================================
+ * PlantUML : a free UML diagram generator
+ * ========================================================================
+ *
+ * (C) Copyright 2009-2020, Arnaud Roques
+ *
+ * Project Info:  http://plantuml.com
+ * 
+ * If you like this project or if you find it useful, you can support us at:
+ * 
+ * http://plantuml.com/patreon (only 1$ per month!)
+ * http://plantuml.com/paypal
+ * 
+ * This file is part of PlantUML.
+ *
+ * PlantUML is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * PlantUML distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
+ * USA.
+ *
+ *
+ * Original Author:  Arnaud Roques
+ * 
+ *
+ */
+package net.sourceforge.plantuml.wire;
+
+import net.sourceforge.plantuml.LineLocation;
+import net.sourceforge.plantuml.command.CommandExecutionResult;
+import net.sourceforge.plantuml.command.SingleLineCommand2;
+import net.sourceforge.plantuml.command.regex.IRegex;
+import net.sourceforge.plantuml.command.regex.RegexConcat;
+import net.sourceforge.plantuml.command.regex.RegexLeaf;
+import net.sourceforge.plantuml.command.regex.RegexOptional;
+import net.sourceforge.plantuml.command.regex.RegexOr;
+import net.sourceforge.plantuml.command.regex.RegexResult;
+
+public class CommandPinSpace extends SingleLineCommand2<WireDiagram> {
+
+	public CommandPinSpace() {
+		super(false, getRegexConcat());
+	}
+
+	static IRegex getRegexConcat() {
+		return RegexConcat.build(CommandPinSpace.class.getName(), RegexLeaf.start(), //
+				RegexLeaf.spaceZeroOrMore(), //
+				new RegexOr("POSITION", //
+						new RegexLeaf("top"), //
+						new RegexLeaf("bottom"), //
+						new RegexLeaf("left"), //
+						new RegexLeaf("right")), //
+				new RegexLeaf(" "), //
+				new RegexLeaf(".*"), //
+				RegexLeaf.end());
+	}
+
+	@Override
+	protected CommandExecutionResult executeArg(WireDiagram diagram, LineLocation location, RegexResult arg) {
+		return CommandExecutionResult.ok();
+	}
+
+}
diff --git a/src/net/sourceforge/plantuml/wire/CommandVspace.java b/src/net/sourceforge/plantuml/wire/CommandVspace.java
new file mode 100644
index 000000000..b78fe2e0b
--- /dev/null
+++ b/src/net/sourceforge/plantuml/wire/CommandVspace.java
@@ -0,0 +1,68 @@
+/* ========================================================================
+ * PlantUML : a free UML diagram generator
+ * ========================================================================
+ *
+ * (C) Copyright 2009-2020, Arnaud Roques
+ *
+ * Project Info:  http://plantuml.com
+ * 
+ * If you like this project or if you find it useful, you can support us at:
+ * 
+ * http://plantuml.com/patreon (only 1$ per month!)
+ * http://plantuml.com/paypal
+ * 
+ * This file is part of PlantUML.
+ *
+ * PlantUML is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * PlantUML distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
+ * USA.
+ *
+ *
+ * Original Author:  Arnaud Roques
+ * 
+ *
+ */
+package net.sourceforge.plantuml.wire;
+
+import net.sourceforge.plantuml.LineLocation;
+import net.sourceforge.plantuml.command.CommandExecutionResult;
+import net.sourceforge.plantuml.command.SingleLineCommand2;
+import net.sourceforge.plantuml.command.regex.IRegex;
+import net.sourceforge.plantuml.command.regex.RegexConcat;
+import net.sourceforge.plantuml.command.regex.RegexLeaf;
+import net.sourceforge.plantuml.command.regex.RegexOptional;
+import net.sourceforge.plantuml.command.regex.RegexResult;
+
+public class CommandVspace extends SingleLineCommand2<WireDiagram> {
+
+	public CommandVspace() {
+		super(false, getRegexConcat());
+	}
+
+	static IRegex getRegexConcat() {
+		return RegexConcat.build(CommandVspace.class.getName(), RegexLeaf.start(), //
+				RegexLeaf.spaceZeroOrMore(), //
+				new RegexLeaf("TYPE", "vspace"), //
+				RegexLeaf.spaceOneOrMore(), //
+				new RegexLeaf("HEIGHT", "([\\d]+)"), //
+				RegexLeaf.end());
+	}
+
+	@Override
+	protected CommandExecutionResult executeArg(WireDiagram diagram, LineLocation location, RegexResult arg) {
+		final String height = arg.get("HEIGHT", 0);
+		return diagram.vspace(Integer.parseInt(height));
+	}
+
+}
diff --git a/src/net/sourceforge/plantuml/wire/WireDiagram.java b/src/net/sourceforge/plantuml/wire/WireDiagram.java
new file mode 100644
index 000000000..1002bc4ee
--- /dev/null
+++ b/src/net/sourceforge/plantuml/wire/WireDiagram.java
@@ -0,0 +1,163 @@
+/* ========================================================================
+ * PlantUML : a free UML diagram generator
+ * ========================================================================
+ *
+ * (C) Copyright 2009-2020, Arnaud Roques
+ *
+ * Project Info:  http://plantuml.com
+ * 
+ * If you like this project or if you find it useful, you can support us at:
+ * 
+ * http://plantuml.com/patreon (only 1$ per month!)
+ * http://plantuml.com/paypal
+ * 
+ * This file is part of PlantUML.
+ *
+ * PlantUML is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * PlantUML distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
+ * USA.
+ *
+ *
+ * Original Author:  Arnaud Roques
+ *
+ *
+ */
+package net.sourceforge.plantuml.wire;
+
+import java.awt.geom.Dimension2D;
+import java.awt.geom.Rectangle2D;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import net.sourceforge.plantuml.AnnotatedWorker;
+import net.sourceforge.plantuml.FileFormatOption;
+import net.sourceforge.plantuml.ISkinParam;
+import net.sourceforge.plantuml.Scale;
+import net.sourceforge.plantuml.UmlDiagram;
+import net.sourceforge.plantuml.UmlDiagramType;
+import net.sourceforge.plantuml.command.CommandExecutionResult;
+import net.sourceforge.plantuml.command.Position;
+import net.sourceforge.plantuml.core.DiagramDescription;
+import net.sourceforge.plantuml.core.ImageData;
+import net.sourceforge.plantuml.graphic.HtmlColor;
+import net.sourceforge.plantuml.graphic.InnerStrategy;
+import net.sourceforge.plantuml.graphic.StringBounder;
+import net.sourceforge.plantuml.graphic.TextBlock;
+import net.sourceforge.plantuml.svek.TextBlockBackcolored;
+import net.sourceforge.plantuml.ugraphic.ImageBuilder;
+import net.sourceforge.plantuml.ugraphic.MinMax;
+import net.sourceforge.plantuml.ugraphic.UGraphic;
+
+public class WireDiagram extends UmlDiagram {
+
+	private final Block root = new Block(getSkinParam());
+	private Block current = root;
+	private Block last;
+
+	public DiagramDescription getDescription() {
+		return new DiagramDescription("Wire Diagram");
+	}
+
+	@Override
+	public UmlDiagramType getUmlDiagramType() {
+		return UmlDiagramType.WIRE;
+	}
+
+	@Override
+	protected ImageData exportDiagramInternal(OutputStream os, int index, FileFormatOption fileFormatOption)
+			throws IOException {
+		final Scale scale = getScale();
+
+		final double dpiFactor = scale == null ? getScaleCoef(fileFormatOption) : scale.getScale(100, 100);
+		final ISkinParam skinParam = getSkinParam();
+		final ImageBuilder imageBuilder = new ImageBuilder(skinParam.getColorMapper(), dpiFactor,
+				skinParam.getBackgroundColor(), fileFormatOption.isWithMetadata() ? getMetadata() : null, "", 10, 10,
+				null, skinParam.handwritten());
+		TextBlock result = getTextBlock();
+
+		result = new AnnotatedWorker(this, skinParam, fileFormatOption.getDefaultStringBounder()).addAdd(result);
+		imageBuilder.setUDrawable(result);
+
+		return imageBuilder.writeImageTOBEMOVED(fileFormatOption, seed(), os);
+	}
+
+	private TextBlockBackcolored getTextBlock() {
+		return new TextBlockBackcolored() {
+
+			public void drawU(UGraphic ug) {
+				drawMe(ug);
+			}
+
+			public Rectangle2D getInnerPosition(String member, StringBounder stringBounder, InnerStrategy strategy) {
+				return null;
+			}
+
+			public Dimension2D calculateDimension(StringBounder stringBounder) {
+				return getDrawingElement().calculateDimension(stringBounder);
+
+			}
+
+			public MinMax getMinMax(StringBounder stringBounder) {
+				throw new UnsupportedOperationException();
+			}
+
+			public HtmlColor getBackcolor() {
+				return null;
+			}
+		};
+	}
+
+	private void drawMe(UGraphic ug) {
+		getDrawingElement().drawU(ug);
+
+	}
+
+	private TextBlock getDrawingElement() {
+		return current;
+	}
+
+	public CommandExecutionResult addComponent(String name) {
+		return addComponent(name, 100, 100);
+	}
+
+	public CommandExecutionResult addComponent(String name, int width, int height) {
+		this.last = current.addNewBlock(name, width, height);
+		return CommandExecutionResult.ok();
+	}
+
+	public CommandExecutionResult vspace(int vspace) {
+		current.vspace(vspace);
+		return CommandExecutionResult.ok();
+	}
+
+	public CommandExecutionResult newColumn() {
+		current.newColumn();
+		return CommandExecutionResult.ok();
+	}
+
+	public CommandExecutionResult addStartContainer(String name) {
+		current = current.createContainer(name);
+		return CommandExecutionResult.ok();
+	}
+
+	public CommandExecutionResult componentEnd() {
+		current = current.componentEnd();
+		return CommandExecutionResult.ok();
+	}
+
+	public void addPin(Position position, String pin) {
+		last.addPin(position, pin);
+	}
+
+}
diff --git a/src/net/sourceforge/plantuml/wire/WireDiagramFactory.java b/src/net/sourceforge/plantuml/wire/WireDiagramFactory.java
new file mode 100644
index 000000000..e15d2b5c4
--- /dev/null
+++ b/src/net/sourceforge/plantuml/wire/WireDiagramFactory.java
@@ -0,0 +1,72 @@
+/* ========================================================================
+ * PlantUML : a free UML diagram generator
+ * ========================================================================
+ *
+ * (C) Copyright 2009-2020, Arnaud Roques
+ *
+ * Project Info:  http://plantuml.com
+ * 
+ * If you like this project or if you find it useful, you can support us at:
+ * 
+ * http://plantuml.com/patreon (only 1$ per month!)
+ * http://plantuml.com/paypal
+ * 
+ * This file is part of PlantUML.
+ *
+ * PlantUML is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * PlantUML distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
+ * USA.
+ *
+ *
+ * Original Author:  Arnaud Roques
+ *
+ *
+ */
+package net.sourceforge.plantuml.wire;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import net.sourceforge.plantuml.command.Command;
+import net.sourceforge.plantuml.command.UmlDiagramFactory;
+import net.sourceforge.plantuml.core.DiagramType;
+
+public class WireDiagramFactory extends UmlDiagramFactory {
+
+	public WireDiagramFactory() {
+		super(DiagramType.WIRE);
+	}
+
+	@Override
+	protected List<Command> createCommands() {
+
+		final List<Command> cmds = new ArrayList<Command>();
+		addCommonCommands1(cmds);
+		cmds.add(new CommandComponent());
+		cmds.add(new CommandContainer());
+		cmds.add(new CommandContainerEnd());
+		cmds.add(new CommandPin());
+		cmds.add(new CommandPinSpace());
+		cmds.add(new CommandVspace());
+		cmds.add(new CommandNewColumn());
+
+		return cmds;
+	}
+
+	@Override
+	public WireDiagram createEmptyDiagram() {
+		return new WireDiagram();
+	}
+
+}
diff --git a/stdlib/cloudinsight-abx.repx b/stdlib/cloudinsight-abx.repx
index 6d6d7832f..b72613854 100644
Binary files a/stdlib/cloudinsight-abx.repx and b/stdlib/cloudinsight-abx.repx differ
diff --git a/stdlib/home.repx b/stdlib/home.repx
index b09566345..edaba9444 100644
--- a/stdlib/home.repx
+++ b/stdlib/home.repx
@@ -9,3 +9,4 @@ office
 c4
 osa
 kubernetes
+logos
\ No newline at end of file
diff --git a/stdlib/logos-abx.repx b/stdlib/logos-abx.repx
new file mode 100644
index 000000000..1daaef512
Binary files /dev/null and b/stdlib/logos-abx.repx differ
diff --git a/stdlib/logos-dex.repx b/stdlib/logos-dex.repx
new file mode 100644
index 000000000..02e926f9e
Binary files /dev/null and b/stdlib/logos-dex.repx differ