1
0
mirror of https://github.com/octoleo/plantuml.git synced 2024-05-29 22:50:48 +00:00

Improve Gantt and add alpha support for ELK

This commit is contained in:
Arnaud Roques 2021-04-13 19:05:18 +02:00
parent f69cfcd011
commit dfdec8820f
27 changed files with 1072 additions and 248 deletions

15
pom.xml
View File

@ -94,6 +94,21 @@
<version>1.0.7</version> <version>1.0.7</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.eclipse.elk</groupId>
<artifactId>org.eclipse.elk.core</artifactId>
<version>0.7.1</version>
</dependency>
<dependency>
<groupId>org.eclipse.elk</groupId>
<artifactId>org.eclipse.elk.alg.layered</artifactId>
<version>0.7.1</version>
</dependency>
<dependency>
<groupId>org.eclipse.elk</groupId>
<artifactId>org.eclipse.elk.alg.mrtree</artifactId>
<version>0.7.1</version>
</dependency>
</dependencies> </dependencies>
<licenses> <licenses>

View File

@ -290,6 +290,15 @@ ganttDiagram {
timeline { timeline {
BackgroundColor transparent BackgroundColor transparent
} }
task {
RoundCorner 0
Margin 2 2 2 2
Padding 0
}
milestone {
Margin 2
Padding 3
}
} }

View File

@ -202,11 +202,20 @@ public abstract class TitledDiagram extends AbstractPSystem implements Diagram,
} }
private boolean useSmetana; private boolean useSmetana;
private boolean useElk;
public void setUseSmetana(boolean useSmetana) { public void setUseSmetana(boolean useSmetana) {
this.useSmetana = useSmetana; this.useSmetana = useSmetana;
} }
public void setUseElk(boolean useElk) {
this.useElk = useElk;
}
public boolean isUseElk() {
return this.useElk;
}
public boolean isUseSmetana() { public boolean isUseSmetana() {
if (FORCE_SMETANA) if (FORCE_SMETANA)
return true; return true;
@ -234,7 +243,6 @@ public abstract class TitledDiagram extends AbstractPSystem implements Diagram,
@Override @Override
public ImageBuilder createImageBuilder(FileFormatOption fileFormatOption) throws IOException { public ImageBuilder createImageBuilder(FileFormatOption fileFormatOption) throws IOException {
return super.createImageBuilder(fileFormatOption) return super.createImageBuilder(fileFormatOption).styled(this);
.styled(this);
} }
} }

View File

