Summary: | Java does not display correctly some glyphs | ||
---|---|---|---|
Product: | DejaVu | Reporter: | Mathieu Legris <mathieu> |
Component: | Sans | Assignee: | Deja Vu bugs <dejavu-bugs> |
Status: | NEW --- | QA Contact: | |
Severity: | normal | ||
Priority: | medium | Keywords: | notourbug |
Version: | unspecified | ||
Hardware: | Other | ||
OS: | All | ||
Whiteboard: | |||
i915 platform: | i915 features: | ||
Attachments: |
Source code of a program that generates font texture
Output Image showing incorrectly placed glyphs Package with source code, font and jar file |
Description
Mathieu Legris
2007-03-16 09:48:38 UTC
Thank you Mathieu. I'm a bit confused with the advance, height or width given. They sound really wrong. What unit are they in? (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); } } > Here is the source code of a modified program: Could you create a new attachment? The code is broken with the reformatting. Created attachment 9201 [details]
Source code of a program that generates font texture
Created attachment 9202 [details]
Output Image showing incorrectly placed glyphs
Comment on attachment 9202 [details]
Output Image showing incorrectly placed glyphs
Bugzilla does not display correctly this image probably because of the alpha channel.
Use Gimp with alpha activated to view this image.
(In reply to comment #6) > (From update of attachment 9202 [details]) Looking at the attachment, it seems Java is getting mixed up when base and diacritics are not in a specific order. btw, I'm getting a code dump when running the class. Created attachment 9568 [details]
Package with source code, font and jar file
(In reply to comment #7) > (In reply to comment #6) > > (From update of attachment 9202 [details] [details]) > Looking at the attachment, it seems Java is getting mixed up when base and > diacritics are not in a specific order. > > btw, I'm getting a code dump when running the class. > I am really sorry. I quickly extracted the code from my projects without giving any documentation and forgiving that the others have other stuff to do... This time I send a full package with a jar inside: -Unzip the package -Go inside the package directory -Run the jar: java -jar FontTexture.jar DejaVuLGC_Sans-regular.ttf Obviously, you can use other fonts. You should check that you are using the Sun implementation of Java if you are under Linux because I have not tested this program with the GNU implementation. |
Use of freedesktop.org services, including Bugzilla, is subject to our Code of Conduct. How we collect and use information is described in our Privacy Policy.