[DejaVu-bugs] [Bug 10319] Java does not display correctly some glyphs

bugzilla-daemon at freedesktop.org bugzilla-daemon at freedesktop.org
Sat Mar 17 02:59:34 PDT 2007


http://bugs.freedesktop.org/show_bug.cgi?id=10319





------- Comment #2 from mathieu at thenesis.com  2007-03-17 01:59 PST -------
(In reply to comment #1)
> Thank you Mathieu.
> 
> I'm a bit confused with the advance, height or width given. They sound really
> wrong. What unit are they in?
> 

All units are in pixels.

Here is the source code of a modified program:

package fonttest;


import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.font.FontRenderContext;
import java.awt.font.GlyphVector;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;


public class FontTextureTest {
        public static final int MAX_FONT_STYLES=        256;
        public static final int MAX_TEXTURES=           256;

        public static final int PIXEL_A=0, PIXEL_RGBA=1;

        public static final int
                STYLE_REGULAR=          0,
                STYLE_BOLD=                     1,
                STYLE_ITALIC=           2,
                STYLE_BOLD_ITALIC=      3;

    public static final String STYLE_STRING[]={ "regular", "bold", "italic",
"bold-italic" };

        public static int
                TEXTURE_SINGLE                          =0, // All characters
of all styles on a single texture.
                TEXTURE_SINGLE_PER_STYLE        =1, // All characters of a
style on a single texture.
                TEXTURE_NEW_PER_STYLE           =2, // Each style has its own
set of textures.
                TEXTURE_SHARED                          =3; // All styles
shared the textures.

        public static final int HORIZONTAL_SPACING=1;
        public static final int VERTICAL_SPACING=1;

        public static class Parameters {
                public String fontName;
                public int size;
                public int blocks[][];
                public int textureWidth, textureHeight;
                public int textureOption;
                public boolean antialiasFlag;
        }

        public static class Style {
                public int ascent, descent, leading, height;
                public int maxAscent, maxDescent, maxAdvance;
                public int maxWidth, maxHeight;
                public int nbCharacters;
                public Glyph characters[];              
        }

        public static class Glyph {
                public int codePoint;
                public short tx, ty, tw, th; // Coordinates in texture.
                public short x, y; // Reference point.
                public short advance;
                public short texture;
        }

        public static class Texture {
                public int width, height;
                public BufferedImage data;
//              public byte data[];
        }

        public boolean debugFlag=false;
        public int pixelFormat=PIXEL_A;
        public int nbStyles;
        public Style styles[]=new Style[MAX_FONT_STYLES];
        public int nbTextures;
        public Texture textures[]=new Texture[MAX_TEXTURES];


        //
        // Data used for construction.
        //

        // Data for the current font style.
        private Font font;
        private int nbRequestedCharacters;
        private int requestedCharacters[];              
        private BufferedImage image;
        private Graphics2D graphics;
        private FontRenderContext fontRenderContext;
        private FontMetrics fontMetrics;
        private int lastTextureY=0;

   
//--------------------------------------------------------------------------------
        // Test.
   
//--------------------------------------------------------------------------------
    public static void main(String[] args) {
        try {
                buildTest("DejaVuLGC_Sans-regular", 20, LGC_BLOCKS, 1024, 1024,
true);
        } catch(Exception e) {
            e.printStackTrace();
            return;
        }

    }

    // Blocks for Latin-Greek-Cyrillic languages and some symbols.
    private static final int LGC_BLOCKS[][]={
        { -1, -1 },
        { 0x00000000, 0x0000007F }, // Basic Latin
        { 0x00000080, 0x000000FF }, // Latin-1 Supplement
        { 0x00000100, 0x0000017F }, // Latin Extended-A
        { 0x00000180, 0x0000024F }, // Latin Extended-B
        { 0x00000250, 0x000002AF }, // IPA Extensions
        { 0x00000370, 0x000003FF }, // Greek and Coptic
        { 0x00000400, 0x000004FF }, // Cyrillic
        { 0x00000500, 0x0000052F }, // Cyrillic Supplement
        { 0x00001E00, 0x00001EFF }, // Latin Extended Additional
        { 0x00001F00, 0x00001FFF }, // Greek Extended
        { 0x00002000, 0x0000206F }, // General Punctuation
        { 0x000020A0, 0x000020CF }, // Currency Symbols
        { 0x00002100, 0x0000214F }, // Letterlike Symbols
        { 0x00002190, 0x000021FF }, // Arrows
        { 0x00002200, 0x000022FF }, // Mathematical Operators
        { 0x000025A0, 0x000025FF }, // Geometric Shapes
        { 0x00002600, 0x000026FF }, // Miscellaneous Symbols
        { 0x00002700, 0x000027BF }, // Dingbats
        { 0x00002B00, 0x00002BFF }, // Miscellaneous Symbols and Arrows
        { 0x00002C60, 0x00002C7F }, // Latin Extended-C
    };

    private static void buildTest(String fontName, int size, int blocks[][],
int textureWidth, int textureHeight, boolean antialiasFlag) {
        FontTextureTest fontTexture=new
FontTextureTest(FontTextureTest.PIXEL_RGBA, true);
        FontTextureTest.Parameters p=new FontTextureTest.Parameters();
        p.size=size;
        p.blocks=blocks;
        p.textureWidth=textureWidth; p.textureHeight=textureHeight;
        p.textureOption=FontTextureTest.TEXTURE_SINGLE_PER_STYLE;
        p.antialiasFlag=antialiasFlag;
            p.fontName=fontName+".ttf";
        fontTexture.addStyle(p);
        fontTexture.printInfos();
        saveFontTextureImages(fontTexture, fontName, size);
    }

    private static void saveFontTextureImages(FontTextureTest ft, String
fontName, int size) {
        for (int i=0; i<ft.nbTextures; i++) {
                saveImage(fontName+"-"+size+"-("+i+").png",
ft.textures[i].data);
        }       
    }

    private static boolean saveImage(String filename, BufferedImage img) {
        try {
            File file=new File(filename);
            ImageIO.write(img, "png", file);
        } catch (IOException e) {
            System.out.println("Cannot write destination image.");
            return true;
        }
        return false;
    }


        public FontTextureTest(int pixelFormat, boolean debugFlag) {
                this.pixelFormat=pixelFormat;
                this.debugFlag=debugFlag;
        }

   
//--------------------------------------------------------------------------------
        // Style.
   
//--------------------------------------------------------------------------------
    public int addStyle(Parameters p) {
        if (nbStyles>MAX_FONT_STYLES) {
                System.out.println("Too many font styles.");
                return -1;
        }

        // Create the list of requested characters.
        if (createCharacterList(p.blocks)) return -1;

        // Load the font.
        if ((font=createFont(p.fontName, p.size))==null) return -1;

        // Create an image.
        if (image==null || p.textureOption==TEXTURE_SINGLE_PER_STYLE ||
p.textureOption==TEXTURE_NEW_PER_STYLE) {
                if (createTexture(p)) return -1;
        } else {
                graphics.setFont(font);
            fontRenderContext=graphics.getFontRenderContext();
            fontMetrics=graphics.getFontMetrics();
        }

        Style fs=new Style();

        // Retrieve font infos.
        {
                Rectangle2D
maxCharBounds=fontMetrics.getMaxCharBounds(graphics);
                fs.ascent=fontMetrics.getAscent();
                fs.descent=fontMetrics.getDescent();
                fs.leading=fontMetrics.getLeading();
                fs.height=fontMetrics.getHeight();
                fs.maxAscent=fontMetrics.getMaxAscent();
                fs.maxDescent=fontMetrics.getMaxDescent();
                fs.maxAdvance=fontMetrics.getMaxAdvance();
               
fs.maxWidth=(int)Math.ceil(maxCharBounds.getMaxX()-maxCharBounds.getMinX());
               
fs.maxHeight=(int)Math.ceil(maxCharBounds.getMaxY()-maxCharBounds.getMinY());
                if (fs.height>fs.maxHeight) fs.maxHeight=fs.height;
        }

        // Build the texture and the character array.
        Glyph characters[]=new Glyph[nbRequestedCharacters];
        int nbEffectiveCharacters=0, nbUndisplayableCharacters=0,
nbInvalidCharacters=0;
                int characterX=0, characterY=lastTextureY;
        for (int i=0; i<nbRequestedCharacters; i++) {
                int codePoint=requestedCharacters[i];
                int characterWidth, characterHeight, characterAdvance;
                GlyphVector glyph=null;
                Rectangle characterBounds=null;

                if (codePoint>=0) {
                        if (!Character.isDefined(codePoint)) {
//                      System.out.println("Invalid codepoint: "+codePoint);
                                continue;
                        }
                        if (!font.canDisplay(codePoint)) {
//                      System.out.println("Cannot display codepoint:
"+codePoint);
                                nbUndisplayableCharacters++;
                                continue;
                        }
                        glyph=font.createGlyphVector(fontRenderContext,
Character.toChars(codePoint));
                } else {
                        // Missing glyph.
                        int missingGlyph[]={ font.getMissingGlyphCode() };
                        glyph=font.createGlyphVector(fontRenderContext,
missingGlyph);
                }

                // Get character infos.
                characterBounds=glyph.getGlyphPixelBounds(0, fontRenderContext,
0, fs.maxAscent);
                characterWidth=characterBounds.width;
characterHeight=characterBounds.height;
characterAdvance=fontMetrics.charWidth(codePoint);                
                // Check if the character is valid.
                if (characterWidth<0 || characterWidth>fs.maxWidth) {
                        System.out.println("Invalid character width:
codePoint="+codePoint+"; width="+characterWidth);
                        nbInvalidCharacters++; continue;
                }
                if (characterHeight<0 || characterHeight>fs.maxHeight) {
                        System.out.println("Invalid character height:
codePoint="+codePoint+"; height="+characterHeight);
                        nbInvalidCharacters++; continue;
                }
                if (characterAdvance<0 || characterAdvance>fs.maxAdvance) {
                System.out.println("Invalid character advance:
codePoint="+codePoint+"; advance="+characterAdvance);
                        nbInvalidCharacters++; continue;
                }

                // Create a new character.
                Glyph gi=new Glyph();
                characters[nbEffectiveCharacters++]=gi;
                gi.codePoint=codePoint;
                gi.advance=(short)characterAdvance;

                boolean hasGlyph=(characterWidth>0 && characterHeight>0);
                if (hasGlyph) {
                // Check if the character can fit on the current line.
                int
nextCharacterX=characterX+HORIZONTAL_SPACING+characterWidth+HORIZONTAL_SPACING;
                        int
nextCharacterY=characterY+VERTICAL_SPACING+fs.maxHeight+VERTICAL_SPACING;
                if (nextCharacterX>=textures[nbTextures-1].width) {
                        if (nextCharacterY>=textures[nbTextures-1].height) {
                                if (p.textureOption==TEXTURE_SINGLE ||
p.textureOption==TEXTURE_SINGLE_PER_STYLE) {
                                        System.out.println("Cannot store all
characters on the same texture.");
                                        break;
                                } else if (createTexture(p)) return -1;
                                nextCharacterY=lastTextureY;
                        }
                        characterX=0;
nextCharacterX=HORIZONTAL_SPACING+characterWidth+HORIZONTAL_SPACING;
                        characterY=nextCharacterY;
                }

//              characterHeight=fs.maxHeight;
                gi.tx=(short)(characterX+HORIZONTAL_SPACING);
gi.ty=(short)(characterY+characterBounds.y+VERTICAL_SPACING);
                gi.tw=(short)(characterWidth); gi.th=(short)(characterHeight);
                gi.x=(short)(characterBounds.x);
gi.y=(short)(characterBounds.y-fs.maxAscent);
                gi.texture=(short)(nbTextures-1);

                // Draw the character.
                if (debugFlag) {
                                graphics.setColor(Color.RED);
                                graphics.drawRect(gi.tx-HORIZONTAL_SPACING,
gi.ty-VERTICAL_SPACING, gi.tw-1+2*HORIZONTAL_SPACING,
gi.th-1+2*VERTICAL_SPACING);
                }
                        if (glyph!=null) {
                            int
drawX=characterX+HORIZONTAL_SPACING-characterBounds.x,
drawY=characterY+VERTICAL_SPACING+fs.maxAscent;
                                graphics.setColor(Color.WHITE);
                                graphics.drawGlyphVector(glyph, drawX, drawY);
                        }

                        characterX=nextCharacterX;
                } else {
                gi.tx=0; gi.ty=0;
                gi.tw=0; gi.th=0;
                gi.x=0; gi.y=0;
                gi.texture=(short)(-1);
                }
        }

        fs.nbCharacters=nbEffectiveCharacters;
        fs.characters=characters;
        styles[nbStyles++]=fs;

        characterY+=VERTICAL_SPACING+fs.maxHeight+VERTICAL_SPACING;
        lastTextureY=characterY;

        System.out.println("Undisplayable: "+nbUndisplayableCharacters+";
Invalid: "+nbInvalidCharacters);
        System.out.println("Total texture height: "+characterY);
        System.out.println();

        return nbStyles-1;
    }

    private static Font createFont(String fontName, int size) {
        Font originalFont;
        try {
                File file=new File(fontName);
                originalFont=Font.createFont(Font.TRUETYPE_FONT, file);
        } catch (Exception e) {
                System.out.println("Cannot open file: "+fontName);
                return null;
        }       
        Font font=originalFont.deriveFont((float)size);
        System.out.println("Building: "+fontName);
        System.out.println("Family: "+font.getFamily()+"; Face name:
"+font.getFontName()+"; Logical name: "+font.getName()+"; Size:
"+font.getSize());
        System.out.println("Num glyphs: "+font.getNumGlyphs()); 
        return font;
    }

    private boolean createCharacterList(int blocks[][]) {
        // Count characters.
        nbRequestedCharacters=0;
        for (int i=0; i<blocks.length; i++) {
                int n=blocks[i][1]-blocks[i][0];
                if (n<0) {
                        System.out.println("Invalid block: "+i+"; Range:
"+blocks[i][0]+"-"+blocks[i][1]);
                        return true;
                }
                nbRequestedCharacters+=n+1;
        }
        if (nbRequestedCharacters<=0) {
                System.out.println("The number of characters is 0.");
                return true;
        }

        // Build characters list.
        requestedCharacters=new int[nbRequestedCharacters];
        {
                int k=0;
                for (int i=0; i<blocks.length; i++) {
                        int n=blocks[i][1]-blocks[i][0];
                        for (int j=0; j<=n; j++)
requestedCharacters[k+j]=blocks[i][0]+j;
                        k+=n+1;
                }
        }

        System.out.println("Requested characters: "+nbRequestedCharacters);
        return false;
    }

    private boolean createTexture(Parameters p) {
        if (nbTextures>=MAX_TEXTURES) {
                System.out.println("Too many textures");
                return true;
        }

        switch (pixelFormat) {
        case PIXEL_A:
                image=new java.awt.image.BufferedImage(p.textureWidth,
p.textureHeight, BufferedImage.TYPE_BYTE_GRAY);
                break;
        case PIXEL_RGBA:
                image=new java.awt.image.BufferedImage(p.textureWidth,
p.textureHeight, BufferedImage.TYPE_4BYTE_ABGR);
                break;
        default: return true;
        }
        lastTextureY=0;

        graphics=image.createGraphics();
        graphics.setRenderingHint(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);
        if (p.antialiasFlag) {
                graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
                graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
        }
        graphics.setFont(font);
        fontRenderContext=graphics.getFontRenderContext();
        fontMetrics=graphics.getFontMetrics();

        Texture texture=new Texture();
        textures[nbTextures++]=texture;
        texture.width=p.textureWidth; texture.height=p.textureHeight;
        texture.data=image;

        return false;
    }

   
//--------------------------------------------------------------------------------
    // Infos.
   
//--------------------------------------------------------------------------------
    private static final String PIXEL_FORMAT_STRING[]={ "A", "RGBA" };

    public void printInfos() {
        System.out.println("Pixel format: "+PIXEL_FORMAT_STRING[pixelFormat]);
        System.out.println("Num styles: "+nbStyles);
        System.out.println("Num textures: "+nbTextures);
        System.out.println();
        for (int i=0; i<nbStyles; i++) {
                if (styles[i]!=null) printInfos(i);
        }
    }

    public void printInfos(int f) {
        Style fs=styles[f];
        System.out.println("Num characters: "+fs.nbCharacters);
        System.out.println("Ascent: "+fs.ascent+"; Descent: "+fs.descent+";
Leading: "+fs.leading+"; Height: "+fs.height);
        System.out.println("Max ascent: "+fs.maxAscent+"; Max descent:
"+fs.maxDescent+"; Max advance: "+fs.maxAdvance);        
        System.out.println("Max width: "+fs.maxWidth+"; Max height:
"+fs.maxHeight);
        System.out.println();
    }

    public void printInfos(Glyph ci) {
        System.out.println("codepoint: "+ci.codePoint+"; tx="+ci.tx+";
ty="+ci.ty+"; tw="+ci.tw+"; th="+ci.th+"; x="+ci.x+"; y="+ci.y);
    }
}



-- 
Configure bugmail: http://bugs.freedesktop.org/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
You are the assignee for the bug, or are watching the assignee.


More information about the DejaVu-bugs mailing list