Worked around a Java issue with rendering fonts with AffineTransforms

This commit is contained in:
marcin.sciesinski
2019-10-12 09:51:52 -07:00
parent 6449db69b1
commit 2213da4977
4 changed files with 208 additions and 121 deletions

View File

@@ -38,7 +38,7 @@ public class ImageGenerator {
final JSONObject cardsObject = (JSONObject) parser.parse(reader);
for (Map.Entry<String, JSONObject> cardEntry : (Set<Map.Entry<String, JSONObject>>) cardsObject.entrySet()) {
final String cardId = cardEntry.getKey();
if (cardId.equals("40_3") || cardId.equals("40_17") || cardId.equals("40_19")) {
// if (cardId.equals("40_3") || cardId.equals("40_17") || cardId.equals("40_19")) {
BufferedImage bufferedImage = new BufferedImage(jsonImageRecipe.getWidth(), jsonImageRecipe.getHeight(), BufferedImage.TYPE_INT_RGB);
jsonImageRecipe.renderImage(properties, cardEntry.getValue(), bufferedImage);
@@ -52,7 +52,7 @@ public class ImageGenerator {
writer.setOutput(outputStream);
writer.write(null, new IIOImage(bufferedImage, null, null), jpegParams);
}
}
// }
}
} catch (IOException | ParseException e) {
throw new ImageGenerationException("Unable to generate image", e);

View File

@@ -27,7 +27,6 @@ public class JSONImageRecipe implements ImageRecipe {
private int height;
private List<LayerRecipe> layers = new LinkedList<>();
private Map<Path, Image> imageCache = new HashMap<>();
private Map<FontDefinition, Font> fontCache = new HashMap<>();
private Map<String, Function<RenderContext, ?>> functionMap;
public JSONImageRecipe(File file) {
@@ -93,14 +92,25 @@ public class JSONImageRecipe implements ImageRecipe {
map.put(fontEntry.getKey(), createFontProvider(fontEntry.getValue()));
}
Map<String, String> glyphMap = (JSONObject) jsonLayer.get("glyphs");
Function<RenderContext, Paint> paint = createPaintProvider(jsonLayer.get("paint"), "black");
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"));
Map<String, Number> yShiftsMap = (JSONObject) jsonLayer.get("yShifts");
return new TextBoxLayerRecipe(
fontStyleProvider,
s -> glyphMap.get(s),
s -> glyphMap.get(s), paint,
s -> {
if (yShiftsMap == null)
return 0f;
final Number value = yShiftsMap.get(s);
if (value == null)
return 0f;
else
return value.floatValue();
},
text, textBox, minYStart);
} else if (type.equalsIgnoreCase("rotate")) {
final LayerRecipe layer = createLayerRecipe((JSONObject) jsonLayer.get("layer"));
@@ -256,16 +266,11 @@ public class JSONImageRecipe implements ImageRecipe {
final Path path = pathProvider.apply(renderContext);
final float size = sizeProvider.apply(renderContext);
final int style = styleProvider.apply(renderContext);
FontDefinition fontDefinition = new FontDefinition(size, style, path);
return fontCache.computeIfAbsent(fontDefinition,
fontDefinition1 -> {
try {
return Font.createFont(Font.TRUETYPE_FONT, path.toFile()).deriveFont(style, size);
} catch (FontFormatException | IOException e) {
throw new ImageGenerationException("Unable to get font", e);
}
});
try {
return Font.createFont(Font.TRUETYPE_FONT, path.toFile()).deriveFont(style, size);
} catch (FontFormatException | IOException e) {
throw new ImageGenerationException("Unable to get font", e);
}
};
}
}
@@ -468,6 +473,12 @@ public class JSONImageRecipe implements ImageRecipe {
}
private Function<RenderContext, Float> createFloatProvider(Object value) {
return createFloatProvider(value, null);
}
private Function<RenderContext, Float> createFloatProvider(Object value, Float defaultValue) {
if (value == null && defaultValue != null)
return renderContext -> defaultValue;
if (value instanceof Number) {
return renderContext -> ((Number) value).floatValue();
} else if (value instanceof JSONObject) {
@@ -539,31 +550,4 @@ public class JSONImageRecipe implements ImageRecipe {
graphics.dispose();
}
}
private class FontDefinition {
private float size;
private int style;
private Path fontPath;
public FontDefinition(float size, int style, Path fontPath) {
this.size = size;
this.style = style;
this.fontPath = fontPath;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
FontDefinition that = (FontDefinition) o;
return Float.compare(that.size, size) == 0 &&
style == that.style &&
fontPath.equals(that.fontPath);
}
@Override
public int hashCode() {
return Objects.hash(size, style, fontPath);
}
}
}

View File

@@ -14,6 +14,8 @@ import java.util.function.Function;
public class TextBoxLayerRecipe implements LayerRecipe {
private Function<String, Function<RenderContext, Font>> fontStyleProvider;
private Function<String, String> glyphProvider;
private Function<RenderContext, Paint> paint;
private Function<String, Float> yShifts;
private Function<RenderContext, String[]> text;
private Function<RenderContext, TextBox> textBox;
private Function<RenderContext, Float> minYStart;
@@ -21,11 +23,15 @@ public class TextBoxLayerRecipe implements LayerRecipe {
public TextBoxLayerRecipe(
Function<String, Function<RenderContext, Font>> fontStyleProvider,
Function<String, String> glyphProvider,
Function<RenderContext, Paint> paint,
Function<String, Float> yShifts,
Function<RenderContext, String[]> text,
Function<RenderContext, TextBox> textBox,
Function<RenderContext, Float> minYStart) {
this.fontStyleProvider = fontStyleProvider;
this.glyphProvider = glyphProvider;
this.paint = paint;
this.yShifts = yShifts;
this.text = text;
this.textBox = textBox;
this.minYStart = minYStart;
@@ -46,6 +52,8 @@ public class TextBoxLayerRecipe implements LayerRecipe {
// graphics.fillRect(box.getX(), box.getY(), box.getWidth(), box.getHeight());
// graphics.setColor(Color.BLACK);
graphics.setPaint(paint.apply(renderContext));
FontRenderContext frc = graphics.getFontRenderContext();
// Dry run to check for height
@@ -96,21 +104,19 @@ public class TextBoxLayerRecipe implements LayerRecipe {
AttributedString string = new AttributedString(resultText.toString());
parseText(text, new TextParsingCallback() {
private int index = 0;
private boolean lowered = false;
private float yShift = 0;
@Override
public void appendText(String text, String style) {
String resultStyle = (style != null) ? style : "default";
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;
final float value = yShifts.apply(resultStyle);
float result = value - yShift;
if (result != 0) {
font = font.deriveFont(AffineTransform.getTranslateInstance(0, result));
yShift = value;
}
string.addAttribute(TextAttribute.FONT, font, index, index + text.length());
string.addAttribute(TextAttribute.FOREGROUND, Color.BLACK, index, index + text.length());
index += text.length();
}
});

View File

@@ -690,30 +690,11 @@
},
{
"comment": "Strength text",
"type": "text",
"type": "textBox",
"paint": "white",
"dropShadow": true,
"font": {
"type": "ttf",
"path": {
"type": "resolve",
"parent": {
"type": "property",
"name": "lotro.resources.folder"
},
"child": {
"type": "property",
"name": "numbers.font"
}
},
"size": {
"type": "property",
"name": "numbers.size"
},
"style": 0
},
"text": {
"type": "append",
"type": "appendText",
"values": [
{
"type": "optional",
@@ -727,15 +708,65 @@
"type": "cardPropertyGreaterThanZero",
"name": "strength"
},
"value": "+"
"value": "[symbol]+[/symbol]"
}
},
{
"type": "cardProperty",
"name": "strength"
"type": "replace",
"source": {
"type": "cardProperty",
"name": "strength"
},
"match": "-",
"with": "[symbol]-[/symbol]"
}
]
},
"font": {
"default": {
"type": "ttf",
"path": {
"type": "resolve",
"parent": {
"type": "property",
"name": "lotro.resources.folder"
},
"child": {
"type": "property",
"name": "numbers.font"
}
},
"size": {
"type": "property",
"name": "numbers.size"
},
"style": 0
},
"symbol": {
"type": "ttf",
"path": {
"type": "resolve",
"parent": {
"type": "property",
"name": "lotro.resources.folder"
},
"child": {
"type": "property",
"name": "numbers.font"
}
},
"size": 29,
"style": 0
}
},
"glyphs": {
"shire": "#",
"dwarven": "@"
},
"yShifts": {
"symbol": -10
},
"minYStart": 1,
"box": {
"type": "box",
"x": 45,
@@ -773,30 +804,11 @@
},
{
"comment": "Vitality text",
"type": "text",
"type": "textBox",
"paint": "white",
"dropShadow": true,
"font": {
"type": "ttf",
"path": {
"type": "resolve",
"parent": {
"type": "property",
"name": "lotro.resources.folder"
},
"child": {
"type": "property",
"name": "numbers.font"
}
},
"size": {
"type": "property",
"name": "numbers.size"
},
"style": 0
},
"text": {
"type": "append",
"type": "appendText",
"values": [
{
"type": "optional",
@@ -810,15 +822,65 @@
"type": "cardPropertyGreaterThanZero",
"name": "vitality"
},
"value": "+"
"value": "[symbol]+[/symbol]"
}
},
{
"type": "cardProperty",
"name": "vitality"
"type": "replace",
"source": {
"type": "cardProperty",
"name": "vitality"
},
"match": "-",
"with": "[symbol]-[/symbol]"
}
]
},
"font": {
"default": {
"type": "ttf",
"path": {
"type": "resolve",
"parent": {
"type": "property",
"name": "lotro.resources.folder"
},
"child": {
"type": "property",
"name": "numbers.font"
}
},
"size": {
"type": "property",
"name": "numbers.size"
},
"style": 0
},
"symbol": {
"type": "ttf",
"path": {
"type": "resolve",
"parent": {
"type": "property",
"name": "lotro.resources.folder"
},
"child": {
"type": "property",
"name": "numbers.font"
}
},
"size": 29,
"style": 0
}
},
"glyphs": {
"shire": "#",
"dwarven": "@"
},
"yShifts": {
"symbol": -10
},
"minYStart": 1,
"box": {
"type": "box",
"x": 45,
@@ -856,29 +918,11 @@
},
{
"comment": "Resistance text",
"type": "text",
"type": "textBox",
"paint": "white",
"font": {
"type": "ttf",
"path": {
"type": "resolve",
"parent": {
"type": "property",
"name": "lotro.resources.folder"
},
"child": {
"type": "property",
"name": "numbers.font"
}
},
"size": {
"type": "property",
"name": "numbers.size"
},
"style": 0
},
"dropShadow": true,
"text": {
"type": "append",
"type": "appendText",
"values": [
{
"type": "optional",
@@ -892,15 +936,65 @@
"type": "cardPropertyGreaterThanZero",
"name": "resistance"
},
"value": "+"
"value": "[symbol]+[/symbol]"
}
},
{
"type": "cardProperty",
"name": "resistance"
"type": "replace",
"source": {
"type": "cardProperty",
"name": "resistance"
},
"match": "-",
"with": "[symbol]-[/symbol]"
}
]
},
"font": {
"default": {
"type": "ttf",
"path": {
"type": "resolve",
"parent": {
"type": "property",
"name": "lotro.resources.folder"
},
"child": {
"type": "property",
"name": "numbers.font"
}
},
"size": {
"type": "property",
"name": "numbers.size"
},
"style": 0
},
"symbol": {
"type": "ttf",
"path": {
"type": "resolve",
"parent": {
"type": "property",
"name": "lotro.resources.folder"
},
"child": {
"type": "property",
"name": "numbers.font"
}
},
"size": 29,
"style": 0
}
},
"glyphs": {
"shire": "#",
"dwarven": "@"
},
"yShifts": {
"symbol": -10
},
"minYStart": 1,
"box": {
"type": "box",
"x": 45,
@@ -1028,6 +1122,9 @@
"shire": "#",
"dwarven": "@"
},
"yShifts": {
"glyph": 3
},
"minYStart": 0.10,
"box": {
"type": "box",