Anil
Anil

Reputation: 1765

Write multi line text on an image using Java

I looked at java.awt.Graphics documentation, stackoverflow, could not find a solution. I have in input two things, an image file and the multi line text (paragraph). I need to write the multi line text on the image file and then save it as a new image. Wondering if I am missing something really simple here. I am open to using any good third party libraries as well.

final BufferedImage image = ImageIO.read(new File("c:/anil/Lenna.png"));

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

Above code writes just a single line on the image.

Upvotes: 0

Views: 4865

Answers (4)

billschen
billschen

Reputation: 749

see Drawing Multiple Lines of Text(Oracle Java Tutorials) and complete code use LineBreakMeasurer:

int width = 400;
int height = 500;
BufferedImage bufferedImage = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
Graphics2D g2d =  bufferedImage.createGraphics();

g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);

g2d.setColor(Color.MAGENTA);

Hashtable<TextAttribute,Object> map = new Hashtable<TextAttribute,Object>();
map.put(TextAttribute.FAMILY, "微软雅黑");
map.put(TextAttribute.SIZE,new Float(18.0));
AttributedString vanGogh = new AttributedString(
"Many people 中国 believe that Vincent van Gogh painted his best works " +
"during the two-year period he spent in Provence. Here is where he " +
"painted The Starry Night--which some consider to be his greatest " +
"work of all. However, as his artistic brilliance reached new " +
"heights in Provence, his physical and mental health plummeted. ",
map);

AttributedCharacterIterator paragraph = vanGogh.getIterator();
int paragraphStart   = paragraph.getBeginIndex();
int paragraphEnd = paragraph.getEndIndex();
FontRenderContext frc = g2d.getFontRenderContext();
LineBreakMeasurer lineMeasurer = new LineBreakMeasurer(paragraph, frc);
float breakWidth = 250f;
float drawPosY = 20;
float drawPosx = 0f;
lineMeasurer.setPosition(paragraphStart);
while(lineMeasurer.getPosition()< paragraphEnd ){
    TextLayout layout = lineMeasurer.nextLayout(breakWidth);
    drawPosx = layout.isLeftToRight()?0:breakWidth-layout.getAdvance();
    drawPosY += layout.getAscent();
    layout.draw(g2d,drawPosx,drawPosY);
    drawPosY += layout.getDescent() + layout.getLeading();
}


g2d.dispose();

File file = new File("myimage.png");
ImageIO.write(bufferedImage,"png",file);

file = new File("myimage.jpg");
ImageIO.write(bufferedImage,"jpg",file);

Upvotes: 0

krzydyn
krzydyn

Reputation: 1032

JLabel accepts simple html to format text. Then you can paint it on your image:

JLabel l=new JLabel("<html>line1<br>line2");
l.setSize(l.getPrefferedSize());
g.translate(10,10); // fixed location
l.paint(g);

edit: complete example

public class OP_32110247 extends JPanel {
    private final JLabel l = new JLabel();
    private final BufferedImage image;
    public OP_32110247(String imgfile, String txt) throws IOException {
        image = ImageIO.read(new URL(imgfile));
        l.setText(txt);
        l.setFont(getFont().deriveFont(Font.BOLD,30f));
        l.setSize(l.getPreferredSize());
        l.setForeground(Color.GREEN);
        Dimension d = new Dimension(image.getWidth(), image.getHeight());
        setPreferredSize(d);
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Dimension d = getSize();
        g.drawImage(image, 0, 0, null);
        //place text in center of image
        g.translate((d.width-l.getWidth())/2, (d.height-l.getHeight())/2);
        l.paint(g);
    }

    public static void main(String[] args) throws IOException {
        String txt = "<html>line1<br>line2";
        String image = "http://kysoft.pl/proj/java/j+c.png";
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
        f.setContentPane(new OP_32110247(image,txt));
        f.pack();
        f.setVisible(true);
    }
}

Upvotes: 1

Dakshinamurthy Karra
Dakshinamurthy Karra

Reputation: 5463

The best solution is to extend JLabel and override paintComponent. Create the Label with the image required. After calling super.paintComponent draw your text, each line positioning below another using the font metrics. Something like below:

class ImageWithTextLabel extends JLabel {
    List<String> lines = new ArrayList<>();
    Point textPosition = new Point(0,0);
    private Font textFont;

    private ImageWithTextLabel(Icon image) {
        super(image);
    }

    public void addText(String text) {
        lines.add(text);
    }

    public void setTextPosition(Point textPosition) {
        this.textPosition = textPosition;
    }

    @Override protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        int from = textPosition.y ;
        g.setFont(textFont);
        for (String line : lines) {
            g.drawString(line, textPosition.x, from);
            from += g.getFontMetrics().getHeight();
        }
    }

    public void setTextFont(Font font) {
        textFont = font;
    }
}

Upvotes: 0

Martin Frank
Martin Frank

Reputation: 3454

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();

Upvotes: 3

Related Questions