0

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.

Anil
  • 1,735
  • 2
  • 20
  • 28

4 Answers4

3

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();
Martin Frank
  • 3,445
  • 1
  • 27
  • 47
1

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);
    }
}
krzydyn
  • 1,012
  • 9
  • 19
  • This is probably the best solution, since it's simple and involves the least fooling around with low level text rendering. I'll edit to make the code example complete, but first: Per [this answer](https://stackoverflow.com/a/2420834/6286797), as of Java 7, JLabels automatically wrap html text so manual `
    ` tags are unnecessary.
    – Mark Peschel Jul 24 '17 at 17:34
  • Actually, I'm having some difficulty getting this to work. I am unable to affect the location of the painted text with either g.setClip() or l.setLocation or l.setBounds(). It is always rendered about 40 px below the top of the image. – Mark Peschel Jul 24 '17 at 21:34
0

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;
    }
}
Dakshinamurthy Karra
  • 5,353
  • 1
  • 17
  • 28
0

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);
billschen
  • 699
  • 6
  • 11