Search code examples
javagraphics2d

Align text to the right in a TextLayout, using Java Graphics2D API


So, I'm using the code in Java tutorial to draw a piece of text, but I don't know how to align text to the right margin.

I just included attstring.addAttribute(TextAttribute.RUN_DIRECTION, TextAttribute.RUN_DIRECTION_RTL); in the code for that case but it doesn't work.

protected float drawParagraph (Graphics2D g2, String text, float width, float x, float y, Boolean dir){
        AttributedString attstring = new AttributedString(text);
        attstring.addAttribute(TextAttribute.FONT, font);
        if (dir == TextAttribute.RUN_DIRECTION_RTL){
            attstring.addAttribute(TextAttribute.RUN_DIRECTION, TextAttribute.RUN_DIRECTION_RTL);
        }
        AttributedCharacterIterator paragraph = attstring.getIterator();
        int paragraphStart = paragraph.getBeginIndex();
        int paragraphEnd = paragraph.getEndIndex();
        FontRenderContext frc = g2.getFontRenderContext();
        LineBreakMeasurer lineMeasurer = new LineBreakMeasurer(paragraph, frc);

        // Set break width to width of Component.
        float breakWidth = width;
        float drawPosY = y;
        // Set position to the index of the first character in the paragraph.
        lineMeasurer.setPosition(paragraphStart);

        // Get lines until the entire paragraph has been displayed.
        while (lineMeasurer.getPosition() < paragraphEnd) {
            // Retrieve next layout. A cleverer program would also cache
            // these layouts until the component is re-sized.
            TextLayout layout = lineMeasurer.nextLayout(breakWidth);
            // Compute pen x position. If the paragraph is right-to-left we
            // will align the TextLayouts to the right edge of the panel.
            // Note: drawPosX is always where the LEFT of the text is placed.
            float drawPosX = (float) (layout.isLeftToRight()
                        ? x : breakWidth - layout.getAdvance());

            // Move y-coordinate by the ascent of the layout.
            drawPosY += layout.getAscent();

            // Draw the TextLayout at (drawPosX, drawPosY).
            layout.draw(g2, drawPosX, drawPosY);

            // Move y-coordinate in preparation for next layout.
            drawPosY += layout.getDescent() + layout.getLeading();
        }
        return drawPosY;
    }

Give a hand please, I'm lost ^^


Solution

  • The error was in the calculation of drawPosX. The working formula is drawPosX = (float) x + breakWidth - layout.getAdvance();

    I ended up doing a little fix to support center alignment and here is the code:

    public abstract class MyClass extends JPanel implements Printable{
    
        [...]
    
        public static enum Alignment {RIGHT, LEFT, CENTER};
    
        [...]
    
        /**
     * Draw paragraph.
     * Pinta un parrafo segun las localizaciones pasadas como parametros.
     *
     * @param g2 Drawing graphic.
     * @param text String to draw.
     * @param width Paragraph's desired width.
     * @param x Start paragraph's X-Position.
     * @param y Start paragraph's Y-Position.
     * @param dir Paragraph's alignment.
     * @return Next line Y-position to write to.
     */
    protected float drawParagraph (Graphics2D g2, String text, float width, float x, float y, Alignment alignment){
        AttributedString attstring = new AttributedString(text);
        attstring.addAttribute(TextAttribute.FONT, font);
        AttributedCharacterIterator paragraph = attstring.getIterator();
        int paragraphStart = paragraph.getBeginIndex();
        int paragraphEnd = paragraph.getEndIndex();
        FontRenderContext frc = g2.getFontRenderContext();
        LineBreakMeasurer lineMeasurer = new LineBreakMeasurer(paragraph, frc);
    
        // Set break width to width of Component.
        float breakWidth = width;
        float drawPosY = y;
        // Set position to the index of the first character in the paragraph.
        lineMeasurer.setPosition(paragraphStart);
    
        // Get lines until the entire paragraph has been displayed.
        while (lineMeasurer.getPosition() < paragraphEnd) {
            // Retrieve next layout. A cleverer program would also cache
            // these layouts until the component is re-sized.
            TextLayout layout = lineMeasurer.nextLayout(breakWidth);
            // Compute pen x position. 
            float drawPosX;
            switch (alignment){         
            case RIGHT:
                drawPosX = (float) x + breakWidth - layout.getAdvance();
                break;
            case CENTER:
                drawPosX = (float) x + (breakWidth - layout.getAdvance())/2;
                break;
            default: 
                drawPosX = (float) x;
            }
            // Move y-coordinate by the ascent of the layout.
            drawPosY += layout.getAscent();
    
            // Draw the TextLayout at (drawPosX, drawPosY).
            layout.draw(g2, drawPosX, drawPosY);
    
            // Move y-coordinate in preparation for next layout.
            drawPosY += layout.getDescent() + layout.getLeading();
        }
        return drawPosY;
    }
    }