1
0
mirror of https://github.com/octoleo/plantuml.git synced 2024-06-01 08:00:48 +00:00

Add Support for Font Selection in Ditaa

This adds support for selecting the font in ditaa diagrams by specifying
--font-family, --font-size and/or --font-variant in the ditaa() line.

The change is actually taken over from @pepijnve who implemented in
https://github.com/pepijnve/ditaa/issues/1. I extended it for supporting
the necessary flags from plantuml.
This commit is contained in:
Christoph Rüthing 2022-02-24 13:12:53 +01:00
parent 87cbe82cd0
commit 8066a3f730
6 changed files with 141 additions and 118 deletions

View File

@ -34,6 +34,7 @@
*/
package net.sourceforge.plantuml.ditaa;
import java.awt.Font;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.OutputStream;
@ -61,11 +62,12 @@ public class PSystemDitaa extends AbstractPSystem {
private final boolean dropShadows;
private final String data;
private final float scale;
private final Font font;
private final boolean performSeparationOfCommonEdges;
private final boolean allCornersAreRound;
public PSystemDitaa(UmlSource source, String data, boolean performSeparationOfCommonEdges, boolean dropShadows,
boolean allCornersAreRound, float scale) {
boolean allCornersAreRound, float scale, Font font) {
super(source);
this.data = data;
this.dropShadows = dropShadows;
@ -84,11 +86,12 @@ public class PSystemDitaa extends AbstractPSystem {
this.processingOptions = null;
}
this.scale = scale;
this.font = font;
}
PSystemDitaa add(String line) {
return new PSystemDitaa(getSource(), data + line + BackSlash.NEWLINE, performSeparationOfCommonEdges,
dropShadows, allCornersAreRound, scale);
dropShadows, allCornersAreRound, scale, font);
}
public DiagramDescription getDescription() {
@ -113,6 +116,10 @@ public class PSystemDitaa extends AbstractPSystem {
final Field f_renderingOptions = options.getClass().getField("renderingOptions");
final Object renderingOptions = f_renderingOptions.get(options);
// renderingOptions.setFont(font);
final Method setFont = renderingOptions.getClass().getMethod("setFont", Font.class);
setFont.invoke(renderingOptions, font);
// renderingOptions.setScale(scale);
final Method setScale = renderingOptions.getClass().getMethod("setScale", float.class);
setScale.invoke(renderingOptions, scale);

View File

@ -34,6 +34,7 @@
*/
package net.sourceforge.plantuml.ditaa;
import java.awt.Font;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -70,10 +71,11 @@ public class PSystemDitaaFactory extends PSystemBasicFactory<PSystemDitaa> {
allCornersAreRound = true;
final float scale = extractScale(startLine);
final Font font = extractFont(startLine);
if (getDiagramType() == DiagramType.UML)
return null;
else if (getDiagramType() == DiagramType.DITAA)
return new PSystemDitaa(source, "", performSeparationOfCommonEdges, dropShadows, allCornersAreRound, scale);
return new PSystemDitaa(source, "", performSeparationOfCommonEdges, dropShadows, allCornersAreRound, scale, font);
else
throw new IllegalStateException(getDiagramType().name());
@ -95,7 +97,8 @@ public class PSystemDitaaFactory extends PSystemBasicFactory<PSystemDitaa> {
allCornersAreRound = true;
final float scale = extractScale(line);
return new PSystemDitaa(source, "", performSeparationOfCommonEdges, dropShadows, allCornersAreRound, scale);
final Font font = extractFont(line);
return new PSystemDitaa(source, "", performSeparationOfCommonEdges, dropShadows, allCornersAreRound, scale, font);
}
if (system == null)
return null;
@ -115,4 +118,46 @@ public class PSystemDitaaFactory extends PSystemBasicFactory<PSystemDitaa> {
}
return 1;
}
private Font extractFont(String line) {
if (line == null)
return new Font("Dialog", Font.BOLD, 12);
final Pattern pName = Pattern.compile("font-family=([a-zA-Z0-0 ]+)");
final Matcher mName = pName.matcher(line);
String fontName = "Dialog";
if (mName.find())
{
fontName = mName.group(1);
}
final Pattern pVariant = Pattern.compile("font-variant=(BOLD|ITALIC|PLAIN)");
final Matcher mVariant = pVariant.matcher(line);
int fontVariant = Font.BOLD;
if (mVariant.find())
{
switch (mVariant.group(1))
{
case "BOLD":
fontVariant = Font.BOLD;
break;
case "ITALIC":
fontVariant = Font.ITALIC;
break;
case "PLAIN":
fontVariant = Font.PLAIN;
break;
}
}
final Pattern pSize = Pattern.compile("font-size=([\\d]+)");
final Matcher mSize = pSize.matcher(line);
int fontSize = 12;
if (mSize.find())
{
fontSize = Integer.parseInt(mSize.group(1));
}
return new Font(fontName, fontVariant, fontSize);
}
}

View File

@ -20,6 +20,7 @@
*/
package org.stathissideris.ascii2image.core;
import java.awt.*;
import java.util.HashMap;
import org.stathissideris.ascii2image.graphics.CustomShapeDefinition;
@ -41,6 +42,8 @@ public class RenderingOptions {
private float scale = 1;
private Font font = new Font("Dialog", Font.BOLD, 12);
/**
* @return
*/
@ -113,4 +116,14 @@ public class RenderingOptions {
antialias = b;
}
public Font getFont()
{
return font;
}
public void setFont(Font font)
{
this.font = font;
}
}

View File

@ -111,6 +111,8 @@ public class Diagram {
this.cellWidth = options.renderingOptions.getCellWidth();
this.cellHeight = options.renderingOptions.getCellHeight();
FontMeasurer fontMeasurer = new FontMeasurer(options.renderingOptions.getFont());
width = grid.getWidth() * cellWidth;
height = grid.getHeight() * cellHeight;
@ -526,7 +528,7 @@ public class Diagram {
ArrayList textGroups = nonBlank.breakIntoDistinctBoundaries();
if(DEBUG) System.out.println(textGroups.size()+" text groups found");
Font font = FontMeasurer.instance().getFontFor(cellHeight);
Font font = fontMeasurer.getFontFor(cellHeight);
Iterator textGroupIt = textGroups.iterator();
while(textGroupIt.hasNext()){
@ -550,10 +552,10 @@ public class Diagram {
int maxX = getCellMaxX(lastCell);
DiagramText textObject;
if(FontMeasurer.instance().getWidthFor(string, font) > maxX - minX){ //does not fit horizontally
Font lessWideFont = FontMeasurer.instance().getFontFor(maxX - minX, string);
textObject = new DiagramText(minX, y, string, lessWideFont);
} else textObject = new DiagramText(minX, y, string, font);
if(fontMeasurer.getWidthFor(string, font) > maxX - minX){ //does not fit horizontally
Font lessWideFont = fontMeasurer.getFontFor(maxX - minX, string);
textObject = new DiagramText(minX, y, string, lessWideFont, fontMeasurer);
} else textObject = new DiagramText(minX, y, string, font, fontMeasurer);
textObject.centerVerticallyBetween(getCellMinY(cell), getCellMaxY(cell));

View File

@ -39,8 +39,9 @@ public class DiagramText extends DiagramComponent {
private boolean isTextOnLine = false;
private boolean hasOutline = false;
private Color outlineColor = Color.white;
private final FontMeasurer fontMeasurer;
public DiagramText(int x, int y, String text, Font font){
public DiagramText(int x, int y, String text, Font font, FontMeasurer fontMeasurer){
if(text == null) throw new IllegalArgumentException("DiagramText cannot be initialised with a null string");
if(font == null) throw new IllegalArgumentException("DiagramText cannot be initialised with a null font");
@ -48,6 +49,7 @@ public class DiagramText extends DiagramComponent {
this.yPos = y;
this.text = text;
this.font = font;
this.fontMeasurer = fontMeasurer;
}
public void centerInBounds(Rectangle2D bounds){
@ -56,20 +58,20 @@ public class DiagramText extends DiagramComponent {
}
public void centerHorizontallyBetween(int minX, int maxX){
int width = FontMeasurer.instance().getWidthFor(text, font);
int width = fontMeasurer.getWidthFor(text, font);
int center = Math.abs(maxX - minX) / 2;
xPos += Math.abs(center - width / 2);
}
public void centerVerticallyBetween(int minY, int maxY){
int zHeight = FontMeasurer.instance().getZHeight(font);
int zHeight = fontMeasurer.getZHeight(font);
int center = Math.abs(maxY - minY) / 2;
yPos -= Math.abs(center - zHeight / 2);
}
public void alignRightEdgeTo(int x){
int width = FontMeasurer.instance().getWidthFor(text, font);
int width = fontMeasurer.getWidthFor(text, font);
xPos = x - width;
}
@ -145,7 +147,7 @@ public class DiagramText extends DiagramComponent {
}
public Rectangle2D getBounds(){
Rectangle2D bounds = FontMeasurer.instance().getBoundsFor(text, font);
Rectangle2D bounds = fontMeasurer.getBoundsFor(text, font);
bounds.setRect(
bounds.getMinX() + xPos,
bounds.getMinY() + yPos,

View File

@ -26,32 +26,25 @@ import java.awt.Graphics2D;
import java.awt.font.FontRenderContext;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.util.Locale;
/**
*
* @author Efstathios Sideris
*/
public class FontMeasurer {
private static final String fontFamilyName = "Dialog";
//private static final String fontFamilyName = "Helvetica";
private static final boolean DEBUG = false;
private static final FontMeasurer instance = new FontMeasurer();
FontRenderContext fakeRenderContext;
Graphics2D fakeGraphics;
{
private final Font baseFont;
private FontRenderContext fakeRenderContext;
private Graphics2D fakeGraphics;
public FontMeasurer(Font font){
baseFont = font;
BufferedImage image = new BufferedImage(1,1, BufferedImage.TYPE_INT_RGB);
fakeGraphics = image.createGraphics();
if (DEBUG) System.out.println("Locale: "+Locale.getDefault());
fakeRenderContext = fakeGraphics.getFontRenderContext();
}
}
public int getWidthFor(String str, int pixelHeight){
Font font = getFontFor(pixelHeight);
@ -74,125 +67,86 @@ public class FontMeasurer {
Rectangle2D rectangle = font.getStringBounds(str, fakeRenderContext);
return (int) rectangle.getHeight();
}
public Rectangle2D getBoundsFor(String str, Font font){
return font.getStringBounds(str, fakeRenderContext);
}
public Font getFontFor(int pixelHeight){
BufferedImage image = new BufferedImage(1,1, BufferedImage.TYPE_INT_RGB);
Graphics2D g2 = image.createGraphics();
return getFontFor(pixelHeight, fakeRenderContext);
}
public int getAscent(Font font){
fakeGraphics.setFont(font);
FontMetrics metrics = fakeGraphics.getFontMetrics();
if(DEBUG) System.out.println("Ascent: "+metrics.getAscent());
return metrics.getAscent();
}
public int getZHeight(Font font){
int height = (int) font.createGlyphVector(fakeRenderContext, "Z").getOutline().getBounds().getHeight();
if(DEBUG) System.out.println("Z height: "+height);
return height;
return (int) font.createGlyphVector(fakeRenderContext, "Z").getOutline().getBounds().getHeight();
}
public Font getFontFor(int maxWidth, String string){
float size = 12;
Font currentFont = new Font(fontFamilyName, Font.BOLD, (int) size);
//ascent is the distance between the baseline and the tallest character
int width = getWidthFor(string, currentFont);
public Font getFontFor(final int maxWidth, final String string){
FontPredicate predicate = new FontPredicate() {
@Override
public boolean test(Font font)
{
int width = getWidthFor(string, font);
return width > maxWidth;
}
};
return deriveFont(predicate, 1.0f);
}
public Font getFontFor(final int pixelHeight){
FontPredicate predicate = new FontPredicate() {
@Override
public boolean test(Font font)
{
//ascent is the distance between the baseline and the tallest character
int ascent = getAscent(font);
return ascent > pixelHeight;
}
};
return deriveFont(predicate, 0.5f);
}
private Font deriveFont(FontPredicate predicate, float sizeDelta)
{
Font currentFont = baseFont;
float size = baseFont.getSize2D();
int direction; //direction of size change (towards smaller or bigger)
if(width > maxWidth){
currentFont = currentFont.deriveFont(size - 1);
if(predicate.test(currentFont)){
currentFont = currentFont.deriveFont(size - 1f);
size--;
direction = -1;
direction = -1;
} else {
currentFont = currentFont.deriveFont(size + 1);
currentFont = currentFont.deriveFont(size + 1f);
size++;
direction = 1;
}
while(size > 0){
currentFont = currentFont.deriveFont(size);
//rectangle = currentFont.getStringBounds(testString, frc);
width = getWidthFor(string, currentFont);
if(direction == 1){
if(width > maxWidth){
size = size - 1;
if (direction == 1) {
if (predicate.test(currentFont)) {
size = size - sizeDelta;
return currentFont.deriveFont(size);
} else {
size = size + sizeDelta;
}
else size = size + 1;
} else {
if(width < maxWidth)
if (!predicate.test(currentFont)) {
return currentFont;
else size = size - 1;
} else {
size = size - sizeDelta;
}
}
}
return null;
}
public Font getFontFor(int pixelHeight, FontRenderContext frc){
float size = 12;
Font currentFont = new Font(fontFamilyName, Font.BOLD, (int) size);
// Font currentFont = new Font("Times", Font.BOLD, (int) size);
if (DEBUG) System.out.println(currentFont.getFontName());
//ascent is the distance between the baseline and the tallest character
int ascent = getAscent(currentFont);
int direction; //direction of size change (towards smaller or bigger)
if(ascent > pixelHeight){
currentFont = currentFont.deriveFont(size - 1);
size--;
direction = -1;
} else {
currentFont = currentFont.deriveFont(size + 1);
size++;
direction = 1;
}
while(size > 0){
currentFont = currentFont.deriveFont(size);
//rectangle = currentFont.getStringBounds(testString, frc);
ascent = getAscent(currentFont);
if(direction == 1){
if(ascent > pixelHeight){
size = size - 0.5f;
return currentFont.deriveFont(size);
}
else size = size + 0.5f;
} else {
if(ascent < pixelHeight)
return currentFont;
else size = size - 0.5f;
}
}
return null;
}
public static FontMeasurer instance(){
return instance;
}
public FontMeasurer(){
}
public static void main(String[] args) {
//FontMeasurer.instance().getFontFor(7);
float size = 12;
Font currentFont = new Font("Sans", Font.BOLD, (int) size);
System.out.println(currentFont.getSize());
currentFont = currentFont.deriveFont(--size);
System.out.println(currentFont.getSize());
currentFont = currentFont.deriveFont(--size);
System.out.println(currentFont.getSize());
currentFont = currentFont.deriveFont(--size);
System.out.println(currentFont.getSize());
currentFont = currentFont.deriveFont(--size);
System.out.println(currentFont.getSize());
currentFont = currentFont.deriveFont(--size);
System.out.println(currentFont.getSize());
private interface FontPredicate {
boolean test(Font font);
}
}