Java Text on Image

Convert text content to Image

The Graphics 2D API should be capable of achieving what you need. It has some complex text handling capabilities as well.

Sample Image

import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;

public class TextToGraphics {

public static void main(String[] args) {
String text = "Hello";

/*
Because font metrics is based on a graphics context, we need to create
a small, temporary image so we can ascertain the width and height
of the final image
*/
BufferedImage img = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = img.createGraphics();
Font font = new Font("Arial", Font.PLAIN, 48);
g2d.setFont(font);
FontMetrics fm = g2d.getFontMetrics();
int width = fm.stringWidth(text);
int height = fm.getHeight();
g2d.dispose();

img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
g2d = img.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
g2d.setFont(font);
fm = g2d.getFontMetrics();
g2d.setColor(Color.BLACK);
g2d.drawString(text, 0, fm.getAscent());
g2d.dispose();
try {
ImageIO.write(img, "png", new File("Text.png"));
} catch (IOException ex) {
ex.printStackTrace();
}

}

}

Also check out Writing/Saving and Image

WARNING I used this to generate 90k PNG images only to find that they can be viewed in IE but not in Chrome Version 70.0.3538.77

The above code works just fine for me (I changed the text color to WHITE so I could see it in chrome)

Running in Chrome

I was using Chrome 70.0.3538.77 on Mac OS Mojave 10.14 using Java 10.0.2. The resulting image was 4778x2411 pixels ...

Updated...

On IE that is black on white but on Chrome that is black on black. Yet I set background to white.

So what you're telling me is, a transparent PNG is been displayed differently on different browsers, because the browsers use different default backgrounds ... why are you surprised by this?

The original solution, deliberately, used a transparent based image. This is evident by the use of BufferedImage.TYPE_INT_ARGB when creating the image, which is applying a Alpha (A) based RGB color model.

This is unexpected as there is g2d.setBackground(Color.white).

No, actually, it is entirely expected, if only you understood what setBackground actually does and how it should be used

From the JavaDocs

Sets the background color for the Graphics2D context. The background
color is used for clearing a region. When a Graphics2D is constructed
for a Component, the background color is inherited from the Component.
Setting the background color in the Graphics2D context only affects
the subsequent clearRect calls and not the background color of the
Component. To change the background of the Component, use appropriate
methods of the Component.

From the "sounds" of things, you want a non-transparent image, with a filled background color. So, once again, it's off to the JavaDocs and a little bit of reading would have lead you to BufferedImage.TYPE_INT_RGB, which removes the Alpha channel, but you'd still have to fill the background of the image.

For this, I'd use Graphics2D#setColor and Graphics2D#fillRect, only because it works.

So, you'd end up with a modified version of the above which might look something like...

img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
g2d = img.createGraphics();
//...
g2d.setColor(Color.WHITE);
g2d.fillRect(0, 0, img.getWidth(), img.getHeight());
g2d.setColor(Color.BLACK);
g2d.drawString(text, 0, fm.getAscent());
g2d.dispose();
try {
ImageIO.write(img, "png", new File("Text.png"));
} catch (IOException ex) {
ex.printStackTrace();
}

If I change to "jpg" then I get orange/pink text on black background on both IE and Chrome

Well, this is related to a well known, and sadly, common issue/bug in ImageIO, which attempts to apply the alpha channel of transparent color models to the JPG, which doesn't support alpha channels.

See Issue using ImageIO.write jpg file: pink background for more details.

But the basic solution is to either use PNG, which supports alpha channels, or to use a non-transparent image.

So, the long and short of all this is. The problem is NOT with the original answer, nor is it with ImageIO, BufferedImage, Graphics, the AWT library, Chrome or IE, but with your lack of understanding of how these APIs (and the example) works.

Adding text to an image and make it fit into a rectangular region in Java

To wrap the text, create TextLayout objects from a LineBreakMeasurer, and use TextLayout.draw instead of drawing with Graphics.drawString.

First, you need to create an AttributedCharacterIterator. You can do that by creating an AttributedString from your text:

AttributedString attrStr = new AttributedString(text);
AttributedCharacterIterator iter = attrStr.getIterator();

Now you can create a LineBreakMeasurer:

Graphics2D g2 = (Graphics2D) g;

LineBreakMeasurer measurer = new LineBreakMeasurer(iter,
g2.getFontRenderContext());

You then obtain the lines as TextLayouts, one at a time, from the LineBreakMeasurer:

List<TextLayout> lines = new ArrayList<>();

while (measurer.getPosition() < text.length()) {
lines.add(measurer.nextLayout(rect.width));
}

Once you have them, you can figure out the total height:

float textHeight = 0;

for (TextLayout line : lines) {
textHeight += line.getAscent() + line.getDescent() + line.getLeading();
}

Finally, you can draw the lines:

float y = (rect.height - textHeight) / 2;