@ -77,11 +77,15 @@ public class CommandPragma extends SingleLineCommand2<TitledDiagram> {
} else { } else {
system.getPragma().define(name, value); system.getPragma().define(name, value);
if (name.equalsIgnoreCase("graphviz_dot") && value.equalsIgnoreCase("jdot")) { if (name.equalsIgnoreCase("graphviz_dot") && value.equalsIgnoreCase("jdot")) {
return CommandExecutionResult.error("This directive has been renamed to '!pragma graphviz_dot smetana'. Please update your diagram."); return CommandExecutionResult.error(
"This directive has been renamed to '!pragma graphviz_dot smetana'. Please update your diagram.");
} }
if (name.equalsIgnoreCase("graphviz_dot") && value.equalsIgnoreCase("smetana")) { if (name.equalsIgnoreCase("graphviz_dot") && value.equalsIgnoreCase("smetana")) {
system.setUseSmetana(true); system.setUseSmetana(true);
} }
if (name.equalsIgnoreCase("graphviz_dot") && value.equalsIgnoreCase("elk")) {
system.setUseElk(true);
}
if (name.equalsIgnoreCase("graphviz_dot") && value.equalsIgnoreCase(GraphvizUtils.VIZJS)) { if (name.equalsIgnoreCase("graphviz_dot") && value.equalsIgnoreCase(GraphvizUtils.VIZJS)) {
system.getSkinParam().setUseVizJs(true); system.getSkinParam().setUseVizJs(true);
} }

View File

@ -57,6 +57,7 @@ import net.sourceforge.plantuml.core.ImageData;
import net.sourceforge.plantuml.creole.CreoleMode; import net.sourceforge.plantuml.creole.CreoleMode;
import net.sourceforge.plantuml.cucadiagram.dot.CucaDiagramTxtMaker; import net.sourceforge.plantuml.cucadiagram.dot.CucaDiagramTxtMaker;
import net.sourceforge.plantuml.cucadiagram.entity.EntityFactory; import net.sourceforge.plantuml.cucadiagram.entity.EntityFactory;
import net.sourceforge.plantuml.elk.CucaDiagramFileMakerElk;
import net.sourceforge.plantuml.graphic.USymbol; import net.sourceforge.plantuml.graphic.USymbol;
import net.sourceforge.plantuml.sdot.CucaDiagramFileMakerSmetana; import net.sourceforge.plantuml.sdot.CucaDiagramFileMakerSmetana;
import net.sourceforge.plantuml.security.SecurityUtils; import net.sourceforge.plantuml.security.SecurityUtils;
@ -650,9 +651,14 @@ public abstract class CucaDiagram extends UmlDiagram implements GroupHierarchy,
entityFactory.buildSuperGroups(); entityFactory.buildSuperGroups();
final CucaDiagramFileMaker maker = this.isUseSmetana() final CucaDiagramFileMaker maker;
? new CucaDiagramFileMakerSmetana(this, fileFormatOption.getDefaultStringBounder(getSkinParam())) if (this.isUseElk()) {
: new CucaDiagramFileMakerSvek(this); maker = new CucaDiagramFileMakerElk(this, fileFormatOption.getDefaultStringBounder(getSkinParam()));
} else if (this.isUseSmetana()) {
maker = new CucaDiagramFileMakerSmetana(this, fileFormatOption.getDefaultStringBounder(getSkinParam()));
} else {
maker = new CucaDiagramFileMakerSvek(this);
}
final ImageData result = maker.createFile(os, getDotStrings(), fileFormatOption); final ImageData result = maker.createFile(os, getDotStrings(), fileFormatOption);
if (result == null) { if (result == null) {

View File

@ -0,0 +1,247 @@
/* ========================================================================
* 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.elk;
import java.awt.geom.Dimension2D;
import java.awt.geom.Point2D;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.eclipse.elk.core.RecursiveGraphLayoutEngine;
import org.eclipse.elk.core.math.ElkPadding;
import org.eclipse.elk.core.options.CoreOptions;
import org.eclipse.elk.core.options.NodeLabelPlacement;
import org.eclipse.elk.core.options.SizeConstraint;
import org.eclipse.elk.core.options.SizeOptions;
import org.eclipse.elk.core.util.NullElkProgressMonitor;
import org.eclipse.elk.graph.ElkEdge;
import org.eclipse.elk.graph.ElkLabel;
import org.eclipse.elk.graph.ElkNode;
import org.eclipse.elk.graph.util.ElkGraphUtil;
import net.sourceforge.plantuml.FileFormatOption;
import net.sourceforge.plantuml.ISkinParam;
import net.sourceforge.plantuml.StringUtils;
import net.sourceforge.plantuml.UmlDiagram;
import net.sourceforge.plantuml.api.ImageDataSimple;
import net.sourceforge.plantuml.core.ImageData;
import net.sourceforge.plantuml.cucadiagram.CucaDiagram;
import net.sourceforge.plantuml.cucadiagram.ILeaf;
import net.sourceforge.plantuml.cucadiagram.Link;
import net.sourceforge.plantuml.graphic.AbstractTextBlock;
import net.sourceforge.plantuml.graphic.QuoteUtils;
import net.sourceforge.plantuml.graphic.StringBounder;
import net.sourceforge.plantuml.graphic.TextBlock;
import net.sourceforge.plantuml.graphic.TextBlockUtils;
import net.sourceforge.plantuml.svek.Bibliotekon;
import net.sourceforge.plantuml.svek.CucaDiagramFileMaker;
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.TextBlockBackcolored;
import net.sourceforge.plantuml.ugraphic.MinMax;
import net.sourceforge.plantuml.ugraphic.UGraphic;
import net.sourceforge.plantuml.ugraphic.UTranslate;
import net.sourceforge.plantuml.ugraphic.color.HColor;
public class CucaDiagramFileMakerElk implements CucaDiagramFileMaker {
private final CucaDiagram diagram;
private final StringBounder stringBounder;
private final DotStringFactory dotStringFactory;
private final Map<ILeaf, ElkNode> nodes = new LinkedHashMap<ILeaf, ElkNode>();
public CucaDiagramFileMakerElk(CucaDiagram diagram, StringBounder stringBounder) {
this.diagram = diagram;
this.stringBounder = stringBounder;
this.dotStringFactory = new DotStringFactory(stringBounder, diagram);
}
// The Drawing class does the real drawing
class Drawing extends AbstractTextBlock implements TextBlockBackcolored {
// min and max of all coord
private final MinMax minMax;
public Drawing(MinMax minMax) {
this.minMax = minMax;
}
public void drawU(UGraphic ug) {
// Draw all nodes
for (Entry<ILeaf, ElkNode> ent : nodes.entrySet()) {
final ILeaf leaf = ent.getKey();
final ElkNode agnode = ent.getValue();
final IEntityImage image = printEntityInternal(leaf);
// Retrieve coord from ELK
final Point2D corner = new Point2D.Double(agnode.getX(), agnode.getY());
// Print the node image at right coord
image.drawU(ug.apply(new UTranslate(corner)));
}
}
public Dimension2D calculateDimension(StringBounder stringBounder) {
if (minMax == null) {
throw new UnsupportedOperationException();
}
return minMax.getDimension();
}
public HColor getBackcolor() {
return null;
}
}
@Override
public ImageData createFile(OutputStream os, List<String> dotStrings, FileFormatOption fileFormatOption)
throws IOException {
try {
final ElkNode root = ElkGraphUtil.createGraph();
final ElkPadding labelPadding = new ElkPadding(2.0);
// Convert all "leaf" to ELK node
for (ILeaf leaf : diagram.getLeafsvalues()) {
final IEntityImage image = printEntityInternal(leaf);
// Expected dimension of the node
final Dimension2D dimension = image.calculateDimension(stringBounder);
// Here, we try to tell ELK to use this dimension as node dimension
final ElkNode node = ElkGraphUtil.createNode(root);
node.setDimensions(dimension.getWidth(), dimension.getHeight());
// There is no real "label" here
// We just would like to force node dimension
final ElkLabel label = ElkGraphUtil.createLabel(node);
label.setDimensions(dimension.getWidth(), dimension.getHeight());
// No idea of what we are doing here :-)
label.setProperty(CoreOptions.NODE_LABELS_PLACEMENT, EnumSet.of(NodeLabelPlacement.INSIDE,
NodeLabelPlacement.H_CENTER, NodeLabelPlacement.V_CENTER));
label.setProperty(CoreOptions.NODE_LABELS_PADDING, labelPadding);
node.setProperty(CoreOptions.NODE_SIZE_CONSTRAINTS, EnumSet.of(SizeConstraint.NODE_LABELS));
node.setProperty(CoreOptions.NODE_SIZE_OPTIONS, EnumSet.noneOf(SizeOptions.class));
// Let's store this
nodes.put(leaf, node);
}
for (final Link link : diagram.getLinks()) {
final ElkEdge edge = ElkGraphUtil.createEdge(root);
System.err.println("edge=" + edge);
edge.getSources().add(nodes.get(link.getEntity1()));
edge.getTargets().add(nodes.get(link.getEntity2()));
}
final RecursiveGraphLayoutEngine engine = new RecursiveGraphLayoutEngine();
engine.layout(root, new NullElkProgressMonitor());
// Debug
for (final ElkNode node : nodes.values()) {
final String name = node.getLabels().get(0).getText();
System.out.println("node " + name + " : " + node.getX() + ", " + node.getY() + " (" + node.getWidth()
+ ", " + node.getHeight() + ")");
}
final MinMax minMax = TextBlockUtils.getMinMax(new Drawing(null), stringBounder, false);
final TextBlock drawable = new Drawing(minMax);
return diagram.createImageBuilder(fileFormatOption) //
.drawable(drawable) //
.write(os); //
} catch (Throwable e) {
UmlDiagram.exportDiagramError(os, e, fileFormatOption, diagram.seed(), diagram.getMetadata(),
diagram.getFlashData(), getFailureText3(e));
return ImageDataSimple.error();
}
}
static private List<String> getFailureText3(Throwable exception) {
exception.printStackTrace();
final List<String> strings = new ArrayList<String>();
strings.add("An error has occured : " + exception);
final String quote = StringUtils.rot(QuoteUtils.getSomeQuote());
strings.add("<i>" + quote);
strings.add(" ");
GraphvizCrash.addProperties(strings);
strings.add(" ");
strings.add("Sorry, ELK intregration is really alpha feature...");
strings.add(" ");
strings.add("You should send this diagram and this image to <b>plantuml@gmail.com</b> or");
strings.add("post to <b>http://plantuml.com/qa</b> to solve this issue.");
strings.add(" ");
return strings;
}
private Bibliotekon getBibliotekon() {
return dotStringFactory.getBibliotekon();
}
private IEntityImage printEntityInternal(ILeaf ent) {
if (ent.isRemoved()) {
throw new IllegalStateException();
}
if (ent.getSvekImage() == null) {
final ISkinParam skinParam = diagram.getSkinParam();
if (skinParam.sameClassWidth()) {
System.err.println("NOT YET IMPLEMENED");
}
return GeneralImageBuilder.createEntityImageBlock(ent, skinParam, diagram.isHideEmptyDescriptionForState(),
diagram, getBibliotekon(), null, diagram.getUmlDiagramType(), diagram.getLinks());
}
return ent.getSvekImage();
}
}

View File

@ -93,35 +93,35 @@ public class ScientificEquationSafe {
private ImageData dimSvg; private ImageData dimSvg;
public UImageSvg getSvg(double scale, Color foregroundColor, Color backgroundColor) { public UImageSvg getSvg(double scale, Color foregroundColor, Color backgroundColor) {
try { if (equation != null)
final UImageSvg svg = equation.getSvg(scale, foregroundColor, backgroundColor);
dimSvg = new ImageDataSimple(equation.getDimension());
return svg;
} catch (Exception e) {
printTrace(e);
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
try { try {
dimSvg = plainImageBuilder(getRollback(), new FileFormatOption(FileFormat.SVG)) final UImageSvg svg = equation.getSvg(scale, foregroundColor, backgroundColor);
.write(baos); dimSvg = new ImageDataSimple(equation.getDimension());
} catch (IOException e1) { return svg;
return null; } catch (Exception e) {
printTrace(e);
} }
return new UImageSvg(new String(baos.toByteArray()), scale); final ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
dimSvg = plainImageBuilder(getRollback(), new FileFormatOption(FileFormat.SVG)).write(baos);
} catch (IOException e1) {
return null;
} }
return new UImageSvg(new String(baos.toByteArray()), scale);
} }
public MutableImage getImage(Color foregroundColor, Color backgroundColor) { public MutableImage getImage(Color foregroundColor, Color backgroundColor) {
try { if (equation != null)
return equation.getImage(foregroundColor, backgroundColor);
} catch (Exception e) {
printTrace(e);
try { try {
final byte[] bytes = plainPngBuilder(getRollback()).writeByteArray(); return equation.getImage(foregroundColor, backgroundColor);
return new PixelImage(ImageIO.read(new ByteArrayInputStream(bytes)), } catch (Exception e) {
AffineTransformType.TYPE_BILINEAR); printTrace(e);
} catch (IOException e1) {
return null;
} }
try {
final byte[] bytes = plainPngBuilder(getRollback()).writeByteArray();
return new PixelImage(ImageIO.read(new ByteArrayInputStream(bytes)), AffineTransformType.TYPE_BILINEAR);
} catch (IOException e1) {
return null;
} }
} }

View File

@ -169,6 +169,9 @@ public class GridTextBlockDecorated extends GridTextBlockSimple {
for (int j = i + 1; j < groups.size(); j++) { for (int j = i + 1; j < groups.size(); j++) {
final NwGroup group1 = groups.get(i); final NwGroup group1 = groups.get(i);
final NwGroup group2 = groups.get(j); final NwGroup group2 = groups.get(j);
if (group1.size() == 0 || group2.size() == 0) {
continue;
}
if (group1.getNetwork() != group2.getNetwork()) { if (group1.getNetwork() != group2.getNetwork()) {
continue; continue;
} }

View File

@ -71,6 +71,10 @@ public class NwGroup {
this.network = network; this.network = network;
} }
public int size() {
return elements.size();
}
public final String getName() { public final String getName() {
return name; return name;
} }

View File

@ -43,8 +43,12 @@ import net.sourceforge.plantuml.project.core.TaskAttribute;
import net.sourceforge.plantuml.project.core.TaskInstant; import net.sourceforge.plantuml.project.core.TaskInstant;
import net.sourceforge.plantuml.project.draw.TaskDraw; import net.sourceforge.plantuml.project.draw.TaskDraw;
import net.sourceforge.plantuml.project.timescale.TimeScale; import net.sourceforge.plantuml.project.timescale.TimeScale;
import net.sourceforge.plantuml.style.ClockwiseTopRightBottomLeft;
import net.sourceforge.plantuml.style.PName; import net.sourceforge.plantuml.style.PName;
import net.sourceforge.plantuml.style.SName;
import net.sourceforge.plantuml.style.Style; import net.sourceforge.plantuml.style.Style;
import net.sourceforge.plantuml.style.StyleBuilder;
import net.sourceforge.plantuml.style.StyleSignature;
import net.sourceforge.plantuml.ugraphic.UGraphic; import net.sourceforge.plantuml.ugraphic.UGraphic;
import net.sourceforge.plantuml.ugraphic.ULine; import net.sourceforge.plantuml.ugraphic.ULine;
import net.sourceforge.plantuml.ugraphic.UStroke; import net.sourceforge.plantuml.ugraphic.UStroke;
@ -62,9 +66,11 @@ public class GanttArrow implements UDrawable {
private final HColorSet colorSet; private final HColorSet colorSet;
private final Style style; private final Style style;
private final ToTaskDraw toTaskDraw; private final ToTaskDraw toTaskDraw;
private final StyleBuilder styleBuilder;
public GanttArrow(HColorSet colorSet, Style style, TimeScale timeScale, TaskInstant source, TaskInstant dest, public GanttArrow(HColorSet colorSet, Style style, TimeScale timeScale, TaskInstant source, TaskInstant dest,
ToTaskDraw toTaskDraw) { ToTaskDraw toTaskDraw, StyleBuilder styleBuilder) {
this.styleBuilder = styleBuilder;
this.toTaskDraw = toTaskDraw; this.toTaskDraw = toTaskDraw;
this.style = style; this.style = style;
this.colorSet = colorSet; this.colorSet = colorSet;
@ -98,30 +104,34 @@ public class GanttArrow implements UDrawable {
public void drawU(UGraphic ug) { public void drawU(UGraphic ug) {
ug = style.applyStrokeAndLineColor(ug, colorSet); ug = style.applyStrokeAndLineColor(ug, colorSet);
// ug = ug.apply(color.bg()).apply(color).apply(style.getStroke3(new
// UStroke(1.5)));
double x1 = getX(source.withDelta(0), atStart); double x1 = getX(source.getAttribute(), getSource(), atStart);
final StringBounder stringBounder = ug.getStringBounder(); final StringBounder stringBounder = ug.getStringBounder();
double y1 = getSource().getY(stringBounder, atStart); double y1 = getSource().getY(stringBounder, atStart);
final double x2 = getX(dest, atEnd.getInv()); final double x2 = getX(dest.getAttribute(), getDestination(), atEnd.getInv());
final double y2 = getDestination().getY(stringBounder, atEnd); final double y2 = getDestination().getY(stringBounder, atEnd);
if (atStart == Direction.DOWN && y2 < y1) { if (atStart == Direction.DOWN && y2 < y1) {
y1 = getSource().getY(stringBounder, atStart.getInv()); y1 = getSource().getY(stringBounder, atStart.getInv());
} }
final double minimalWidth = 8;
// final Style style = getStyleSignatureTask().getMergedStyle(styleBuilder);
// final ClockwiseTopRightBottomLeft margin = style.getMargin();
// final ClockwiseTopRightBottomLeft padding = style.getPadding();
if (this.atStart == Direction.DOWN && this.atEnd == Direction.RIGHT) { if (this.atStart == Direction.DOWN && this.atEnd == Direction.RIGHT) {
if (x2 > x1) { if (x2 > x1) {
if (x2 - x1 < 8) { if (x2 - x1 < minimalWidth) {
x1 = x2 - 8; x1 = x2 - minimalWidth;
} }
drawLine(ug, x1, y1, x1, y2, x2, y2); drawLine(ug, x1, y1, x1, y2, x2, y2);
} else { } else {
x1 = getX(source.withDelta(0), Direction.RIGHT); x1 = getX(source.getAttribute(), getSource(), Direction.RIGHT);
y1 = getSource().getY(stringBounder, Direction.RIGHT); y1 = getSource().getY(stringBounder, Direction.RIGHT);
drawLine(ug, x1, y1, x1 + 6, y1, x1 + 6, y1 + 8, x2 - 8, y1 + 8, x2 - 8, y2, x2, y2); final double y1b = getDestination().getY(stringBounder);
drawLine(ug, x1, y1, x1 + 6, y1, x1 + 6, y1b, x2 - 8, y1b, x2 - 8, y2, x2, y2);
} }
} else if (this.atStart == Direction.RIGHT && this.atEnd == Direction.LEFT) { } else if (this.atStart == Direction.RIGHT && this.atEnd == Direction.LEFT) {
final double xmax = Math.max(x1, x2) + 8; final double xmax = Math.max(x1, x2) + 8;
@ -151,15 +161,17 @@ public class GanttArrow implements UDrawable {
} }
private double getX(TaskInstant when, Direction direction) { private StyleSignature getStyleSignatureTask() {
final double x1 = timeScale.getStartingPosition(when.getInstantTheorical()); return StyleSignature.of(SName.root, SName.element, SName.ganttDiagram, SName.task);
final double x2 = timeScale.getEndingPosition(when.getInstantTheorical()); }
private double getX(TaskAttribute taskAttribute, TaskDraw task, Direction direction) {
if (direction == Direction.LEFT) { if (direction == Direction.LEFT) {
return x1; return task.getX1(taskAttribute) - 1;
} }
if (direction == Direction.RIGHT) { if (direction == Direction.RIGHT) {
return x2; return task.getX2(taskAttribute) + 1;
} }
return (x1 + x2) / 2; return (task.getX1(taskAttribute) + (task.getX2(taskAttribute))) / 2;
} }
} }

View File

@ -101,7 +101,7 @@ public class GanttConstraint extends WithLinkType {
Style style = styleBuilder.getMergedStyle(getStyleSignature()).eventuallyOverride(PName.LineColor, Style style = styleBuilder.getMergedStyle(getStyleSignature()).eventuallyOverride(PName.LineColor,
getSpecificColor()); getSpecificColor());
style = style.eventuallyOverride(getType().getStroke3(style.getStroke())); style = style.eventuallyOverride(getType().getStroke3(style.getStroke()));
return new GanttArrow(colorSet, style, timeScale, source, dest, toTaskDraw); return new GanttArrow(colorSet, style, timeScale, source, dest, toTaskDraw, styleBuilder);
} }
public boolean isHidden(Day min, Day max) { public boolean isHidden(Day min, Day max) {

View File

@ -60,6 +60,7 @@ import net.sourceforge.plantuml.core.ImageData;
import net.sourceforge.plantuml.cucadiagram.Display; import net.sourceforge.plantuml.cucadiagram.Display;
import net.sourceforge.plantuml.graphic.InnerStrategy; import net.sourceforge.plantuml.graphic.InnerStrategy;
import net.sourceforge.plantuml.graphic.StringBounder; import net.sourceforge.plantuml.graphic.StringBounder;
import net.sourceforge.plantuml.graphic.UDrawable;
import net.sourceforge.plantuml.project.core.Moment; import net.sourceforge.plantuml.project.core.Moment;
import net.sourceforge.plantuml.project.core.MomentImpl; import net.sourceforge.plantuml.project.core.MomentImpl;
import net.sourceforge.plantuml.project.core.PrintScale; import net.sourceforge.plantuml.project.core.PrintScale;
@ -93,6 +94,7 @@ import net.sourceforge.plantuml.style.PName;
import net.sourceforge.plantuml.style.SName; import net.sourceforge.plantuml.style.SName;
import net.sourceforge.plantuml.style.Style; import net.sourceforge.plantuml.style.Style;
import net.sourceforge.plantuml.style.StyleSignature; import net.sourceforge.plantuml.style.StyleSignature;
import net.sourceforge.plantuml.svek.GraphvizCrash;
import net.sourceforge.plantuml.svek.TextBlockBackcolored; import net.sourceforge.plantuml.svek.TextBlockBackcolored;
import net.sourceforge.plantuml.ugraphic.MinMax; import net.sourceforge.plantuml.ugraphic.MinMax;
import net.sourceforge.plantuml.ugraphic.UGraphic; import net.sourceforge.plantuml.ugraphic.UGraphic;
@ -215,28 +217,35 @@ public class GanttDiagram extends TitledDiagram implements ToTaskDraw, WithSprit
return new TextBlockBackcolored() { return new TextBlockBackcolored() {
public void drawU(UGraphic ug) { public void drawU(UGraphic ug) {
final Style timelineStyle = StyleSignature try {
.of(SName.root, SName.element, SName.ganttDiagram, SName.timeline) final Style timelineStyle = StyleSignature
.getMergedStyle(getCurrentStyleBuilder()); .of(SName.root, SName.element, SName.ganttDiagram, SName.timeline)
.getMergedStyle(getCurrentStyleBuilder());
final HColor back = timelineStyle.value(PName.BackGroundColor).asColor(getIHtmlColorSet()); final HColor back = timelineStyle.value(PName.BackGroundColor).asColor(getIHtmlColorSet());
if (HColorUtils.isTransparent(back) == false) { if (HColorUtils.isTransparent(back) == false) {
final URectangle rect1 = new URectangle(calculateDimension(ug.getStringBounder()).getWidth(), final URectangle rect1 = new URectangle(calculateDimension(ug.getStringBounder()).getWidth(),
timeHeader.getTimeHeaderHeight()); timeHeader.getTimeHeaderHeight());
final URectangle rect2 = new URectangle(calculateDimension(ug.getStringBounder()).getWidth(), final URectangle rect2 = new URectangle(calculateDimension(ug.getStringBounder()).getWidth(),
timeHeader.getTimeFooterHeight()); timeHeader.getTimeFooterHeight());
ug.apply(back.bg()).draw(rect1); ug.apply(back.bg()).draw(rect1);
ug.apply(back.bg()).apply(UTranslate.dy(totalHeightWithoutFooter)).draw(rect2); ug.apply(back.bg()).apply(UTranslate.dy(totalHeightWithoutFooter)).draw(rect2);
} }
timeHeader.drawTimeHeader(ug, totalHeightWithoutFooter); timeHeader.drawTimeHeader(ug, totalHeightWithoutFooter);
drawConstraints(ug, timeHeader.getTimeScale());
drawTasksRect(ug);
drawTasksTitle(ug);
drawResources(ug);
if (showFootbox) {
timeHeader.drawTimeFooter(ug.apply(UTranslate.dy(totalHeightWithoutFooter)));
}
} catch (Throwable t) {
t.printStackTrace();
final UDrawable crash = new GraphvizCrash(getSource().getPlainString(), false, t);
crash.drawU(ug);
drawConstraints(ug, timeHeader.getTimeScale());
drawTasksRect(ug);
drawTasksTitle(ug);
drawResources(ug);
if (showFootbox) {
timeHeader.drawTimeFooter(ug.apply(UTranslate.dy(totalHeightWithoutFooter)));
} }
} }
@ -366,7 +375,7 @@ public class GanttDiagram extends TitledDiagram implements ToTaskDraw, WithSprit
draw.setColorsAndCompletion(tmp.getColors(), tmp.getCompletion(), tmp.getUrl(), tmp.getNote()); draw.setColorsAndCompletion(tmp.getColors(), tmp.getCompletion(), tmp.getUrl(), tmp.getNote());
} }
if (task.getRow() == null) { if (task.getRow() == null) {
y += draw.getHeightTask(stringBounder); y += draw.getFullHeightTask(stringBounder);
} }
draws.put(task, draw); draws.put(task, draw);
} }

View File

@ -46,6 +46,7 @@ import net.sourceforge.plantuml.project.core.Task;
import net.sourceforge.plantuml.project.lang.CenterBorderColor; import net.sourceforge.plantuml.project.lang.CenterBorderColor;
import net.sourceforge.plantuml.project.time.Day; import net.sourceforge.plantuml.project.time.Day;
import net.sourceforge.plantuml.project.timescale.TimeScale; import net.sourceforge.plantuml.project.timescale.TimeScale;
import net.sourceforge.plantuml.style.ClockwiseTopRightBottomLeft;
import net.sourceforge.plantuml.style.PName; import net.sourceforge.plantuml.style.PName;
import net.sourceforge.plantuml.style.Style; import net.sourceforge.plantuml.style.Style;
import net.sourceforge.plantuml.style.StyleBuilder; import net.sourceforge.plantuml.style.StyleBuilder;
@ -68,8 +69,6 @@ public abstract class AbstractTaskDraw implements TaskDraw {
private final Task task; private final Task task;
private final ToTaskDraw toTaskDraw; private final ToTaskDraw toTaskDraw;
protected final double margin = 2;
@Override @Override
final public String toString() { final public String toString() {
return super.toString() + " " + task; return super.toString() + " " + task;
@ -112,12 +111,12 @@ public abstract class AbstractTaskDraw implements TaskDraw {
return getStyleSignature().getMergedStyle(styleBuilder); return getStyleSignature().getMergedStyle(styleBuilder);
} }
final protected double getShapeHeight(StringBounder stringBounder) { abstract protected double getShapeHeight(StringBounder stringBounder);
return getHeightTask(stringBounder) - 2 * margin;
}
final public double getHeightTask(StringBounder stringBounder) { final public double getFullHeightTask(StringBounder stringBounder) {
return getFontConfiguration().getFont().getSize2D() + 5; final Style style = getStyle();
final ClockwiseTopRightBottomLeft margin = style.getMargin();
return margin.getTop() + getShapeHeight(stringBounder) + margin.getBottom();
} }
public TaskDraw getTrueRow() { public TaskDraw getTrueRow() {
@ -142,13 +141,21 @@ public abstract class AbstractTaskDraw implements TaskDraw {
} }
public final double getY(StringBounder stringBounder, Direction direction) { public final double getY(StringBounder stringBounder, Direction direction) {
final Style style = getStyle();
final ClockwiseTopRightBottomLeft margin = style.getMargin();
final ClockwiseTopRightBottomLeft padding = style.getPadding();
final double y1 = margin.getTop() + getY(stringBounder);
final double y2 = y1 + getShapeHeight(stringBounder);
if (direction == Direction.UP) { if (direction == Direction.UP) {
return getY(stringBounder); return y1;
} }
if (direction == Direction.DOWN) { if (direction == Direction.DOWN) {
return getY(stringBounder) + getHeightTask(stringBounder); return y2;
} }
return getY(stringBounder) + getHeightTask(stringBounder) / 2; return (y1 + y2) / 2;
} }
protected final StyleBuilder getStyleBuilder() { protected final StyleBuilder getStyleBuilder() {

View File

@ -41,26 +41,26 @@ import net.sourceforge.plantuml.ugraphic.UPath;
public class PathUtils { public class PathUtils {
private final static double round = 4; public static UPath UtoRight(double width, double height, double round) {
final double halfRound = round / 2;
public static UPath UtoRight(double width, double height) {
final UPath result = new UPath(); final UPath result = new UPath();
result.moveTo(0, 0); result.moveTo(0, 0);
result.lineTo(width - round, 0); result.lineTo(width - halfRound, 0);
result.arcTo(new Point2D.Double(width, round), round, 0, 1); result.arcTo(new Point2D.Double(width, halfRound), halfRound, 0, 1);
result.lineTo(width, height - round); result.lineTo(width, height - halfRound);
result.arcTo(new Point2D.Double(width - round, height), round, 0, 1); result.arcTo(new Point2D.Double(width - halfRound, height), halfRound, 0, 1);
result.lineTo(0, height); result.lineTo(0, height);
return result; return result;
} }
public static UPath UtoLeft(double width, double height) { public static UPath UtoLeft(double width, double height, double round) {
final double halfRound = round / 2;
final UPath result = new UPath(); final UPath result = new UPath();
result.moveTo(width, height); result.moveTo(width, height);
result.lineTo(round, height); result.lineTo(halfRound, height);
result.arcTo(new Point2D.Double(0, height - round), round, 0, 1); result.arcTo(new Point2D.Double(0, height - halfRound), halfRound, 0, 1);
result.lineTo(0, round); result.lineTo(0, halfRound);
result.arcTo(new Point2D.Double(round, 0), round, 0, 1); result.arcTo(new Point2D.Double(halfRound, 0), halfRound, 0, 1);
result.lineTo(width, 0); result.lineTo(width, 0);
return result; return result;
} }

View File

@ -0,0 +1,195 @@
/* ========================================================================
* 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.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import net.sourceforge.plantuml.sequencediagram.graphic.Segment;
import net.sourceforge.plantuml.ugraphic.UGraphic;
import net.sourceforge.plantuml.ugraphic.ULine;
import net.sourceforge.plantuml.ugraphic.URectangle;
import net.sourceforge.plantuml.ugraphic.UStroke;
import net.sourceforge.plantuml.ugraphic.UTranslate;
import net.sourceforge.plantuml.ugraphic.color.HColor;
import net.sourceforge.plantuml.ugraphic.color.HColorNone;
public class RectangleTask {
private final List<Segment> segments;
private final double round;
private final int completion;
public RectangleTask(double startPos, double endPos, double round, int completion, Collection<Segment> paused) {
this.round = round;
this.completion = completion;
if (startPos < endPos) {
this.segments = new ArrayList<Segment>(new Segment(startPos, endPos).cutSegmentIfNeed(paused));
} else {
this.segments = Collections.singletonList(new Segment(startPos, startPos + 1));
}
}
private void draw2hlines(UGraphic ug, double height, ULine hline) {
ug.draw(hline);
ug.apply(UTranslate.dy(height)).draw(hline);
}
private void drawRect(UGraphic ug, int completion, HColor documentBackground, double width, double height) {
if (completion == 100 || completion == 0) {
if (completion == 0)
ug = ug.apply(documentBackground.bg());
final URectangle rect = new URectangle(width, height);
ug.draw(rect);
} else {
final URectangle rect1 = new URectangle(width * completion / 100, height);
ug.draw(rect1);
final URectangle rect2 = new URectangle(width * (100 - completion) / 100, height);
ug.apply(documentBackground.bg()).apply(UTranslate.dx(width * completion / 100)).draw(rect2);
}
}
public void draw(UGraphic ug, double height, HColor documentBackground, boolean oddStart, boolean oddEnd) {
if (round == 0) {
drawWithoutRound(ug, height, documentBackground, oddStart, oddEnd);
return;
}
if (segments.size() != 1) {
drawWithRound(ug, height, documentBackground);
return;
}
assert segments.size() == 1;
assert round > 0;
final Segment segment = segments.get(0);
final double width = segment.getLength();
final URectangle partial = new URectangle(width, height).rounded(round);
if (completion == 100 || completion == 0) {
if (completion == 0)
ug = ug.apply(documentBackground.bg());
if (oddStart && !oddEnd)
ug.apply(UTranslate.dx(segment.getPos1())).draw(PathUtils.UtoRight(width, height, round));
else if (!oddStart && oddEnd)
ug.apply(UTranslate.dx(segment.getPos1())).draw(PathUtils.UtoLeft(width, height, round));
else
ug.apply(UTranslate.dx(segment.getPos1())).draw(partial);
} else {
final double x1 = width * completion / 100;
ug.apply(new HColorNone()).apply(UTranslate.dx(segment.getPos1()))
.draw(PathUtils.UtoLeft(x1, height, round));
ug.apply(documentBackground.bg()).apply(new HColorNone()).apply(UTranslate.dx(segment.getPos1() + x1))
.draw(PathUtils.UtoRight(width * (100 - completion) / 100, height, round));
ug.apply(new HColorNone().bg()).apply(UTranslate.dx(segment.getPos1())).draw(partial);
}
}
private void drawWithRound(UGraphic ug, double height, HColor documentBackground) {
final Segment first = segments.get(0);
ug.apply(UTranslate.dx(first.getPos1())).draw(PathUtils.UtoLeft(first.getLength(), height, round));
for (int i = 1; i < segments.size() - 1; i++) {
final Segment segment = segments.get(i);
drawPartly(ug, segment, height, documentBackground, i);
}
final Segment last = segments.get(segments.size() - 1);
ug.apply(UTranslate.dx(last.getPos1())).draw(PathUtils.UtoRight(last.getLength(), height, round));
drawIntermediateDotted(ug, height);
}
private void drawWithoutRound(UGraphic ug, double height, HColor documentBackground, boolean oddStart,
boolean oddEnd) {
final ULine vline = ULine.vline(height);
for (int i = 0; i < segments.size(); i++) {
final Segment segment = segments.get(i);
drawPartly(ug, segment, height, documentBackground, i);
if (!oddStart && i == 0) {
ug.apply(UTranslate.dx(segment.getPos1())).draw(vline);
}
if (!oddEnd && i == segments.size() - 1) {
ug.apply(UTranslate.dx(segment.getPos2())).draw(vline);
}
}
drawIntermediateDotted(ug, height);
}
private void drawIntermediateDotted(UGraphic ug, double height) {
ug = ug.apply(new UStroke(2, 3, 1));
for (int i = 0; i < segments.size() - 1; i++) {
final double v1 = segments.get(i).getPos2() + 3;
final double v2 = segments.get(i + 1).getPos1() - 3;
if (v2 > v1) {
draw2hlines(ug.apply(UTranslate.dx(v1)), height, ULine.hline(v2 - v1));
}
}
}
private void drawPartly(UGraphic ug, final Segment segment, double height, HColor documentBackground, int i) {
double width = segment.getLength();
if (i != segments.size() - 1) {
width++;
}
if (width > 0) {
drawRect(ug.apply(new HColorNone()).apply(UTranslate.dx(segment.getPos1())), completion, documentBackground,
width, height);
}
double pos1 = segment.getPos1();
double len = segment.getLength();
if (i == 0) {
if (segments.size() > 1) {
len--;
}
} else {
pos1++;
len--;
}
if (len > 0) {
draw2hlines(ug.apply(UTranslate.dx(pos1)), height, ULine.hline(len));
}
}
}

View File

@ -41,6 +41,7 @@ import net.sourceforge.plantuml.cucadiagram.Display;
import net.sourceforge.plantuml.graphic.StringBounder; import net.sourceforge.plantuml.graphic.StringBounder;
import net.sourceforge.plantuml.graphic.UDrawable; import net.sourceforge.plantuml.graphic.UDrawable;
import net.sourceforge.plantuml.project.core.Task; import net.sourceforge.plantuml.project.core.Task;
import net.sourceforge.plantuml.project.core.TaskAttribute;
import net.sourceforge.plantuml.project.lang.CenterBorderColor; import net.sourceforge.plantuml.project.lang.CenterBorderColor;
import net.sourceforge.plantuml.ugraphic.UGraphic; import net.sourceforge.plantuml.ugraphic.UGraphic;
@ -58,7 +59,7 @@ public interface TaskDraw extends UDrawable {
public void drawTitle(UGraphic ug); public void drawTitle(UGraphic ug);
public double getHeightTask(StringBounder stringBounder); public double getFullHeightTask(StringBounder stringBounder);
public double getHeightMax(StringBounder stringBounder); public double getHeightMax(StringBounder stringBounder);
@ -68,5 +69,9 @@ public interface TaskDraw extends UDrawable {
public FingerPrint getFingerPrintNote(StringBounder stringBounder); public FingerPrint getFingerPrintNote(StringBounder stringBounder);
public double getX1(TaskAttribute taskAttribute);
public double getX2(TaskAttribute taskAttribute);
} }

View File

@ -43,9 +43,12 @@ import net.sourceforge.plantuml.graphic.StringBounder;
import net.sourceforge.plantuml.graphic.TextBlock; import net.sourceforge.plantuml.graphic.TextBlock;
import net.sourceforge.plantuml.project.ToTaskDraw; import net.sourceforge.plantuml.project.ToTaskDraw;
import net.sourceforge.plantuml.project.core.Task; import net.sourceforge.plantuml.project.core.Task;
import net.sourceforge.plantuml.project.core.TaskAttribute;
import net.sourceforge.plantuml.project.time.Day; import net.sourceforge.plantuml.project.time.Day;
import net.sourceforge.plantuml.project.timescale.TimeScale; import net.sourceforge.plantuml.project.timescale.TimeScale;
import net.sourceforge.plantuml.style.ClockwiseTopRightBottomLeft;
import net.sourceforge.plantuml.style.SName; import net.sourceforge.plantuml.style.SName;
import net.sourceforge.plantuml.style.Style;
import net.sourceforge.plantuml.style.StyleBuilder; import net.sourceforge.plantuml.style.StyleBuilder;
import net.sourceforge.plantuml.style.StyleSignature; import net.sourceforge.plantuml.style.StyleSignature;
import net.sourceforge.plantuml.ugraphic.UGraphic; import net.sourceforge.plantuml.ugraphic.UGraphic;
@ -67,27 +70,53 @@ public class TaskDrawDiamond extends AbstractTaskDraw {
} }
public double getHeightMax(StringBounder stringBounder) { public double getHeightMax(StringBounder stringBounder) {
return getHeightTask(stringBounder); return getFullHeightTask(stringBounder);
} }
// final UFont font = UFont.serif(11); @Override
// return new FontConfiguration(font, HColorUtils.BLACK, HColorUtils.BLACK, false); protected double getShapeHeight(StringBounder stringBounder) {
// final Style style = getStyle();
// final ClockwiseTopRightBottomLeft padding = style.getPadding();
int result = (int) getFontConfiguration().getFont().getSize2D();
if (result % 2 == 1)
result--;
return result;
}
final public void drawTitle(UGraphic ug) { final public void drawTitle(UGraphic ug) {
final Style style = getStyle();
final ClockwiseTopRightBottomLeft margin = style.getMargin();
final ClockwiseTopRightBottomLeft padding = style.getPadding();
ug = ug.apply(UTranslate.dy(margin.getTop()));
final TextBlock title = Display.getWithNewlines(prettyDisplay).create(getFontConfiguration(), final TextBlock title = Display.getWithNewlines(prettyDisplay).create(getFontConfiguration(),
HorizontalAlignment.LEFT, new SpriteContainerEmpty()); HorizontalAlignment.LEFT, new SpriteContainerEmpty());
final StringBounder stringBounder = ug.getStringBounder(); final StringBounder stringBounder = ug.getStringBounder();
final double titleHeight = title.calculateDimension(stringBounder).getHeight(); final double titleHeight = title.calculateDimension(stringBounder).getHeight();
final double h = (margin + getShapeHeight(stringBounder) - titleHeight) / 2; final double h = (getShapeHeight(stringBounder) - titleHeight) / 2;
final double endingPosition = timeScale.getStartingPosition(start) + getHeightTask(stringBounder);
title.drawU(ug.apply(new UTranslate(endingPosition, h))); final double x1 = timeScale.getStartingPosition(start);
final double x2 = timeScale.getEndingPosition(start);
final double width = getShapeHeight(ug.getStringBounder());
final double delta = x2 - x1 - width;
final double x = x2 - delta / 2 + padding.getLeft();
title.drawU(ug.apply(new UTranslate(x, h)));
} }
public void drawU(UGraphic ug1) { public void drawU(UGraphic ug) {
final double startPos = timeScale.getStartingPosition(start);
ug1 = applyColors(ug1); final Style style = getStyle();
UGraphic ug2 = ug1.apply(new UTranslate(startPos + margin, margin)); final ClockwiseTopRightBottomLeft margin = style.getMargin();
drawShape(ug2); ug = ug.apply(UTranslate.dy(margin.getTop()));
final double x1 = timeScale.getStartingPosition(start);
final double x2 = timeScale.getEndingPosition(start);
final double width = getShapeHeight(ug.getStringBounder());
final double delta = x2 - x1 - width;
drawShape(applyColors(ug).apply(UTranslate.dx(x1 + delta / 2)));
} }
private UGraphic applyColors(UGraphic ug) { private UGraphic applyColors(UGraphic ug) {
@ -106,13 +135,13 @@ public class TaskDrawDiamond extends AbstractTaskDraw {
} }
public FingerPrint getFingerPrint(StringBounder stringBounder) { public FingerPrint getFingerPrint(StringBounder stringBounder) {
final double h = getHeightTask(stringBounder); final double h = getFullHeightTask(stringBounder);
final double startPos = timeScale.getStartingPosition(start); final double startPos = timeScale.getStartingPosition(start);
return new FingerPrint(startPos, getY(stringBounder), startPos + h, getY(stringBounder) + h); return new FingerPrint(startPos, getY(stringBounder), startPos + h, getY(stringBounder) + h);
} }
private UShape getDiamond(StringBounder stringBounder) { private UShape getDiamond(StringBounder stringBounder) {
final double h = getHeightTask(stringBounder) - 2 * margin; final double h = getShapeHeight(stringBounder);
final UPolygon result = new UPolygon(); final UPolygon result = new UPolygon();
result.addPoint(h / 2, 0); result.addPoint(h / 2, 0);
result.addPoint(h, h / 2); result.addPoint(h, h / 2);
@ -121,4 +150,20 @@ public class TaskDrawDiamond extends AbstractTaskDraw {
return result; return result;
} }
public double getX1(TaskAttribute taskAttribute) {
final double x1 = timeScale.getStartingPosition(start);
final double x2 = timeScale.getEndingPosition(start);
final double width = getShapeHeight(null);
final double delta = x2 - x1 - width;
return x1 + delta;
}
public double getX2(TaskAttribute taskAttribute) {
final double x1 = timeScale.getStartingPosition(start);
final double x2 = timeScale.getEndingPosition(start);
final double width = getShapeHeight(null);
final double delta = x2 - x1 - width;
return x2 - delta;
}
} }

View File

@ -36,6 +36,7 @@
package net.sourceforge.plantuml.project.draw; package net.sourceforge.plantuml.project.draw;
import java.awt.geom.Dimension2D; import java.awt.geom.Dimension2D;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.TreeSet; import java.util.TreeSet;
@ -54,9 +55,12 @@ import net.sourceforge.plantuml.graphic.TextBlock;
import net.sourceforge.plantuml.project.GanttConstraint; import net.sourceforge.plantuml.project.GanttConstraint;
import net.sourceforge.plantuml.project.ToTaskDraw; import net.sourceforge.plantuml.project.ToTaskDraw;
import net.sourceforge.plantuml.project.core.Task; import net.sourceforge.plantuml.project.core.Task;
import net.sourceforge.plantuml.project.core.TaskAttribute;
import net.sourceforge.plantuml.project.core.TaskImpl; import net.sourceforge.plantuml.project.core.TaskImpl;
import net.sourceforge.plantuml.project.time.Day; import net.sourceforge.plantuml.project.time.Day;
import net.sourceforge.plantuml.project.timescale.TimeScale; import net.sourceforge.plantuml.project.timescale.TimeScale;
import net.sourceforge.plantuml.sequencediagram.graphic.Segment;
import net.sourceforge.plantuml.style.ClockwiseTopRightBottomLeft;
import net.sourceforge.plantuml.style.PName; import net.sourceforge.plantuml.style.PName;
import net.sourceforge.plantuml.style.SName; import net.sourceforge.plantuml.style.SName;
import net.sourceforge.plantuml.style.Style; import net.sourceforge.plantuml.style.Style;
@ -82,7 +86,7 @@ public class TaskDrawRegular extends AbstractTaskDraw {
private final Collection<GanttConstraint> constraints; private final Collection<GanttConstraint> constraints;
private final ISkinParam skinParam; private final ISkinParam skinParam;
private final double margin = 2; // private final double margin = 2;
public TaskDrawRegular(TimeScale timeScale, double y, String prettyDisplay, Day start, Day end, boolean oddStart, public TaskDrawRegular(TimeScale timeScale, double y, String prettyDisplay, Day start, Day end, boolean oddStart,
boolean oddEnd, ISkinParam skinParam, Task task, ToTaskDraw toTaskDraw, boolean oddEnd, ISkinParam skinParam, Task task, ToTaskDraw toTaskDraw,
@ -103,12 +107,23 @@ public class TaskDrawRegular extends AbstractTaskDraw {
} }
} }
@Override
protected double getShapeHeight(StringBounder stringBounder) {
final Style style = getStyle();
final ClockwiseTopRightBottomLeft padding = style.getPadding();
return padding.getTop() + getTextBlock().calculateDimension(stringBounder).getHeight() + padding.getBottom();
}
public void drawTitle(UGraphic ug) { public void drawTitle(UGraphic ug) {
final TextBlock title = Display.getWithNewlines(prettyDisplay).create(getFontConfiguration(), final TextBlock title = getTextBlock();
HorizontalAlignment.LEFT, new SpriteContainerEmpty());
final StringBounder stringBounder = ug.getStringBounder(); final StringBounder stringBounder = ug.getStringBounder();
final Dimension2D dim = title.calculateDimension(stringBounder); final Dimension2D dim = title.calculateDimension(stringBounder);
final double h = (margin + getShapeHeight(stringBounder) - dim.getHeight()) / 2;
final Style style = getStyleSignature().getMergedStyle(getStyleBuilder());
final ClockwiseTopRightBottomLeft margin = style.getMargin();
final ClockwiseTopRightBottomLeft padding = style.getPadding();
ug = ug.apply(UTranslate.dy(margin.getTop() + padding.getTop()));
final double pos1 = timeScale.getStartingPosition(start) + 6; final double pos1 = timeScale.getStartingPosition(start) + 6;
final double pos2 = timeScale.getEndingPosition(end) - 6; final double pos2 = timeScale.getEndingPosition(end) - 6;
final double pos; final double pos;
@ -116,7 +131,12 @@ public class TaskDrawRegular extends AbstractTaskDraw {
pos = pos1; pos = pos1;
else else
pos = getOutPosition(pos2); pos = getOutPosition(pos2);
title.drawU(ug.apply(new UTranslate(pos, h))); title.drawU(ug.apply(UTranslate.dx(pos)));
}
private TextBlock getTextBlock() {
return Display.getWithNewlines(prettyDisplay).create(getFontConfiguration(), HorizontalAlignment.LEFT,
new SpriteContainerEmpty());
} }
private double getOutPosition(double pos2) { private double getOutPosition(double pos2) {
@ -142,14 +162,16 @@ public class TaskDrawRegular extends AbstractTaskDraw {
public void drawU(UGraphic ug) { public void drawU(UGraphic ug) {
final double startPos = timeScale.getStartingPosition(start); final double startPos = timeScale.getStartingPosition(start);
drawNote(ug.apply((new UTranslate(startPos + margin, getYNotePosition(ug.getStringBounder()))))); drawNote(ug.apply((new UTranslate(startPos, getYNotePosition(ug.getStringBounder())))));
ug = applyColors(ug).apply(new UTranslate(margin, margin)); ug = applyColors(ug);
drawShape(ug); drawShape(ug);
} }
private double getYNotePosition(StringBounder stringBounder) { private double getYNotePosition(StringBounder stringBounder) {
return getShapeHeight(stringBounder) + margin * 3; final Style style = getStyle();
final ClockwiseTopRightBottomLeft margin = style.getMargin();
return margin.getTop() + getShapeHeight(stringBounder) + margin.getBottom();
} }
private void drawNote(UGraphic ug) { private void drawNote(UGraphic ug) {
@ -162,7 +184,7 @@ public class TaskDrawRegular extends AbstractTaskDraw {
public double getHeightMax(StringBounder stringBounder) { public double getHeightMax(StringBounder stringBounder) {
if (note == null) { if (note == null) {
return getHeightTask(stringBounder); return getFullHeightTask(stringBounder);
} }
return getYNotePosition(stringBounder) + getOpaleNote().calculateDimension(stringBounder).getHeight(); return getYNotePosition(stringBounder) + getOpaleNote().calculateDimension(stringBounder).getHeight();
} }
@ -186,7 +208,7 @@ public class TaskDrawRegular extends AbstractTaskDraw {
} }
public FingerPrint getFingerPrint(StringBounder stringBounder) { public FingerPrint getFingerPrint(StringBounder stringBounder) {
final double h = getHeightTask(stringBounder); final double h = getFullHeightTask(stringBounder);
final double startPos = timeScale.getStartingPosition(start); final double startPos = timeScale.getStartingPosition(start);
final double endPos = timeScale.getEndingPosition(end); final double endPos = timeScale.getEndingPosition(end);
return new FingerPrint(startPos, getY(stringBounder), endPos - startPos, h); return new FingerPrint(startPos, getY(stringBounder), endPos - startPos, h);
@ -210,37 +232,101 @@ public class TaskDrawRegular extends AbstractTaskDraw {
return ug.apply(getLineColor()).apply(getBackgroundColor().bg()); return ug.apply(getLineColor()).apply(getBackgroundColor().bg());
} }
private void drawShape(UGraphic ug) { public double getX1(TaskAttribute taskAttribute) {
final double startPos = timeScale.getStartingPosition(start); final Style style = getStyleSignature().getMergedStyle(getStyleBuilder());
final double endPos = timeScale.getEndingPosition(end); final ClockwiseTopRightBottomLeft margin = style.getMargin();
final double startPos = taskAttribute == TaskAttribute.START ? timeScale.getStartingPosition(start)
: timeScale.getStartingPosition(end) + margin.getLeft();
return startPos;
}
double fullLength = endPos - startPos - 2 * margin; public double getX2(TaskAttribute taskAttribute) {
final Style style = getStyleSignature().getMergedStyle(getStyleBuilder());
final ClockwiseTopRightBottomLeft margin = style.getMargin();
final double endPos = taskAttribute == TaskAttribute.START ? timeScale.getEndingPosition(start)
: timeScale.getEndingPosition(end) - margin.getLeft();
return endPos;
}
private void drawShape(UGraphic ug) {
final Style style = getStyleSignature().getMergedStyle(getStyleBuilder());
final ClockwiseTopRightBottomLeft margin = style.getMargin();
final double startPos = timeScale.getStartingPosition(start) + margin.getLeft();
final double endPos = timeScale.getEndingPosition(end) - margin.getRight();
if (url != null) {
ug.startUrl(url);
}
ug = ug.apply(UTranslate.dy(margin.getTop()));
final StringBounder stringBounder = ug.getStringBounder();
final double round = style.value(PName.RoundCorner).asDouble();
final Collection<Segment> off = new ArrayList<Segment>();
for (Day pause : paused) {
final double x1 = timeScale.getStartingPosition(pause);
final double x2 = timeScale.getEndingPosition(pause);
off.add(new Segment(x1, x2));
}
final HColor back2 = StyleSignature.of(SName.root, SName.document, SName.ganttDiagram)
.getMergedStyle(getStyleBuilder()).value(PName.BackGroundColor).asColor(getColorSet());
final RectangleTask rectangleTask = new RectangleTask(startPos, endPos, round, completion, off);
rectangleTask.draw(ug, getShapeHeight(stringBounder), back2, oddStart, oddEnd);
if (url != null) {
ug.closeUrl();
}
}
private void drawShapeOld(UGraphic ug) {
final Style style = getStyleSignature().getMergedStyle(getStyleBuilder());
final ClockwiseTopRightBottomLeft margin = style.getMargin();
final double startPos = timeScale.getStartingPosition(start) + margin.getLeft();
final double endPos = timeScale.getEndingPosition(end) - margin.getRight();
double fullLength = endPos - startPos;
if (fullLength < 3) { if (fullLength < 3) {
fullLength = 3; fullLength = 3;
} }
if (url != null) { if (url != null) {
ug.startUrl(url); ug.startUrl(url);
} }
ug = ug.apply(UTranslate.dy(margin.getTop()));
final StringBounder stringBounder = ug.getStringBounder(); final StringBounder stringBounder = ug.getStringBounder();
final double round = style.value(PName.RoundCorner).asDouble();
if (oddStart && !oddEnd) { if (oddStart && !oddEnd) {
ug.apply(UTranslate.dx(startPos)).draw(PathUtils.UtoRight(fullLength, getShapeHeight(stringBounder))); ug.apply(UTranslate.dx(startPos))
.draw(PathUtils.UtoRight(fullLength, getShapeHeight(stringBounder), round));
} else if (!oddStart && oddEnd) { } else if (!oddStart && oddEnd) {
ug.apply(UTranslate.dx(startPos)).draw(PathUtils.UtoLeft(fullLength, getShapeHeight(stringBounder))); ug.apply(UTranslate.dx(startPos)).draw(PathUtils.UtoLeft(fullLength, getShapeHeight(stringBounder), round));
} else { } else {
final URectangle full = new URectangle(fullLength, getShapeHeight(stringBounder)).rounded(8); final URectangle full = new URectangle(fullLength, getShapeHeight(stringBounder)).rounded(round);
if (completion == 100) { if (completion == 100) {
ug.apply(UTranslate.dx(startPos)).draw(full); ug.apply(UTranslate.dx(startPos)).draw(full);
} else { } else {
final double partialLength = fullLength * completion / 100.; final double partialLength = fullLength * completion / 100.;
ug.apply(UTranslate.dx(startPos)).apply(HColorUtils.WHITE).apply(HColorUtils.WHITE.bg()).draw(full); ug.apply(UTranslate.dx(startPos)).apply(HColorUtils.WHITE).apply(HColorUtils.WHITE.bg()).draw(full);
if (partialLength > 2) { if (partialLength > 2) {
final URectangle partial = new URectangle(partialLength, getShapeHeight(stringBounder)).rounded(8); final URectangle partial = new URectangle(partialLength, getShapeHeight(stringBounder))
.rounded(round);
ug.apply(UTranslate.dx(startPos)).apply(new HColorNone()).draw(partial); ug.apply(UTranslate.dx(startPos)).apply(new HColorNone()).draw(partial);
} }
if (partialLength > 10 && partialLength < fullLength - 10) { if (partialLength > 10 && partialLength < fullLength - 10) {
final URectangle patch = new URectangle(8, getShapeHeight(stringBounder)); final URectangle patch = new URectangle(round, getShapeHeight(stringBounder));
ug.apply(UTranslate.dx(startPos)).apply(new HColorNone()).apply(UTranslate.dx(partialLength - 8)) ug.apply(UTranslate.dx(startPos)).apply(new HColorNone())
.draw(patch); .apply(UTranslate.dx(partialLength - round)).draw(patch);
} }
ug.apply(UTranslate.dx(startPos)).apply(new HColorNone().bg()).draw(full); ug.apply(UTranslate.dx(startPos)).apply(new HColorNone().bg()).draw(full);
} }

View File

@ -45,6 +45,7 @@ import net.sourceforge.plantuml.graphic.StringBounder;
import net.sourceforge.plantuml.graphic.TextBlock; import net.sourceforge.plantuml.graphic.TextBlock;
import net.sourceforge.plantuml.graphic.TextBlockUtils; import net.sourceforge.plantuml.graphic.TextBlockUtils;
import net.sourceforge.plantuml.project.core.Task; import net.sourceforge.plantuml.project.core.Task;
import net.sourceforge.plantuml.project.core.TaskAttribute;
import net.sourceforge.plantuml.project.lang.CenterBorderColor; import net.sourceforge.plantuml.project.lang.CenterBorderColor;
import net.sourceforge.plantuml.project.time.Day; import net.sourceforge.plantuml.project.time.Day;
import net.sourceforge.plantuml.project.timescale.TimeScale; import net.sourceforge.plantuml.project.timescale.TimeScale;
@ -152,12 +153,12 @@ public class TaskDrawSeparator implements TaskDraw {
} }
public FingerPrint getFingerPrint(StringBounder stringBounder) { public FingerPrint getFingerPrint(StringBounder stringBounder) {
final double h = getHeightTask(stringBounder); final double h = getFullHeightTask(stringBounder);
final double end = timeScale.getEndingPosition(max); final double end = timeScale.getEndingPosition(max);
return new FingerPrint(0, y, end, y + h); return new FingerPrint(0, y, end, y + h);
} }
public double getHeightTask(StringBounder stringBounder) { public double getFullHeightTask(StringBounder stringBounder) {
final ClockwiseTopRightBottomLeft padding = getStyle().getPadding(); final ClockwiseTopRightBottomLeft padding = getStyle().getPadding();
final ClockwiseTopRightBottomLeft margin = getStyle().getMargin(); final ClockwiseTopRightBottomLeft margin = getStyle().getMargin();
return margin.getTop() + padding.getTop() + getTextHeight(stringBounder) + padding.getBottom() return margin.getTop() + padding.getTop() + getTextHeight(stringBounder) + padding.getBottom()
@ -196,7 +197,15 @@ public class TaskDrawSeparator implements TaskDraw {
} }
public double getHeightMax(StringBounder stringBounder) { public double getHeightMax(StringBounder stringBounder) {
return getHeightTask(stringBounder); return getFullHeightTask(stringBounder);
}
public double getX1(TaskAttribute taskAttribute) {
throw new UnsupportedOperationException();
}
public double getX2(TaskAttribute taskAttribute) {
throw new UnsupportedOperationException();
} }
} }

View File

@ -99,23 +99,27 @@ public class Segment {
Collections.sort(sortedDelay, new SortPos1()); Collections.sort(sortedDelay, new SortPos1());
final List<Segment> result2 = new ArrayList<Segment>(); final List<Segment> result2 = new ArrayList<Segment>();
double pendingStart = pos1; double pendingStart = pos1;
for (Segment d : sortedDelay) { for (Segment pause : sortedDelay) {
if (d.pos1 <= pendingStart) { if (pause.pos1 == pendingStart) {
pendingStart = pause.pos2;
continue; continue;
} }
if (d.pos1 > this.pos2) { if (pause.pos1 < pendingStart) {
result2.add(new Segment(pendingStart, this.pos2)); continue;
}
if (pause.pos1 > this.pos2) {
if (pendingStart < this.pos2)
result2.add(new Segment(pendingStart, this.pos2));
return Collections.unmodifiableCollection(result2); return Collections.unmodifiableCollection(result2);
} }
// if (this.contains(d) == false) { if (this.contains(pause)) {
// throw new IllegalStateException(); if (pendingStart < pause.pos1)
// } result2.add(new Segment(pendingStart, pause.pos1));
if (this.contains(d)) { pendingStart = pause.pos2;
result2.add(new Segment(pendingStart, d.pos1));
pendingStart = d.pos2;
} }
} }
result2.add(new Segment(pendingStart, this.pos2)); if (pendingStart < this.pos2)
result2.add(new Segment(pendingStart, this.pos2));
return Collections.unmodifiableCollection(result2); return Collections.unmodifiableCollection(result2);
} }

View File

@ -49,12 +49,16 @@ public class HColorSimple extends HColorAbstract implements HColor {
@Override @Override
public String toString() { public String toString() {
if (color.getAlpha() == 0) { if (isTransparent()) {
return "transparent"; return "transparent";
} }
return color.toString() + " alpha=" + color.getAlpha() + " monochrome=" + monochrome; return color.toString() + " alpha=" + color.getAlpha() + " monochrome=" + monochrome;
} }
public boolean isTransparent() {
return color.getAlpha() == 0;
}
@Override @Override
public boolean equals(Object other) { public boolean equals(Object other) {
if (other instanceof HColorSimple == false) { if (other instanceof HColorSimple == false) {

View File

@ -146,6 +146,9 @@ public class HColorUtils {
if (back instanceof HColorBackground && ((HColorBackground) back).getBack() == TRANSPARENT) { if (back instanceof HColorBackground && ((HColorBackground) back).getBack() == TRANSPARENT) {
return true; return true;
} }
if (back instanceof HColorSimple && ((HColorSimple) back).isTransparent()) {
return true;
}
return false; return false;
} }

View File

@ -60,6 +60,7 @@ import net.sourceforge.plantuml.ugraphic.UShape;
import net.sourceforge.plantuml.ugraphic.UText; import net.sourceforge.plantuml.ugraphic.UText;
import net.sourceforge.plantuml.ugraphic.color.ColorMapper; import net.sourceforge.plantuml.ugraphic.color.ColorMapper;
import net.sourceforge.plantuml.ugraphic.color.HColor; import net.sourceforge.plantuml.ugraphic.color.HColor;
import net.sourceforge.plantuml.ugraphic.color.HColorUtils;
public class DriverTextEps implements UDriver<EpsGraphics> { public class DriverTextEps implements UDriver<EpsGraphics> {
@ -84,12 +85,16 @@ public class DriverTextEps implements UDriver<EpsGraphics> {
final UText shape = (UText) ushape; final UText shape = (UText) ushape;
final FontConfiguration fontConfiguration = shape.getFontConfiguration();
if (HColorUtils.isTransparent(fontConfiguration.getColor())) {
return;
}
if (strategy == EpsStrategy.WITH_MACRO_AND_TEXT) { if (strategy == EpsStrategy.WITH_MACRO_AND_TEXT) {
drawAsText(shape, x, y, param, eps, mapper); drawAsText(shape, x, y, param, eps, mapper);
return; return;
} }
final FontConfiguration fontConfiguration = shape.getFontConfiguration();
final UFont font = fontConfiguration.getFont(); final UFont font = fontConfiguration.getFont();
final TextLayout textLayout = new TextLayout(shape.getText(), font.getUnderlayingFont(), fontRenderContext); final TextLayout textLayout = new TextLayout(shape.getText(), font.getUnderlayingFont(), fontRenderContext);

View File

@ -61,6 +61,7 @@ import net.sourceforge.plantuml.ugraphic.UText;
import net.sourceforge.plantuml.ugraphic.color.ColorMapper; import net.sourceforge.plantuml.ugraphic.color.ColorMapper;
import net.sourceforge.plantuml.ugraphic.color.HColor; import net.sourceforge.plantuml.ugraphic.color.HColor;
import net.sourceforge.plantuml.ugraphic.color.HColorGradient; import net.sourceforge.plantuml.ugraphic.color.HColorGradient;
import net.sourceforge.plantuml.ugraphic.color.HColorUtils;
public class DriverTextG2d implements UDriver<Graphics2D> { public class DriverTextG2d implements UDriver<Graphics2D> {
@ -73,6 +74,10 @@ public class DriverTextG2d implements UDriver<Graphics2D> {
public void draw(UShape ushape, double x, double y, ColorMapper mapper, UParam param, Graphics2D g2d) { public void draw(UShape ushape, double x, double y, ColorMapper mapper, UParam param, Graphics2D g2d) {
final UText shape = (UText) ushape; final UText shape = (UText) ushape;
final FontConfiguration fontConfiguration = shape.getFontConfiguration(); final FontConfiguration fontConfiguration = shape.getFontConfiguration();
if (HColorUtils.isTransparent(fontConfiguration.getColor())) {
return;
}
final String text = shape.getText(); final String text = shape.getText();
final List<StyledString> strings = StyledString.build(text); final List<StyledString> strings = StyledString.build(text);
@ -82,8 +87,7 @@ public class DriverTextG2d implements UDriver<Graphics2D> {
for (StyledString styledString : strings) { for (StyledString styledString : strings) {
final FontConfiguration fc = styledString.getStyle() == FontStyle.BOLD ? fontConfiguration.bold() final FontConfiguration fc = styledString.getStyle() == FontStyle.BOLD ? fontConfiguration.bold()
: fontConfiguration; : fontConfiguration;
final Dimension2D dim = calculateDimension( final Dimension2D dim = calculateDimension(FileFormat.PNG.getDefaultStringBounder(), fc.getFont(),
FileFormat.PNG.getDefaultStringBounder(), fc.getFont(),
styledString.getText()); styledString.getText());
printSingleText(g2d, fc, styledString.getText(), x, y, mapper, param); printSingleText(g2d, fc, styledString.getText(), x, y, mapper, param);
x += dim.getWidth(); x += dim.getWidth();
@ -109,8 +113,7 @@ public class DriverTextG2d implements UDriver<Graphics2D> {
} else if (orientation == 0) { } else if (orientation == 0) {
final Dimension2D dimBack = calculateDimension( final Dimension2D dimBack = calculateDimension(FileFormat.PNG.getDefaultStringBounder(), font, text);
FileFormat.PNG.getDefaultStringBounder(), font, text);
if (fontConfiguration.containsStyle(FontStyle.BACKCOLOR)) { if (fontConfiguration.containsStyle(FontStyle.BACKCOLOR)) {
final Rectangle2D.Double area = new Rectangle2D.Double(x, y - dimBack.getHeight() + 1.5, final Rectangle2D.Double area = new Rectangle2D.Double(x, y - dimBack.getHeight() + 1.5,
dimBack.getWidth(), dimBack.getHeight()); dimBack.getWidth(), dimBack.getHeight());
@ -140,16 +143,14 @@ public class DriverTextG2d implements UDriver<Graphics2D> {
if (extended != null) { if (extended != null) {
g2d.setColor(mapper.toColor(extended)); g2d.setColor(mapper.toColor(extended));
} }
final Dimension2D dim = calculateDimension( final Dimension2D dim = calculateDimension(FileFormat.PNG.getDefaultStringBounder(), font, text);
FileFormat.PNG.getDefaultStringBounder(), font, text);
final int ypos = (int) (y + 2.5); final int ypos = (int) (y + 2.5);
g2d.setStroke(new BasicStroke((float) 1)); g2d.setStroke(new BasicStroke((float) 1));
g2d.drawLine((int) x, ypos, (int) (x + dim.getWidth()), ypos); g2d.drawLine((int) x, ypos, (int) (x + dim.getWidth()), ypos);
g2d.setStroke(new BasicStroke()); g2d.setStroke(new BasicStroke());
} }
if (fontConfiguration.containsStyle(FontStyle.WAVE)) { if (fontConfiguration.containsStyle(FontStyle.WAVE)) {
final Dimension2D dim = calculateDimension( final Dimension2D dim = calculateDimension(FileFormat.PNG.getDefaultStringBounder(), font, text);
FileFormat.PNG.getDefaultStringBounder(), font, text);
final int ypos = (int) (y + 2.5) - 1; final int ypos = (int) (y + 2.5) - 1;
if (extended != null) { if (extended != null) {
g2d.setColor(mapper.toColor(extended)); g2d.setColor(mapper.toColor(extended));
@ -160,8 +161,7 @@ public class DriverTextG2d implements UDriver<Graphics2D> {
} }
} }
if (fontConfiguration.containsStyle(FontStyle.STRIKE)) { if (fontConfiguration.containsStyle(FontStyle.STRIKE)) {
final Dimension2D dim = calculateDimension( final Dimension2D dim = calculateDimension(FileFormat.PNG.getDefaultStringBounder(), font, text);
FileFormat.PNG.getDefaultStringBounder(), font, text);
final FontMetrics fm = g2d.getFontMetrics(font.getUnderlayingFont()); final FontMetrics fm = g2d.getFontMetrics(font.getUnderlayingFont());
final int ypos = (int) (y - fm.getDescent() - 0.5); final int ypos = (int) (y - fm.getDescent() - 0.5);
if (extended != null) { if (extended != null) {

View File

@ -52,6 +52,7 @@ import net.sourceforge.plantuml.ugraphic.UText;
import net.sourceforge.plantuml.ugraphic.color.ColorMapper; import net.sourceforge.plantuml.ugraphic.color.ColorMapper;
import net.sourceforge.plantuml.ugraphic.color.HColor; import net.sourceforge.plantuml.ugraphic.color.HColor;
import net.sourceforge.plantuml.ugraphic.color.HColorGradient; import net.sourceforge.plantuml.ugraphic.color.HColorGradient;
import net.sourceforge.plantuml.ugraphic.color.HColorUtils;
public class DriverTextSvg implements UDriver<SvgGraphics> { public class DriverTextSvg implements UDriver<SvgGraphics> {
@ -72,6 +73,9 @@ public class DriverTextSvg implements UDriver<SvgGraphics> {
final UText shape = (UText) ushape; final UText shape = (UText) ushape;
final FontConfiguration fontConfiguration = shape.getFontConfiguration(); final FontConfiguration fontConfiguration = shape.getFontConfiguration();
if (HColorUtils.isTransparent(fontConfiguration.getColor())) {
return;
}
final UFont font = fontConfiguration.getFont(); final UFont font = fontConfiguration.getFont();
String fontWeight = null; String fontWeight = null;
if (fontConfiguration.containsStyle(FontStyle.BOLD) || font.isBold()) { if (fontConfiguration.containsStyle(FontStyle.BOLD) || font.isBold()) {

View File

@ -80,7 +80,7 @@ public class Version {
} }
public static int beta() { public static int beta() {
final int beta = 3; final int beta = 4;
return beta; return beta;
} }

View File

@ -5,7 +5,7 @@ public class A0003_TestResult {
/* /*
""" """
DPI: 96 DPI: 96
dimension: [ 367.7447 ; 78.0000 ] dimension: [ 367.7447 ; 76.0000 ]
scaleFactor: 2.0000 scaleFactor: 2.0000
seed: -6040919743496430850 seed: -6040919743496430850
svgLinkTarget: _top svgLinkTarget: _top
@ -14,7 +14,7 @@ preserveAspectRatio: none
RECTANGLE: RECTANGLE:
pt1: [ 8.0000 ; 29.0000 ] pt1: [ 8.0000 ; 29.0000 ]
pt2: [ 16.0000 ; 61.0000 ] pt2: [ 16.0000 ; 59.0000 ]
xCorner: 0 xCorner: 0
yCorner: 0 yCorner: 0
stroke: 0.0-0.0-1.0 stroke: 0.0-0.0-1.0
@ -24,7 +24,7 @@ RECTANGLE:
RECTANGLE: RECTANGLE:
pt1: [ 36.0000 ; 29.0000 ] pt1: [ 36.0000 ; 29.0000 ]
pt2: [ 44.0000 ; 61.0000 ] pt2: [ 44.0000 ; 59.0000 ]
xCorner: 0 xCorner: 0
yCorner: 0 yCorner: 0
stroke: 0.0-0.0-1.0 stroke: 0.0-0.0-1.0
@ -34,7 +34,7 @@ RECTANGLE:
RECTANGLE: RECTANGLE:
pt1: [ 44.0000 ; 29.0000 ] pt1: [ 44.0000 ; 29.0000 ]
pt2: [ 72.0000 ; 61.0000 ] pt2: [ 72.0000 ; 59.0000 ]
xCorner: 0 xCorner: 0
yCorner: 0 yCorner: 0
stroke: 0.0-0.0-1.0 stroke: 0.0-0.0-1.0
@ -44,7 +44,7 @@ RECTANGLE:
RECTANGLE: RECTANGLE:
pt1: [ 92.0000 ; 29.0000 ] pt1: [ 92.0000 ; 29.0000 ]
pt2: [ 100.0000 ; 61.0000 ] pt2: [ 100.0000 ; 59.0000 ]
xCorner: 0 xCorner: 0
yCorner: 0 yCorner: 0
stroke: 0.0-0.0-1.0 stroke: 0.0-0.0-1.0
@ -54,7 +54,7 @@ RECTANGLE:
RECTANGLE: RECTANGLE:
pt1: [ 120.0000 ; 29.0000 ] pt1: [ 120.0000 ; 29.0000 ]
pt2: [ 128.0000 ; 61.0000 ] pt2: [ 128.0000 ; 59.0000 ]
xCorner: 0 xCorner: 0
yCorner: 0 yCorner: 0
stroke: 0.0-0.0-1.0 stroke: 0.0-0.0-1.0
@ -64,7 +64,7 @@ RECTANGLE:
RECTANGLE: RECTANGLE:
pt1: [ 148.0000 ; 29.0000 ] pt1: [ 148.0000 ; 29.0000 ]
pt2: [ 156.0000 ; 61.0000 ] pt2: [ 156.0000 ; 59.0000 ]
xCorner: 0 xCorner: 0
yCorner: 0 yCorner: 0
stroke: 0.0-0.0-1.0 stroke: 0.0-0.0-1.0
@ -122,49 +122,49 @@ TEXT:
LINE: LINE:
pt1: [ 16.0000 ; 16.0000 ] pt1: [ 16.0000 ; 16.0000 ]
pt2: [ 16.0000 ; 61.0000 ] pt2: [ 16.0000 ; 59.0000 ]
stroke: 0.0-0.0-1.0 stroke: 0.0-0.0-1.0
shadow: 0 shadow: 0
color: ffc0c0c0 color: ffc0c0c0
LINE: LINE:
pt1: [ 44.0000 ; 16.0000 ] pt1: [ 44.0000 ; 16.0000 ]
pt2: [ 44.0000 ; 61.0000 ] pt2: [ 44.0000 ; 59.0000 ]
stroke: 0.0-0.0-1.0 stroke: 0.0-0.0-1.0
shadow: 0 shadow: 0
color: ffc0c0c0 color: ffc0c0c0
LINE: LINE:
pt1: [ 72.0000 ; 16.0000 ] pt1: [ 72.0000 ; 16.0000 ]
pt2: [ 72.0000 ; 61.0000 ] pt2: [ 72.0000 ; 59.0000 ]
stroke: 0.0-0.0-1.0 stroke: 0.0-0.0-1.0
shadow: 0 shadow: 0
color: ffc0c0c0 color: ffc0c0c0
LINE: LINE:
pt1: [ 100.0000 ; 16.0000 ] pt1: [ 100.0000 ; 16.0000 ]
pt2: [ 100.0000 ; 61.0000 ] pt2: [ 100.0000 ; 59.0000 ]
stroke: 0.0-0.0-1.0 stroke: 0.0-0.0-1.0
shadow: 0 shadow: 0
color: ffc0c0c0 color: ffc0c0c0
LINE: LINE:
pt1: [ 128.0000 ; 16.0000 ] pt1: [ 128.0000 ; 16.0000 ]
pt2: [ 128.0000 ; 61.0000 ] pt2: [ 128.0000 ; 59.0000 ]
stroke: 0.0-0.0-1.0 stroke: 0.0-0.0-1.0
shadow: 0 shadow: 0
color: ffc0c0c0 color: ffc0c0c0
LINE: LINE:
pt1: [ 156.0000 ; 16.0000 ] pt1: [ 156.0000 ; 16.0000 ]
pt2: [ 156.0000 ; 61.0000 ] pt2: [ 156.0000 ; 59.0000 ]
stroke: 0.0-0.0-1.0 stroke: 0.0-0.0-1.0
shadow: 0 shadow: 0
color: ffc0c0c0 color: ffc0c0c0
LINE: LINE:
pt1: [ 168.0000 ; 16.0000 ] pt1: [ 168.0000 ; 16.0000 ]
pt2: [ 168.0000 ; 61.0000 ] pt2: [ 168.0000 ; 59.0000 ]
stroke: 0.0-0.0-1.0 stroke: 0.0-0.0-1.0
shadow: 0 shadow: 0
color: ffc0c0c0 color: ffc0c0c0
@ -228,25 +228,25 @@ LINE:
color: ffc0c0c0 color: ffc0c0c0
LINE: LINE:
pt1: [ 132.0000 ; 45.0000 ] pt1: [ 131.0000 ; 42.0000 ]
pt2: [ 132.0000 ; 53.0000 ] pt2: [ 131.0000 ; 51.5000 ]
stroke: 0.0-0.0-1.5 stroke: 0.0-0.0-1.5
shadow: 0 shadow: 0
color: ffa80036 color: ffa80036
LINE: LINE:
pt1: [ 132.0000 ; 53.0000 ] pt1: [ 131.0000 ; 51.5000 ]
pt2: [ 140.0000 ; 53.0000 ] pt2: [ 139.0000 ; 51.5000 ]
stroke: 0.0-0.0-1.5 stroke: 0.0-0.0-1.5
shadow: 0 shadow: 0
color: ffa80036 color: ffa80036
POLYGON: POLYGON:
points: points:
- [ 136.0000 ; 49.0000 ] - [ 135.0000 ; 47.5000 ]
- [ 136.0000 ; 53.0000 ] - [ 135.0000 ; 51.5000 ]
- [ 136.0000 ; 57.0000 ] - [ 135.0000 ; 55.5000 ]
- [ 140.0000 ; 53.0000 ] - [ 139.0000 ; 51.5000 ]
stroke: 0.0-0.0-1.5 stroke: 0.0-0.0-1.5
shadow: 0 shadow: 0
color: ffa80036 color: ffa80036
@ -254,171 +254,311 @@ POLYGON:
RECTANGLE: RECTANGLE:
pt1: [ 2.0000 ; 31.0000 ] pt1: [ 2.0000 ; 31.0000 ]
pt2: [ 138.0000 ; 43.0000 ] pt2: [ 9.0000 ; 42.0000 ]
xCorner: 8 xCorner: 0
yCorner: 8 yCorner: 0
stroke: 0.0-0.0-1.0 stroke: 0.0-0.0-1.0
shadow: 0 shadow: 0
color: ffadd8e6 color: NULL_COLOR
backcolor: ffe6e6fa backcolor: ffe6e6fa
LINE:
pt1: [ 2.0000 ; 31.0000 ]
pt2: [ 7.0000 ; 31.0000 ]
stroke: 0.0-0.0-1.0
shadow: 0
color: ffadd8e6
LINE:
pt1: [ 2.0000 ; 42.0000 ]
pt2: [ 7.0000 ; 42.0000 ]
stroke: 0.0-0.0-1.0
shadow: 0
color: ffadd8e6
LINE:
pt1: [ 2.0000 ; 31.0000 ]
pt2: [ 2.0000 ; 42.0000 ]
stroke: 0.0-0.0-1.0
shadow: 0
color: ffadd8e6
RECTANGLE: RECTANGLE:
pt1: [ 9.0000 ; 31.0000 ] pt1: [ 16.0000 ; 31.0000 ]
pt2: [ 16.0000 ; 44.0000 ] pt2: [ 37.0000 ; 42.0000 ]
xCorner: 0 xCorner: 0
yCorner: 0 yCorner: 0
stroke: 0.0-0.0-1.0 stroke: 0.0-0.0-1.0
shadow: 0 shadow: 0
color: ffffffff color: NULL_COLOR
backcolor: ffffffff backcolor: ffe6e6fa
LINE: LINE:
pt1: [ 9.0000 ; 31.0000 ] pt1: [ 17.0000 ; 31.0000 ]
pt2: [ 16.0000 ; 31.0000 ] pt2: [ 36.0000 ; 31.0000 ]
stroke: 2.0-3.0-1.0 stroke: 0.0-0.0-1.0
shadow: 0 shadow: 0
color: ffadd8e6 color: ffadd8e6
LINE: LINE:
pt1: [ 9.0000 ; 43.0000 ] pt1: [ 17.0000 ; 42.0000 ]
pt2: [ 16.0000 ; 43.0000 ] pt2: [ 36.0000 ; 42.0000 ]
stroke: 2.0-3.0-1.0 stroke: 0.0-0.0-1.0
shadow: 0 shadow: 0
color: ffadd8e6 color: ffadd8e6
RECTANGLE: RECTANGLE:
pt1: [ 37.0000 ; 31.0000 ] pt1: [ 44.0000 ; 31.0000 ]
pt2: [ 44.0000 ; 44.0000 ] pt2: [ 65.0000 ; 42.0000 ]
xCorner: 0 xCorner: 0
yCorner: 0 yCorner: 0
stroke: 0.0-0.0-1.0 stroke: 0.0-0.0-1.0
shadow: 0 shadow: 0
color: ffffffff color: NULL_COLOR
backcolor: ffffffff backcolor: ffe6e6fa
LINE: LINE:
pt1: [ 37.0000 ; 31.0000 ] pt1: [ 45.0000 ; 31.0000 ]
pt2: [ 44.0000 ; 31.0000 ] pt2: [ 64.0000 ; 31.0000 ]
stroke: 2.0-3.0-1.0 stroke: 0.0-0.0-1.0
shadow: 0 shadow: 0
color: ffadd8e6 color: ffadd8e6
LINE: LINE:
pt1: [ 37.0000 ; 43.0000 ] pt1: [ 45.0000 ; 42.0000 ]
pt2: [ 44.0000 ; 43.0000 ] pt2: [ 64.0000 ; 42.0000 ]
stroke: 2.0-3.0-1.0 stroke: 0.0-0.0-1.0
shadow: 0 shadow: 0
color: ffadd8e6 color: ffadd8e6
RECTANGLE: RECTANGLE:
pt1: [ 65.0000 ; 31.0000 ] pt1: [ 72.0000 ; 31.0000 ]
pt2: [ 72.0000 ; 44.0000 ] pt2: [ 93.0000 ; 42.0000 ]
xCorner: 0 xCorner: 0
yCorner: 0 yCorner: 0
stroke: 0.0-0.0-1.0 stroke: 0.0-0.0-1.0
shadow: 0 shadow: 0
color: ffffffff color: NULL_COLOR
backcolor: ffffffff backcolor: ffe6e6fa
LINE: LINE:
pt1: [ 65.0000 ; 31.0000 ] pt1: [ 73.0000 ; 31.0000 ]
pt2: [ 72.0000 ; 31.0000 ] pt2: [ 92.0000 ; 31.0000 ]
stroke: 2.0-3.0-1.0 stroke: 0.0-0.0-1.0
shadow: 0 shadow: 0
color: ffadd8e6 color: ffadd8e6
LINE: LINE:
pt1: [ 65.0000 ; 43.0000 ] pt1: [ 73.0000 ; 42.0000 ]
pt2: [ 72.0000 ; 43.0000 ] pt2: [ 92.0000 ; 42.0000 ]
stroke: 2.0-3.0-1.0 stroke: 0.0-0.0-1.0
shadow: 0 shadow: 0
color: ffadd8e6 color: ffadd8e6
RECTANGLE: RECTANGLE:
pt1: [ 93.0000 ; 31.0000 ] pt1: [ 100.0000 ; 31.0000 ]
pt2: [ 100.0000 ; 44.0000 ] pt2: [ 121.0000 ; 42.0000 ]
xCorner: 0 xCorner: 0
yCorner: 0 yCorner: 0
stroke: 0.0-0.0-1.0 stroke: 0.0-0.0-1.0
shadow: 0 shadow: 0
color: ffffffff color: NULL_COLOR
backcolor: ffffffff backcolor: ffe6e6fa
LINE: LINE:
pt1: [ 93.0000 ; 31.0000 ] pt1: [ 101.0000 ; 31.0000 ]
pt2: [ 100.0000 ; 31.0000 ] pt2: [ 120.0000 ; 31.0000 ]
stroke: 2.0-3.0-1.0 stroke: 0.0-0.0-1.0
shadow: 0 shadow: 0
color: ffadd8e6 color: ffadd8e6
LINE: LINE:
pt1: [ 93.0000 ; 43.0000 ] pt1: [ 101.0000 ; 42.0000 ]
pt2: [ 100.0000 ; 43.0000 ] pt2: [ 120.0000 ; 42.0000 ]
stroke: 2.0-3.0-1.0 stroke: 0.0-0.0-1.0
shadow: 0 shadow: 0
color: ffadd8e6 color: ffadd8e6
RECTANGLE: RECTANGLE:
pt1: [ 121.0000 ; 31.0000 ] pt1: [ 128.0000 ; 31.0000 ]
pt2: [ 128.0000 ; 44.0000 ] pt2: [ 138.0000 ; 42.0000 ]
xCorner: 0 xCorner: 0
yCorner: 0 yCorner: 0
stroke: 0.0-0.0-1.0 stroke: 0.0-0.0-1.0
shadow: 0 shadow: 0
color: ffffffff color: NULL_COLOR
backcolor: ffffffff backcolor: ffe6e6fa
LINE: LINE:
pt1: [ 121.0000 ; 31.0000 ] pt1: [ 129.0000 ; 31.0000 ]
pt2: [ 128.0000 ; 31.0000 ] pt2: [ 138.0000 ; 31.0000 ]
stroke: 0.0-0.0-1.0
shadow: 0
color: ffadd8e6
LINE:
pt1: [ 129.0000 ; 42.0000 ]
pt2: [ 138.0000 ; 42.0000 ]
stroke: 0.0-0.0-1.0
shadow: 0
color: ffadd8e6
LINE:
pt1: [ 138.0000 ; 31.0000 ]
pt2: [ 138.0000 ; 42.0000 ]
stroke: 0.0-0.0-1.0
shadow: 0
color: ffadd8e6
LINE:
pt1: [ 11.0000 ; 31.0000 ]
pt2: [ 13.0000 ; 31.0000 ]
stroke: 2.0-3.0-1.0 stroke: 2.0-3.0-1.0
shadow: 0 shadow: 0
color: ffadd8e6 color: ffadd8e6
LINE: LINE:
pt1: [ 121.0000 ; 43.0000 ] pt1: [ 11.0000 ; 42.0000 ]
pt2: [ 128.0000 ; 43.0000 ] pt2: [ 13.0000 ; 42.0000 ]
stroke: 2.0-3.0-1.0
shadow: 0
color: ffadd8e6
LINE:
pt1: [ 39.0000 ; 31.0000 ]
pt2: [ 41.0000 ; 31.0000 ]
stroke: 2.0-3.0-1.0
shadow: 0
color: ffadd8e6
LINE:
pt1: [ 39.0000 ; 42.0000 ]
pt2: [ 41.0000 ; 42.0000 ]
stroke: 2.0-3.0-1.0
shadow: 0
color: ffadd8e6
LINE:
pt1: [ 67.0000 ; 31.0000 ]
pt2: [ 69.0000 ; 31.0000 ]
stroke: 2.0-3.0-1.0
shadow: 0
color: ffadd8e6
LINE:
pt1: [ 67.0000 ; 42.0000 ]
pt2: [ 69.0000 ; 42.0000 ]
stroke: 2.0-3.0-1.0
shadow: 0
color: ffadd8e6
LINE:
pt1: [ 95.0000 ; 31.0000 ]
pt2: [ 97.0000 ; 31.0000 ]
stroke: 2.0-3.0-1.0
shadow: 0
color: ffadd8e6
LINE:
pt1: [ 95.0000 ; 42.0000 ]
pt2: [ 97.0000 ; 42.0000 ]
stroke: 2.0-3.0-1.0
shadow: 0
color: ffadd8e6
LINE:
pt1: [ 123.0000 ; 31.0000 ]
pt2: [ 125.0000 ; 31.0000 ]
stroke: 2.0-3.0-1.0
shadow: 0
color: ffadd8e6
LINE:
pt1: [ 123.0000 ; 42.0000 ]
pt2: [ 125.0000 ; 42.0000 ]
stroke: 2.0-3.0-1.0 stroke: 2.0-3.0-1.0
shadow: 0 shadow: 0
color: ffadd8e6 color: ffadd8e6
RECTANGLE: RECTANGLE:
pt1: [ 142.0000 ; 47.0000 ] pt1: [ 142.0000 ; 46.0000 ]
pt2: [ 166.0000 ; 59.0000 ] pt2: [ 149.0000 ; 57.0000 ]
xCorner: 8 xCorner: 0
yCorner: 8 yCorner: 0
stroke: 0.0-0.0-1.0 stroke: 0.0-0.0-1.0
shadow: 0 shadow: 0
color: ffa80036 color: NULL_COLOR
backcolor: fffefece backcolor: fffefece
LINE:
pt1: [ 142.0000 ; 46.0000 ]
pt2: [ 147.0000 ; 46.0000 ]
stroke: 0.0-0.0-1.0
shadow: 0
color: ffa80036
LINE:
pt1: [ 142.0000 ; 57.0000 ]
pt2: [ 147.0000 ; 57.0000 ]
stroke: 0.0-0.0-1.0
shadow: 0
color: ffa80036
LINE:
pt1: [ 142.0000 ; 46.0000 ]
pt2: [ 142.0000 ; 57.0000 ]
stroke: 0.0-0.0-1.0
shadow: 0
color: ffa80036
RECTANGLE: RECTANGLE:
pt1: [ 149.0000 ; 47.0000 ] pt1: [ 156.0000 ; 46.0000 ]
pt2: [ 156.0000 ; 60.0000 ] pt2: [ 166.0000 ; 57.0000 ]
xCorner: 0 xCorner: 0
yCorner: 0 yCorner: 0
stroke: 0.0-0.0-1.0 stroke: 0.0-0.0-1.0
shadow: 0 shadow: 0
color: ffffffff color: NULL_COLOR
backcolor: ffffffff backcolor: fffefece
LINE: LINE:
pt1: [ 149.0000 ; 47.0000 ] pt1: [ 157.0000 ; 46.0000 ]
pt2: [ 156.0000 ; 47.0000 ] pt2: [ 166.0000 ; 46.0000 ]
stroke: 0.0-0.0-1.0
shadow: 0
color: ffa80036
LINE:
pt1: [ 157.0000 ; 57.0000 ]
pt2: [ 166.0000 ; 57.0000 ]
stroke: 0.0-0.0-1.0
shadow: 0
color: ffa80036
LINE:
pt1: [ 166.0000 ; 46.0000 ]
pt2: [ 166.0000 ; 57.0000 ]
stroke: 0.0-0.0-1.0
shadow: 0
color: ffa80036
LINE:
pt1: [ 151.0000 ; 46.0000 ]
pt2: [ 153.0000 ; 46.0000 ]
stroke: 2.0-3.0-1.0 stroke: 2.0-3.0-1.0
shadow: 0 shadow: 0
color: ffa80036 color: ffa80036
LINE: LINE:
pt1: [ 149.0000 ; 59.0000 ] pt1: [ 151.0000 ; 57.0000 ]
pt2: [ 156.0000 ; 59.0000 ] pt2: [ 153.0000 ; 57.0000 ]
stroke: 2.0-3.0-1.0 stroke: 2.0-3.0-1.0
shadow: 0 shadow: 0
color: ffa80036 color: ffa80036
TEXT: TEXT:
text: Prototype design text: Prototype design
position: [ 142.0000 ; 39.0556 ] position: [ 142.0000 ; 39.5556 ]
orientation: 0 orientation: 0
font: SansSerif.plain/11 [] font: SansSerif.plain/11 []
color: ff000000 color: ff000000
@ -426,59 +566,59 @@ TEXT:
TEXT: TEXT:
text: Testing text: Testing
position: [ 170.0000 ; 55.0556 ] position: [ 170.0000 ; 54.5556 ]
orientation: 0 orientation: 0
font: SansSerif.plain/11 [] font: SansSerif.plain/11 []
color: ff000000 color: ff000000
extendedColor: NULL_COLOR extendedColor: NULL_COLOR
LINE: LINE:
pt1: [ 0.0000 ; 61.0000 ] pt1: [ 0.0000 ; 59.0000 ]
pt2: [ 168.0000 ; 61.0000 ] pt2: [ 168.0000 ; 59.0000 ]
stroke: 0.0-0.0-1.0 stroke: 0.0-0.0-1.0
shadow: 0 shadow: 0
color: ffc0c0c0 color: ffc0c0c0
LINE: LINE:
pt1: [ 0.0000 ; 61.0000 ] pt1: [ 0.0000 ; 59.0000 ]
pt2: [ 0.0000 ; 77.0000 ] pt2: [ 0.0000 ; 75.0000 ]
stroke: 0.0-0.0-1.0 stroke: 0.0-0.0-1.0
shadow: 0 shadow: 0
color: ffc0c0c0 color: ffc0c0c0
LINE: LINE:
pt1: [ 68.0000 ; 61.0000 ] pt1: [ 68.0000 ; 59.0000 ]
pt2: [ 68.0000 ; 77.0000 ] pt2: [ 68.0000 ; 75.0000 ]
stroke: 0.0-0.0-1.0 stroke: 0.0-0.0-1.0
shadow: 0 shadow: 0
color: ffc0c0c0 color: ffc0c0c0
TEXT: TEXT:
text: Oct text: Oct
position: [ 17.4989 ; 70.3333 ] position: [ 17.4989 ; 68.3333 ]
orientation: 0 orientation: 0
font: Serif.bold/12 [BOLD] font: Serif.bold/12 [BOLD]
color: ff000000 color: ff000000
extendedColor: NULL_COLOR extendedColor: NULL_COLOR
LINE: LINE:
pt1: [ 168.0000 ; 61.0000 ] pt1: [ 168.0000 ; 59.0000 ]
pt2: [ 168.0000 ; 77.0000 ] pt2: [ 168.0000 ; 75.0000 ]
stroke: 0.0-0.0-1.0 stroke: 0.0-0.0-1.0
shadow: 0 shadow: 0
color: ffc0c0c0 color: ffc0c0c0
TEXT: TEXT:
text: Nov 2020 text: Nov 2020
position: [ 72.7816 ; 70.3333 ] position: [ 72.7816 ; 68.3333 ]
orientation: 0 orientation: 0
font: Serif.bold/12 [BOLD] font: Serif.bold/12 [BOLD]
color: ff000000 color: ff000000
extendedColor: NULL_COLOR extendedColor: NULL_COLOR
LINE: LINE:
pt1: [ 0.0000 ; 77.0000 ] pt1: [ 0.0000 ; 75.0000 ]
pt2: [ 168.0000 ; 77.0000 ] pt2: [ 168.0000 ; 75.0000 ]
stroke: 0.0-0.0-1.0 stroke: 0.0-0.0-1.0
shadow: 0 shadow: 0
color: ffc0c0c0 color: ffc0c0c0