[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