Forum Stats

  • 3,757,133 Users
  • 2,251,200 Discussions
  • 7,869,738 Comments

Discussions

GlyphVector bounds and kerning, ligatures

843807
843807 Member Posts: 46,582
edited Jun 11, 2009 5:16PM in Abstract Window Toolkit (AWT)
IS THIS A BUG?? Shouldn't GlyphVector report positions/bounds for glyphs which reflect kerning and ligatures, i.e., shouldn't it yield metrics which correspond to what is actually rendered by Graphics2D.drawString() or TextLayout.draw()? If not, what does provide this service? Below is a little app which will throw up a frame showing a string rendered in black, the GlyphVector's logicalBounds in yellow fill, and the logicalBounds of each glyph in red. You can see the bounding rectangles creep ahead as kerns and ligatures are encountered. I had to pick a font that actually does kerning and ligatures, DejaVu Sans, because on my system (JDK1.6-u13 on Ubuntu 9.04) the default fonts do not.
package examples;

import java.util.*;
import java.awt.*;
import java.awt.font.*;
import java.awt.geom.*;
import static java.awt.font.TextAttribute.*;
import javax.swing.*;


public class GlyphVectorBounds {

    public static void main(String[] args) {
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        JPanel p = new XPanel();
        p.setBackground(Color.WHITE);
        p.setOpaque(true);
        f.setContentPane(p);
        f.setSize(750, 150);
        f.setVisible(true);
    }

    static class XPanel extends JPanel {
        String text = "Tiffany's Terrific Toffee Taffy";
        Font baseFont = new Font("DejaVu Sans", Font.PLAIN, 48);
        Map attr= new HashMap();
        {
            attr.put(KERNING, KERNING_ON);
            attr.put(LIGATURES, LIGATURES_ON);
        }
        AffineTransform trans = AffineTransform.getTranslateInstance(20, 70);

        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2 = (Graphics2D) g;
            g2.setTransform(trans);
            Font font = baseFont.deriveFont(attr);
            g2.setFont(font);
            FontRenderContext frc = g2.getFontRenderContext();
            GlyphVector gv = font.createGlyphVector(frc, text);
            Rectangle2D vb = gv.getLogicalBounds();
            g2.setColor(Color.YELLOW);
            g2.fill(vb);
            g2.setColor(Color.RED);
            for (int i=0; i<gv.getNumGlyphs(); i++) {
                Shape gb = gv.getGlyphLogicalBounds(i);
                g2.draw(gb);
            }
            g2.setColor(Color.BLACK);
            g2.drawString(text, 0, 0);
        }
    }

}
Edited by: slovelace on Jun 5, 2009 12:06 AM

Edited by: slovelace on Jun 5, 2009 2:43 AM

Edited by: slovelace on Jun 5, 2009 4:11 PM

Comments

  • pietblok
    pietblok Member Posts: 577
    Hi,

    You may replace:
    	    GlyphVector gv = font.createGlyphVector(frc, text);
    with:
    	    char[] chars = text.toCharArray();
    	    GlyphVector gv = font.layoutGlyphVector(frc, chars, 0,
    		    chars.length, Font.LAYOUT_LEFT_TO_RIGHT);
    Piet
  • 843807
    843807 Member Posts: 46,582
    Thanks for the reply. I see that this works, but I can't say that my question, "IS THIS A BUG??", has been completely answered (did tick the option to give you the stars, though). Maybe this is just a documentation bug - here's the doc string for Font.createGlyphVector():

    Creates a GlyphVector by mapping characters to glyphs one-to-one based on the Unicode cmap in this Font. This method does no other processing besides the mapping of glyphs to characters. This means that this method is not useful for some scripts, such as Arabic, Hebrew, Thai, and Indic, that require reordering, shaping, or ligature substitution.

    And here's the opening one for layoutGlyphVector():

    Returns a new GlyphVector object, performing full layout of the text if possible. Full layout is required for complex text, such as Arabic or Hindi. Support for different scripts depends on the font and implementation.

    But I'm not using Arabic, Hebrew, Thai or Hindi. I'm dealing with English, which is to say Latin characters, in the usual way, though I am expecting "usual" to include kerning and ligatures. Okay, so maybe I should have spotted the phrase "ligature substitution" and thrown English into the list with Arabic, Hebrew, Thai, and Hindi. But what about kerning? Try typing the word "To" into any decent word processor (like the message window I'm typing into now) using any decent proportional font (any font smart enough to handle kerning - this excludes whatever Java is defaulting to on my system - is it Nimbus?) and you will see the "o" tucked part way under the arm of the "T". If this isn't done, the spacing looks horrible. I'm saying that kerning is routine, not a cause for setting the complexity flag. Or if for engineering reasons the complexity flag needs to be set, then the docs need to be changed.

    So there's my rant. Thanks again for the solution.
  • pietblok
    pietblok Member Posts: 577
    Yes, I think you are right when you spot an omission in the docs. It should state that full layout is needed not only for some languages, but for kerning as well.

    BTW, the solution works also when you pass a flag parameter of zero. So, it's not the complexity flag itself that does the trick, it is the method that will do a full layout, in contrast to the createGlyphVector methods.

    Thanks for the dukes. But they didn't arrive yet. Did you only tick a "answered" button or did you as well tick a "award" button? I don't exactly know how the mechanism works.

    Piet
  • 843807
    843807 Member Posts: 46,582
    Right. Dukes are on their way (this was my first post).

    Steve
This discussion has been closed.