Burkhard
Burkhard

Reputation: 14738

How to output a String on multiple lines using Graphics

My Program overrides public void paint(Graphics g, int x, int y); in order to draw some stings using g.drawString(someString, x+10, y+30);

Now someString can be quite long and thus, it may not fit on one line.

What is the best way to write the text on multiple line.
For instance, in a rectangle (x1, y1, x2, y2)?

Upvotes: 11

Views: 15835

Answers (5)

msj121
msj121

Reputation: 2842

Had some trouble myself this is my solution:

Graphics2D g=....
FontRenderContext frc = g.getFontRenderContext();
TextLayout layout = new TextLayout(text, font, frc);
String[] outputs = text.split("\n");
for(int i=0; i<outputs.length; i++){
    g.drawString(outputs[i], 15,(int) (15+i*layout.getBounds().getHeight()+0.5));

Hope that helps.... simple but it works.

Upvotes: 1

Ryan Fox
Ryan Fox

Reputation: 10269

Incrementally build your string, one word at a time, using Epaga's method to find the length of your string. Once the length is longer than your rectangle, remove the last word and print. Repeat until you run out of words.

It sounds like a bad algorithm, but for each line, it's really O(screenWidth/averageCharacterWidth) => O(1).

Still, use a StringBuffer to build your string!

Upvotes: 2

PhiLho
PhiLho

Reputation: 41162

Thanks to Epaga's hint and a couple of examples on the Net (not so obvious to find! I used mainly Break a Line for text layout), I could make a component to display wrapped text. It is incomplete, but at least it shows the intended effect.

class TextContainer extends JPanel
{
    private int m_width;
    private int m_height;
    private String m_text;
    private AttributedCharacterIterator m_iterator;
    private int m_start;
    private int m_end;

    public TextContainer(String text, int width, int height)
    {
        m_text = text;
        m_width = width;
        m_height = height;

        AttributedString styledText = new AttributedString(text);
        m_iterator = styledText.getIterator();
        m_start = m_iterator.getBeginIndex();
        m_end = m_iterator.getEndIndex();
    }

    public String getText()
    {
        return m_text;
    }

    public Dimension getPreferredSize()
    {
        return new Dimension(m_width, m_height);
    }

    public void paint(Graphics g)
    {
        super.paintComponent(g);

        Graphics2D g2 = (Graphics2D) g;
        FontRenderContext frc = g2.getFontRenderContext();

        LineBreakMeasurer measurer = new LineBreakMeasurer(m_iterator, frc);
        measurer.setPosition(m_start);

        float x = 0, y = 0;
        while (measurer.getPosition() < m_end)
        {
            TextLayout layout = measurer.nextLayout(m_width);

            y += layout.getAscent();
            float dx = layout.isLeftToRight() ?
                    0 : m_width - layout.getAdvance();

            layout.draw(g2, x + dx, y);
            y += layout.getDescent() + layout.getLeading();
        }
    }
}

Just for fun, I made it fitting a circle (alas, no justification, it seems):

public void paint(Graphics g)
{
    super.paintComponent(g);

    Graphics2D g2 = (Graphics2D) g;
    FontRenderContext frc = g2.getFontRenderContext();

    LineBreakMeasurer measurer = new LineBreakMeasurer(m_iterator, frc);
    measurer.setPosition(m_start);

    float y = 0;
    while (measurer.getPosition() < m_end)
    {
        double ix = Math.sqrt((m_width / 2 - y) * y);
        float x = m_width / 2.0F - (float) ix;
        int width = (int) ix * 2;

        TextLayout layout = measurer.nextLayout(width);

        y += layout.getAscent();
        float dx = layout.isLeftToRight() ?
                0 : width - layout.getAdvance();

        layout.draw(g2, x + dx, y);
        y += layout.getDescent() + layout.getLeading();
    }
}

I am not too sure about dx computation, though.

Upvotes: 11

michelemarcon
michelemarcon

Reputation: 24797

You can use a JLabel and embed the text with html.

JLabel.setText("<html>"+line1+"<br>"+line2);

Upvotes: 0

John Goering
John Goering

Reputation: 39050

java.awt.font.TextLayout might be helpful. Here's a snippet of their example code:

   Graphics2D g = ...;
   Point2D loc = ...;
   Font font = Font.getFont("Helvetica-bold-italic");
   FontRenderContext frc = g.getFontRenderContext();
   TextLayout layout = new TextLayout("This is a string", font, frc);
   layout.draw(g, (float)loc.getX(), (float)loc.getY());

   Rectangle2D bounds = layout.getBounds();
   bounds.setRect(bounds.getX()+loc.getX(),
                  bounds.getY()+loc.getY(),
                  bounds.getWidth(),
                  bounds.getHeight());
   g.draw(bounds);

Otherwise you could always use a Swing text element to do the job for you, just pass in the Graphics you want it to paint into.

Upvotes: 3

Related Questions