Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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)?

like image 688
Burkhard Avatar asked Oct 27 '08 10:10

Burkhard


Video Answer


4 Answers

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.

like image 151
PhiLho Avatar answered Nov 08 '22 15:11

PhiLho


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.

like image 29
Epaga Avatar answered Nov 08 '22 13:11

Epaga


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!

like image 32
Ryan Fox Avatar answered Nov 08 '22 13:11

Ryan Fox


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.

like image 26
msj121 Avatar answered Nov 08 '22 15:11

msj121