for (TextLayout line : lines) {
Rectangle2D bounds = line.getBounds();
float x = (rect.width - (float) bounds.getWidth()) / 2;

line.draw(g2, x, y + line.getAscent());

y += line.getAscent() + line.getDescent() + line.getLeading();
}

How to add text to an image in java?

It's easy, just get the Graphics object from the image and draw your string onto the image. This example (and output image) is doing that:

public static void main(String[] args) throws Exception {
final BufferedImage image = ImageIO.read(new URL(
"http://upload.wikimedia.org/wikipedia/en/2/24/Lenna.png"));

Graphics g = image.getGraphics();
g.setFont(g.getFont().deriveFont(30f));
g.drawString("Hello World!", 100, 100);
g.dispose();

ImageIO.write(image, "png", new File("test.png"));
}

Output (test.png):

output

Trying to add text to image in Java, results in the same image

You need to set the color to something besides BG for the String to show.
private static void end(Graphics g)

{
g.setColor(BG);

g.fillRect(0, 0, 900, 900);
g.setFont(new Font("TimesRoman", Font.PLAIN, 40)); g.setColor(FG) // Here g.drawString("GAME OVER!!", 10, 30);

}

How can I load an image and write text to it using Java?

Try this way:

import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;
import javax.imageio.ImageIO;

public class ImagingTest {

public static void main(String[] args) throws IOException {
String url = "http://icomix.eu/gr/images/non-batman-t-shirt-gross.jpg";
String text = "Hello Java Imaging!";
byte[] b = mergeImageAndText(url, text, new Point(200, 200));
FileOutputStream fos = new FileOutputStream("so2.png");
fos.write(b);
fos.close();
}

public static byte[] mergeImageAndText(String imageFilePath,
String text, Point textPosition) throws IOException {
BufferedImage im = ImageIO.read(new URL(imageFilePath));
Graphics2D g2 = im.createGraphics();
g2.drawString(text, textPosition.x, textPosition.y);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(im, "png", baos);
return baos.toByteArray();
}
}

Java Text on Image

You should be able to control the text quality with the rendering hints that are shown in the other responses. I know that it works because I have done it a lot, so I think it must be something else that causes the quality degradation in this case.

  1. How do you check the image quality? Do you paint the generated BufferedImage directly into a screen graphics in your java application or do you save to disk, i.e. as JPEG? If you save it to disk try to save it as PNG 24 and not as JPEG. I assume your desktop screen is running in True Color (32 or 24 bits color depth), right?
  2. Are you sure that the image is actually created as BufferedImage.TYPE_INT_RGB? If you have no control over the creation of the BufferedImage in your code try to create a new BufferedImage with TYPE_INT_RGB and paint the source into this one and then draw the text into it.
  3. Try setting the RenderingHints.KEY_DITHERING to RenderingHints.VALUE_DITHER_DISABLE (although this shouldn't be needed for true color images).

If this still doesn't help you to find the cause please provide some more information (VM version, operating system) and source code. The text rendering has become quite good with JDK 1.6 Update 10 but also earlier releases were able to produce clean text in images, it just didn't look as good because of less sophisticated antialiasing.

in response to your comment:

High contrast sharp edges as in text are a general problem with JPEG compression since it is not a lossless compression. If you really need to go with JPEG and can't switch to PNG, you can tune the compression quality of your saved image to find a better compromise between image quality and file size. See the following code on how to set the compression quality when you save a JPEG.

import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.plugins.jpeg.JPEGImageWriteParam;
import javax.imageio.stream.ImageOutputStream;

//----

float quality = 0.85f;
File outfile = new File( "MyImage.jpg" );
BufferedImage image = ...;

ImageWriter imgWriter = ImageIO.getImageWritersByFormatName( "jpg" ).next();
ImageOutputStream ioStream = ImageIO.createImageOutputStream( outfile );
imgWriter.setOutput( ioStream );

JPEGImageWriteParam jpegParams = new JPEGImageWriteParam( Locale.getDefault() );
jpegParams.setCompressionMode( ImageWriteParam.MODE_EXPLICIT );
jpegParams.setCompressionQuality( quality );

imgWriter.write( null, new IIOImage( image, null, null ), jpegParams );

ioStream.flush();
ioStream.close();
imgWriter.dispose();

Write multi line text on an image using Java

if you want to draw several lines you have to do it explicitly...

so first step is to 'detect' lines

String str = ... //some text with line breaks;
String [] lines = str.spilt("\n"); //breaking the lines into an array

second step is to draw all lines

Graphics g = image.getGraphics();
g.setFont(g.getFont().deriveFont(30f));
int lineHeight = g.getFontMetrics().getHeight();
//here comes the iteration over all lines
for(int lineCount = 0; lineCount < lines.length; lineCount ++){ //lines from above
int xPos = 100;
int yPos = 100 + lineCount * lineHeight;
String line = lines[lineCount];
g.drawString(line, xpos, yPos);
}
g.dispose();


Related Topics



Leave a reply



Submit