I'm trying to render the text into an image using JAVA
AWT. When I use a font smaller than 100 points, everything works as expected. But when I use, for example, 240 points, then some parts of the text that overlap are transparent like in this image. In the sample image, I used this font. I used this font in a text editor without any problem.
The font is loaded using this code:
Font font = Font.createFont(Font.TRUETYPE_FONT, new File("src/main/resources/Autumn in November.ttf"));
Font derived = font.deriveFont(240f);
To render the text in the image I use this code:
public static void saveTextToImage(String text, Font font) throws IOException {
# Temp image to get the text dimensions
BufferedImage tempImage = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB);
Graphics2D tempGraphics = tempImage.createGraphics();
tempGraphics.setFont(font);
FontMetrics fontMetrics = tempGraphics.getFontMetrics();
FontRenderContext fontRenderContext = tempGraphics.getFontRenderContext();
GlyphVector gv = font.createGlyphVector(fontRenderContext, text);
Area area = new Area(gv.getOutline());
Rectangle2D rectangle2D = area.getBounds2D();
int width = (int) rectangle2D.getWidth();
int height = (int) rectangle2D.getHeight();
// Create a new image with white background
BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D graphics = img.createGraphics();
graphics.setColor(Color.WHITE);
graphics.fillRect(0, 0, width, height);
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// Draw the text on the image
graphics.setColor(Color.BLACK);
graphics.setFont(font);
graphics.drawString(text, 10, fontMetrics.getAscent() + 5);
// Save the image
File outputFile = new File(text + ".jpg");
ImageIO.write(img, "jpg", outputFile);
}
I've tried changing the font type to OTF
, adding and changing RenderingHints
and also switching the image type to PNG
, but without success. I don't know what else I could try.
This is indeed a strange behavior. I can’t tell you why this happens, but I can offer you a work-around; the problem does not occur when rendering the string’s glyphs one by one:
static final boolean USE_WORKAROUND = true;
public static void saveTextToImage(String text, Font font) throws IOException {
// Temp image to get the GlyphVector and text dimensions
BufferedImage tempImage = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB);
Graphics2D tempGraphics = tempImage.createGraphics();
tempGraphics.setFont(font);
tempGraphics.setRenderingHint(
RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
FontRenderContext fontRenderContext = tempGraphics.getFontRenderContext();
GlyphVector gv = font.createGlyphVector(fontRenderContext, text);
Rectangle2D rectangle2D = gv.getVisualBounds();
int width = (int)(rectangle2D.getWidth());
int height = (int)(rectangle2D.getHeight());
// Create a new image with white background
BufferedImage img
= new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D graphics = img.createGraphics();
graphics.setBackground(Color.WHITE);
graphics.clearRect(0, 0, width, height);
graphics.setRenderingHint(
RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
graphics.setColor(Color.BLACK);
// using GlyphVector avoids repeating the work already done above
if(!USE_WORKAROUND) {
graphics.drawGlyphVector(gv,
(float)-rectangle2D.getX(), (float)-rectangle2D.getY());
} else {
graphics.translate(-rectangle2D.getX(), -rectangle2D.getY());
for(int gix = 0, gnum = gv.getNumGlyphs(); gix < gnum; gix++) {
graphics.fill(gv.getGlyphOutline(gix));
}
}
// Save the image
File outputFile = new File(text + ".jpg");
ImageIO.write(img, "jpg", outputFile);
}
As a side note, I would use PNG instead of JPG as it is better suited for such images, but perhaps you don’t have this choice.
Further note that rendering an existing GlyphVector
via drawGlyphVector
has been documented to be “the fastest way to render a set of characters to the screen”, so if we have to perform this preparation work anyway for calculating the bounds, why not use it for the rendering…