Fixed multiple issues with the layout
This commit is contained in:
@@ -81,9 +81,10 @@ public class JSONImageRecipe implements ImageRecipe {
|
||||
Function<RenderContext, Paint> paint = createPaintProvider(jsonLayer.get("paint"), "black");
|
||||
final Function<RenderContext, String> text = createStringProvider(jsonLayer.get("text"));
|
||||
Function<RenderContext, TextBox> textBox = createTextBoxProvider(jsonLayer.get("box"));
|
||||
Function<RenderContext, Boolean> dropShadow = createBooleanProvider(jsonLayer.get("dropShadow"), false);
|
||||
|
||||
return new TextLayerRecipe(
|
||||
font, text, paint, textBox);
|
||||
font, text, paint, textBox, dropShadow);
|
||||
} else if (type.equalsIgnoreCase("textBox")) {
|
||||
final Function<RenderContext, String[]> text = createStringArrayProvider(jsonLayer.get("text"));
|
||||
JSONObject object = (JSONObject) jsonLayer.get("font");
|
||||
@@ -95,11 +96,12 @@ public class JSONImageRecipe implements ImageRecipe {
|
||||
|
||||
Function<String, Function<RenderContext, Font>> fontStyleProvider = map::get;
|
||||
Function<RenderContext, TextBox> textBox = createTextBoxProvider(jsonLayer.get("box"));
|
||||
final Function<RenderContext, Float> minYStart = createFloatProvider(jsonLayer.get("minYStart"));
|
||||
|
||||
return new TextBoxLayerRecipe(
|
||||
fontStyleProvider,
|
||||
s -> glyphMap.get(s),
|
||||
text, textBox);
|
||||
text, textBox, minYStart);
|
||||
} else if (type.equalsIgnoreCase("rotate")) {
|
||||
final LayerRecipe layer = createLayerRecipe((JSONObject) jsonLayer.get("layer"));
|
||||
final Function<RenderContext, Integer> angle = createIntProvider(jsonLayer.get("angle"));
|
||||
@@ -124,7 +126,20 @@ public class JSONImageRecipe implements ImageRecipe {
|
||||
if (value instanceof JSONObject) {
|
||||
JSONObject valueObj = (JSONObject) value;
|
||||
final String type = (String) valueObj.get("type");
|
||||
if (type.equalsIgnoreCase("append")) {
|
||||
if (type.equalsIgnoreCase("appendText")) {
|
||||
final JSONArray values = (JSONArray) valueObj.get("values");
|
||||
final List<Function<RenderContext, String>> list = (List<Function<RenderContext, String>>) values.stream().map(text -> createStringProvider(text)).collect(toList());
|
||||
|
||||
return renderContext -> {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (Function<RenderContext, String> valueProvider : list) {
|
||||
final String textValue = valueProvider.apply(renderContext);
|
||||
if (textValue != null)
|
||||
sb.append(textValue);
|
||||
}
|
||||
return new String[]{sb.toString()};
|
||||
};
|
||||
} else if (type.equalsIgnoreCase("append")) {
|
||||
final JSONArray values = (JSONArray) valueObj.get("values");
|
||||
|
||||
List<Function<RenderContext, String[]>> providers = (List<Function<RenderContext, String[]>>) values.stream().map(appendValue -> createStringArrayProvider(appendValue)).collect(Collectors.toList());
|
||||
@@ -211,8 +226,16 @@ public class JSONImageRecipe implements ImageRecipe {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHorizontalAlignment() {
|
||||
return horizontalAlignment.apply(renderContext);
|
||||
public HorizontalAlignment getHorizontalAlignment() {
|
||||
final String align = horizontalAlignment.apply(renderContext);
|
||||
if (align == null || align.equalsIgnoreCase("left"))
|
||||
return (availableWidth, usedWidth) -> 0;
|
||||
else if (align.equalsIgnoreCase("center"))
|
||||
return (availableWidth, usedWidth) -> (availableWidth - usedWidth) / 2;
|
||||
else if (align.equalsIgnoreCase("right"))
|
||||
return (availableWidth, usedWidth) -> (availableWidth - usedWidth);
|
||||
|
||||
throw new ImageGenerationException("Unable to recognize horizontal alignment: " + align);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -376,7 +399,15 @@ public class JSONImageRecipe implements ImageRecipe {
|
||||
}
|
||||
|
||||
private Function<RenderContext, Boolean> createBooleanProvider(Object condition) {
|
||||
if (condition instanceof JSONObject) {
|
||||
return createBooleanProvider(condition, null);
|
||||
}
|
||||
|
||||
private Function<RenderContext, Boolean> createBooleanProvider(Object condition, Boolean defaultValue) {
|
||||
if (condition == null && defaultValue != null)
|
||||
return renderContext -> defaultValue;
|
||||
else if (condition instanceof Boolean)
|
||||
return renderContext -> (Boolean) condition;
|
||||
else if (condition instanceof JSONObject) {
|
||||
JSONObject conditionObj = (JSONObject) condition;
|
||||
final String type = (String) conditionObj.get("type");
|
||||
if (type.equalsIgnoreCase("cardHasProperty")) {
|
||||
|
||||
@@ -9,5 +9,9 @@ public interface TextBox {
|
||||
|
||||
int getHeight();
|
||||
|
||||
String getHorizontalAlignment();
|
||||
HorizontalAlignment getHorizontalAlignment();
|
||||
|
||||
interface HorizontalAlignment {
|
||||
float getXShift(float availableWidth, float usedWidth);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import java.awt.font.FontRenderContext;
|
||||
import java.awt.font.LineBreakMeasurer;
|
||||
import java.awt.font.TextAttribute;
|
||||
import java.awt.font.TextLayout;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.text.AttributedCharacterIterator;
|
||||
import java.text.AttributedString;
|
||||
@@ -15,16 +16,19 @@ public class TextBoxLayerRecipe implements LayerRecipe {
|
||||
private Function<String, String> glyphProvider;
|
||||
private Function<RenderContext, String[]> text;
|
||||
private Function<RenderContext, TextBox> textBox;
|
||||
private Function<RenderContext, Float> minYStart;
|
||||
|
||||
public TextBoxLayerRecipe(
|
||||
Function<String, Function<RenderContext, Font>> fontStyleProvider,
|
||||
Function<String, String> glyphProvider,
|
||||
Function<RenderContext, String[]> text,
|
||||
Function<RenderContext, TextBox> textBox) {
|
||||
Function<RenderContext, TextBox> textBox,
|
||||
Function<RenderContext, Float> minYStart) {
|
||||
this.fontStyleProvider = fontStyleProvider;
|
||||
this.glyphProvider = glyphProvider;
|
||||
this.text = text;
|
||||
this.textBox = textBox;
|
||||
this.minYStart = minYStart;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -46,20 +50,27 @@ public class TextBoxLayerRecipe implements LayerRecipe {
|
||||
|
||||
// Dry run to check for height
|
||||
float textHeight = processText((layout, x, y) -> {
|
||||
}, iteratorLines, 0, 0, box.getWidth(), frc);
|
||||
}, iteratorLines, 0, 0, box.getWidth(), box.getHorizontalAlignment(), frc);
|
||||
|
||||
float textAscent = Math.min(0.125f, (1 - textHeight / box.getHeight()) / 2f) * box.getHeight();
|
||||
processText((layout, x, y) -> layout.draw(graphics, x, y), iteratorLines, box.getX(), box.getY() + textAscent, box.getWidth(), frc);
|
||||
final float boxPercUsed = textHeight / box.getHeight();
|
||||
float textAscent = Math.min(minYStart.apply(renderContext), (1 - boxPercUsed) / 2f) * box.getHeight();
|
||||
processText((layout, x, y) -> layout.draw(graphics, x, y), iteratorLines, box.getX(), box.getY() + textAscent, box.getWidth(), box.getHorizontalAlignment(), frc);
|
||||
}
|
||||
}
|
||||
|
||||
private float processText(LineRenderingCallback callback, AttributedCharacterIterator[] iteratorLines, float x, float y, float width, FontRenderContext frc) {
|
||||
private float processText(LineRenderingCallback callback, AttributedCharacterIterator[] iteratorLines, float x, float y, float width, TextBox.HorizontalAlignment horizontalAlignment, FontRenderContext frc) {
|
||||
Point2D.Float pen = new Point2D.Float(x, y);
|
||||
|
||||
boolean firstParagraph = true;
|
||||
for (AttributedCharacterIterator styledText : iteratorLines) {
|
||||
// let styledText be an AttributedCharacterIterator containing at least
|
||||
// one character
|
||||
|
||||
// Paragraph break
|
||||
if (!firstParagraph) {
|
||||
pen.y += 5;
|
||||
}
|
||||
|
||||
LineBreakMeasurer measurer = new LineBreakMeasurer(styledText, frc);
|
||||
float wrappingWidth = width;
|
||||
|
||||
@@ -67,14 +78,13 @@ public class TextBoxLayerRecipe implements LayerRecipe {
|
||||
TextLayout layout = measurer.nextLayout(wrappingWidth);
|
||||
|
||||
pen.y += (layout.getAscent());
|
||||
float dx = layout.isLeftToRight() ?
|
||||
0 : (wrappingWidth - layout.getAdvance());
|
||||
float xShift = horizontalAlignment.getXShift(wrappingWidth, layout.getAdvance());
|
||||
|
||||
callback.drawLine(layout, pen.x + dx, pen.y);
|
||||
callback.drawLine(layout, pen.x + xShift, pen.y);
|
||||
pen.y += layout.getDescent() + layout.getLeading();
|
||||
}
|
||||
// Paragraph break
|
||||
pen.y += 5;
|
||||
|
||||
firstParagraph = false;
|
||||
}
|
||||
return pen.y;
|
||||
}
|
||||
@@ -86,11 +96,20 @@ public class TextBoxLayerRecipe implements LayerRecipe {
|
||||
AttributedString string = new AttributedString(resultText.toString());
|
||||
parseText(text, new TextParsingCallback() {
|
||||
private int index = 0;
|
||||
private boolean lowered = false;
|
||||
|
||||
@Override
|
||||
public void appendText(String text, String style) {
|
||||
String resultStyle = (style != null) ? style : "default";
|
||||
string.addAttribute(TextAttribute.FONT, fontStyleProvider.apply(resultStyle).apply(renderContext), index, index + text.length());
|
||||
Font font = fontStyleProvider.apply(resultStyle).apply(renderContext);
|
||||
if (resultStyle.equals("glyph") && !lowered) {
|
||||
font = font.deriveFont(AffineTransform.getTranslateInstance(0, 3));
|
||||
lowered = true;
|
||||
} else if (!resultStyle.equals("glyph") && lowered) {
|
||||
font = font.deriveFont(AffineTransform.getTranslateInstance(0, -3));
|
||||
lowered = false;
|
||||
}
|
||||
string.addAttribute(TextAttribute.FONT, font, index, index + text.length());
|
||||
string.addAttribute(TextAttribute.FOREGROUND, Color.BLACK, index, index + text.length());
|
||||
index += text.length();
|
||||
}
|
||||
|
||||
@@ -8,12 +8,16 @@ public class TextLayerRecipe implements LayerRecipe {
|
||||
private Function<RenderContext, String> textProvider;
|
||||
private Function<RenderContext, Paint> paintProvider;
|
||||
private Function<RenderContext, TextBox> textBoxProvider;
|
||||
private Function<RenderContext, Boolean> dropShadow;
|
||||
|
||||
public TextLayerRecipe(Function<RenderContext, Font> fontProvider, Function<RenderContext, String> textProvider, Function<RenderContext, Paint> paintProvider, Function<RenderContext, TextBox> textBoxProvider) {
|
||||
public TextLayerRecipe(Function<RenderContext, Font> fontProvider, Function<RenderContext, String> textProvider,
|
||||
Function<RenderContext, Paint> paintProvider, Function<RenderContext, TextBox> textBoxProvider,
|
||||
Function<RenderContext, Boolean> dropShadow) {
|
||||
this.fontProvider = fontProvider;
|
||||
this.textProvider = textProvider;
|
||||
this.paintProvider = paintProvider;
|
||||
this.textBoxProvider = textBoxProvider;
|
||||
this.dropShadow = dropShadow;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -28,25 +32,29 @@ public class TextLayerRecipe implements LayerRecipe {
|
||||
|
||||
final Font font = fontProvider.apply(renderContext);
|
||||
graphics.setFont(font);
|
||||
graphics.setPaint(paintProvider.apply(renderContext));
|
||||
|
||||
final FontMetrics fontMetrics = graphics.getFontMetrics();
|
||||
|
||||
int dropShadowDistance = 3;
|
||||
|
||||
int yMargin = (textBox.getHeight() - fontMetrics.getHeight()) / 2;
|
||||
|
||||
final String horizontalAlignment = textBox.getHorizontalAlignment();
|
||||
if (horizontalAlignment == null) {
|
||||
graphics.drawString(text, textBox.getX(), textBox.getY() + yMargin + fontMetrics.getAscent());
|
||||
} else if (horizontalAlignment.equals("center")) {
|
||||
final int width = fontMetrics.stringWidth(text);
|
||||
graphics.drawString(text, textBox.getX() + (textBox.getWidth() - width) / 2, textBox.getY() + yMargin + fontMetrics.getAscent());
|
||||
} else if (horizontalAlignment.equals("right")) {
|
||||
final int width = fontMetrics.stringWidth(text);
|
||||
graphics.drawString(text, textBox.getX() + (textBox.getWidth() - width), textBox.getY() + yMargin + fontMetrics.getAscent());
|
||||
} else {
|
||||
throw new ImageGenerationException("Unable to recognize horizontal alignment: " + horizontalAlignment);
|
||||
final TextBox.HorizontalAlignment horizontalAlignment = textBox.getHorizontalAlignment();
|
||||
|
||||
if (dropShadow.apply(renderContext)) {
|
||||
graphics.setPaint(Color.BLACK);
|
||||
drawText(graphics, text, textBox, fontMetrics, yMargin, horizontalAlignment, dropShadowDistance);
|
||||
}
|
||||
graphics.setPaint(paintProvider.apply(renderContext));
|
||||
drawText(graphics, text, textBox, fontMetrics, yMargin, horizontalAlignment, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private void drawText(Graphics2D graphics, String text, TextBox textBox, FontMetrics fontMetrics, int yMargin, TextBox.HorizontalAlignment horizontalAlignment,
|
||||
int distance) {
|
||||
final int width = fontMetrics.stringWidth(text);
|
||||
float xShift = horizontalAlignment.getXShift(textBox.getWidth(), width);
|
||||
|
||||
graphics.drawString(text, xShift + distance + textBox.getX(), distance + textBox.getY() + yMargin + fontMetrics.getAscent());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -334,6 +334,7 @@
|
||||
"comment": "Cost",
|
||||
"type": "text",
|
||||
"paint": "white",
|
||||
"dropShadow": true,
|
||||
"font": {
|
||||
"type": "ttf",
|
||||
"path": {
|
||||
@@ -374,28 +375,9 @@
|
||||
"name": "isCharacter"
|
||||
},
|
||||
"values": {
|
||||
"type": "text",
|
||||
"font": {
|
||||
"type": "ttf",
|
||||
"path": {
|
||||
"type": "resolve",
|
||||
"parent": {
|
||||
"type": "property",
|
||||
"name": "lotro.resources.folder"
|
||||
},
|
||||
"child": {
|
||||
"type": "property",
|
||||
"name": "type.font"
|
||||
}
|
||||
},
|
||||
"size": {
|
||||
"type": "property",
|
||||
"name": "type.size"
|
||||
},
|
||||
"style": 0
|
||||
},
|
||||
"type": "textBox",
|
||||
"text": {
|
||||
"type": "append",
|
||||
"type": "appendText",
|
||||
"values": [
|
||||
{
|
||||
"comment": "Card type",
|
||||
@@ -415,7 +397,7 @@
|
||||
"value": {
|
||||
"type": "append",
|
||||
"values": [
|
||||
" • ",
|
||||
" {bullet} ",
|
||||
{
|
||||
"type": "capitalize",
|
||||
"value": {
|
||||
@@ -447,7 +429,7 @@
|
||||
"value": {
|
||||
"type": "append",
|
||||
"values": [
|
||||
" • ",
|
||||
" {bullet} ",
|
||||
{
|
||||
"type": "capitalize",
|
||||
"value": {
|
||||
@@ -471,12 +453,52 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"font": {
|
||||
"default": {
|
||||
"type": "ttf",
|
||||
"path": {
|
||||
"type": "resolve",
|
||||
"parent": {
|
||||
"type": "property",
|
||||
"name": "lotro.resources.folder"
|
||||
},
|
||||
"child": {
|
||||
"type": "property",
|
||||
"name": "type.font"
|
||||
}
|
||||
},
|
||||
"size": {
|
||||
"type": "property",
|
||||
"name": "type.size"
|
||||
},
|
||||
"style": 0
|
||||
},
|
||||
"glyph": {
|
||||
"type": "ttf",
|
||||
"path": {
|
||||
"type": "resolve",
|
||||
"parent": {
|
||||
"type": "property",
|
||||
"name": "lotro.resources.folder"
|
||||
},
|
||||
"child": "LOTRSymbols.ttf"
|
||||
},
|
||||
"size": 40,
|
||||
"style": 0
|
||||
}
|
||||
},
|
||||
"glyphs": {
|
||||
"shire": "#",
|
||||
"dwarven": "@",
|
||||
"bullet": "•"
|
||||
},
|
||||
"minYStart": 1,
|
||||
"box": {
|
||||
"type": "box",
|
||||
"x": 0,
|
||||
"y": 615,
|
||||
"y": 611,
|
||||
"width": 751,
|
||||
"height": 30,
|
||||
"height": 40,
|
||||
"horAlign": "center"
|
||||
}
|
||||
}
|
||||
@@ -645,6 +667,7 @@
|
||||
"comment": "Strength text",
|
||||
"type": "text",
|
||||
"paint": "white",
|
||||
"dropShadow": true,
|
||||
"font": {
|
||||
"type": "ttf",
|
||||
"path": {
|
||||
@@ -727,6 +750,7 @@
|
||||
"comment": "Vitality text",
|
||||
"type": "text",
|
||||
"paint": "white",
|
||||
"dropShadow": true,
|
||||
"font": {
|
||||
"type": "ttf",
|
||||
"path": {
|
||||
@@ -979,12 +1003,13 @@
|
||||
"shire": "#",
|
||||
"dwarven": "@"
|
||||
},
|
||||
"minYStart": 0.15,
|
||||
"box": {
|
||||
"type": "box",
|
||||
"x": 168,
|
||||
"y": 663,
|
||||
"width": 500,
|
||||
"height": 318
|
||||
"x": 160,
|
||||
"y": 690,
|
||||
"width": 540,
|
||||
"height": 265
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
recipe.file=/Users/marcin.sciesinski/private/gemp-lotr/gemp-lotr/gemp-lotr-images/src/main/resources/cardRecipe.json
|
||||
output.folder=/Users/marcin.sciesinski/private/gemp-lotr/gemp-lotr/gemp-lotr-images/target/images
|
||||
lotro.resources.folder=/Users/marcin.sciesinski/lotro-resources
|
||||
"child":"fonts/o_wap_matbolsmallcap.ttf"
|
||||
|
||||
#title.font=Joe-resources/Matrix-Bold.ttf
|
||||
title.font=fonts/o_wap_matbolsmallcap.ttf
|
||||
title.size=40
|
||||
@@ -15,11 +15,11 @@ numbers.font=Joe-resources/GaramondBE-Medium.otf
|
||||
numbers.size=60
|
||||
#text.font=Joe-resources/LOTR.ttf
|
||||
text.font=fonts/o_lotrrg__.ttf
|
||||
text.size=25
|
||||
text.size=27
|
||||
#keyword.font=Joe-resources/LOTR Bold.ttf
|
||||
keyword.font=fonts/o_lotrb___.ttf
|
||||
keyword.size=25
|
||||
keyword.size=27
|
||||
#flavor.font=Joe-resources/CentaurMT-SwashCapitals.otf
|
||||
flavor.font=fonts/o_lotr-cmt.ttf
|
||||
flavor.size=25
|
||||
flavor.size=27
|
||||
glyph.size=22
|
||||
|
||||
Reference in New Issue
Block a user