Developing new font metrics

git-svn-id: svn://ultimatepp.org/upp/trunk@1358 f0d560ea-af0d-0410-9eb7-867de7ffcac7
This commit is contained in:
cxl 2009-07-05 14:48:01 +00:00
parent 3af6cadacb
commit dad8006d13
7 changed files with 920 additions and 798 deletions

View file

@ -1,223 +1,223 @@
#include "FontInfo.h"
enum {
CG_NONE,
CG_CAPITAL,
CG_SMALL
};
enum {
CG_GRAVE = 0x60,
CG_ACUTE = 0xb4,
CG_CEDILLA = 0xb8,
CG_MACRON = 175,
CG_CIRCUMFLEX = 0x2c6,
CG_TILDE = 0x2dc,
CG_DOT_ABOVE = 0x2d9,
CG_OGONEK = 0x2db,
CG_STROKE = '-',
CG_BREVE = 0x2d8,
CG_CARON = 0x2c7,
CG_MIDDLE_DOT = 0xb7,
CG_DOUBLE_ACUTE = 0x2dd,
CG_DIAERESIS = 0xa8,
CG_RING_ABOVE = 0x2da,
CG_COMMA_T = ',',
CG_COMMA_UR = 1,
CG_COMMA_URI,
};
struct CGInfo {
byte type;
char ascii;
wchar mark;
}
gc_info[128] = {
{ CG_CAPITAL, 'A', CG_MACRON },
{ CG_SMALL, 'a', CG_MACRON },
{ CG_CAPITAL, 'A', CG_BREVE },
{ CG_SMALL, 'a', CG_BREVE },
{ CG_CAPITAL, 'A', CG_OGONEK },
{ CG_SMALL, 'a', CG_OGONEK },
{ CG_CAPITAL, 'C', CG_ACUTE },
{ CG_SMALL, 'c', CG_ACUTE },
{ CG_CAPITAL, 'C', CG_CIRCUMFLEX },
{ CG_SMALL, 'c', CG_CIRCUMFLEX },
{ CG_CAPITAL, 'C', CG_DOT_ABOVE },
{ CG_SMALL, 'c', CG_DOT_ABOVE },
{ CG_CAPITAL, 'C', CG_CARON },
{ CG_SMALL, 'c', CG_CARON },
{ CG_CAPITAL, 'D', CG_CARON },
{ CG_SMALL, 'd', CG_COMMA_UR },
{ CG_CAPITAL, 'D', CG_STROKE },
{ CG_SMALL, 'd', CG_STROKE },
{ CG_CAPITAL, 'E', CG_MACRON },
{ CG_SMALL, 'e', CG_MACRON },
{ CG_CAPITAL, 'E', CG_BREVE },
{ CG_SMALL, 'e', CG_BREVE },
{ CG_CAPITAL, 'E', CG_DOT_ABOVE },
{ CG_SMALL, 'e', CG_DOT_ABOVE },
{ CG_CAPITAL, 'E', CG_OGONEK },
{ CG_SMALL, 'e', CG_OGONEK },
{ CG_CAPITAL, 'E', CG_CARON },
{ CG_SMALL, 'e', CG_CARON },
{ CG_CAPITAL, 'G', CG_CIRCUMFLEX },
{ CG_SMALL, 'g', CG_CIRCUMFLEX },
{ CG_CAPITAL, 'G', CG_BREVE },
{ CG_SMALL, 'g', CG_BREVE },
{ CG_CAPITAL, 'G', CG_DOT_ABOVE },
{ CG_SMALL, 'g', CG_DOT_ABOVE },
{ CG_CAPITAL, 'G', CG_CEDILLA },
{ CG_SMALL, 'g', CG_CEDILLA },
{ CG_CAPITAL, 'H', CG_CIRCUMFLEX },
{ CG_SMALL, 'h', CG_CIRCUMFLEX },
{ CG_CAPITAL, 'H', CG_STROKE },
{ CG_SMALL, 'h', CG_STROKE },
{ CG_CAPITAL, 'I', CG_TILDE },
{ CG_SMALL, 'i', CG_TILDE },
{ CG_CAPITAL, 'I', CG_MACRON },
{ CG_SMALL, 'i', CG_MACRON },
{ CG_CAPITAL, 'I', CG_BREVE },
{ CG_SMALL, 'i', CG_BREVE },
{ CG_CAPITAL, 'I', CG_OGONEK },
{ CG_SMALL, 'i', CG_OGONEK },
{ CG_CAPITAL, 'I', CG_DOT_ABOVE },
{ CG_NONE, 0, 0 }, // , CG_SMALL, 'DOTLESS I
{ CG_NONE, 0, 0 }, // LATIN CAPITAL LIGATURE IJ
{ CG_NONE, 0, 0 }, // LATIN SMALL LIGATURE IJ
{ CG_CAPITAL, 'J', CG_CIRCUMFLEX },
{ CG_SMALL, 'j', CG_CIRCUMFLEX },
{ CG_CAPITAL, 'K', CG_CEDILLA },
{ CG_SMALL, 'k', CG_CEDILLA },
{ CG_NONE, 0, 0 }, // CG_SMALL, 'KRA
{ CG_CAPITAL, 'L', CG_ACUTE },
{ CG_SMALL, 'l', CG_ACUTE },
{ CG_CAPITAL, 'L', CG_CEDILLA },
{ CG_SMALL, 'l', CG_CEDILLA },
{ CG_CAPITAL, 'L', CG_COMMA_URI },
{ CG_SMALL, 'l', CG_COMMA_UR },
{ CG_CAPITAL, 'L', CG_MIDDLE_DOT },
{ CG_SMALL, 'l', CG_MIDDLE_DOT },
{ CG_CAPITAL, 'L', CG_STROKE },
{ CG_SMALL, 'l', CG_STROKE },
{ CG_CAPITAL, 'N', CG_ACUTE },
{ CG_SMALL, 'n', CG_ACUTE },
{ CG_CAPITAL, 'N', CG_CEDILLA },
{ CG_SMALL, 'n', CG_CEDILLA },
{ CG_CAPITAL, 'N', CG_CARON },
{ CG_SMALL, 'n', CG_CARON },
{ CG_NONE, 0, 0 }, // CG_SMALL, 'N PRECEDED BY APOSTROPHE
{ CG_NONE, 0, 0 }, //CG_CAPITAL, 'ENG
{ CG_NONE, 0, 0 }, //CG_SMALL, 'ENG
{ CG_CAPITAL, 'O', CG_MACRON },
{ CG_SMALL, 'o', CG_MACRON },
{ CG_CAPITAL, 'O', CG_BREVE },
{ CG_SMALL, 'o', CG_BREVE },
{ CG_CAPITAL, 'O', CG_DOUBLE_ACUTE },
{ CG_SMALL, 'o', CG_DOUBLE_ACUTE },
{ CG_NONE, 0, 0 }, // LATIN CAPITAL LIGATURE OE
{ CG_NONE, 0, 0 }, // LATIN SMALL LIGATURE OE
{ CG_CAPITAL, 'R', CG_ACUTE },
{ CG_SMALL, 'r', CG_ACUTE },
{ CG_CAPITAL, 'R', CG_CEDILLA },
{ CG_SMALL, 'r', CG_CEDILLA },
{ CG_CAPITAL, 'R', CG_CARON },
{ CG_SMALL, 'r', CG_CARON },
{ CG_CAPITAL, 'S', CG_ACUTE },
{ CG_SMALL, 's', CG_ACUTE },
{ CG_CAPITAL, 'S', CG_CIRCUMFLEX },
{ CG_SMALL, 's', CG_CIRCUMFLEX },
{ CG_CAPITAL, 'S', CG_CEDILLA },
{ CG_SMALL, 's', CG_CEDILLA },
{ CG_CAPITAL, 'S', CG_CARON },
{ CG_SMALL, 's', CG_CARON },
{ CG_CAPITAL, 'T', CG_CEDILLA },
{ CG_SMALL, 't', CG_CEDILLA },
{ CG_CAPITAL, 'T', CG_CARON },
{ CG_SMALL, 't', CG_COMMA_T },
{ CG_CAPITAL, 'T', CG_STROKE },
{ CG_SMALL, 't', CG_STROKE },
{ CG_CAPITAL, 'U', CG_TILDE },
{ CG_SMALL, 'u', CG_TILDE },
{ CG_CAPITAL, 'U', CG_MACRON },
{ CG_SMALL, 'u', CG_MACRON },
{ CG_CAPITAL, 'U', CG_BREVE },
{ CG_SMALL, 'u', CG_BREVE },
{ CG_CAPITAL, 'U', CG_RING_ABOVE },
{ CG_SMALL, 'u', CG_RING_ABOVE },
{ CG_CAPITAL, 'U', CG_DOUBLE_ACUTE },
{ CG_SMALL, 'u', CG_DOUBLE_ACUTE },
{ CG_CAPITAL, 'U', CG_OGONEK },
{ CG_SMALL, 'u', CG_OGONEK },
{ CG_CAPITAL, 'W', CG_CIRCUMFLEX },
{ CG_SMALL, 'w', CG_CIRCUMFLEX },
{ CG_CAPITAL, 'Y', CG_CIRCUMFLEX },
{ CG_SMALL, 'y', CG_CIRCUMFLEX },
{ CG_CAPITAL, 'Y', CG_DIAERESIS },
{ CG_CAPITAL, 'Z', CG_ACUTE },
{ CG_SMALL, 'z', CG_ACUTE },
{ CG_CAPITAL, 'Z', CG_DOT_ABOVE },
{ CG_SMALL, 'z', CG_DOT_ABOVE },
{ CG_CAPITAL, 'Z', CG_CARON },
{ CG_SMALL, 'z', CG_CARON },
{ CG_NONE, 0, 0 } // CG_SMALL, 'LONG S
};
bool Compose(Font font, int chr, ComposedGlyph& cg)
{
if(chr < 256 || chr > 256 + 128)
return false;
CGInfo f = gc_info[chr - 256];
if(f.type == CG_NONE)
return false;
GlyphInfo gi = GetGlyphInfo(font, f.ascii);
if(gi.IsMissing())
return false;
int cw = gi.width;
CommonFontInfo fi = GetFontInfo(font);
gi = GetGlyphInfo(font, f.mark);
if(gi.IsMissing())
return false;
int mw = gi.width;
cg.mark_font = font;
cg.mark_pos.x = cg.mark_pos.y = 0;
cg.basic_char = f.ascii;
cg.mark_char = f.mark;
if(cg.mark_char == CG_COMMA_UR && fi.fixedpitch)
cg.mark_char = CG_CARON;
if(cg.mark_char == CG_COMMA_T) {
cg.mark_pos.y -= 3 * font.GetHeight() / 4;
cg.mark_pos.x += 4 * cw / 10;
if(font.IsItalic())
cg.mark_pos.x += mw / 2;
}
else
if(cg.mark_char == CG_COMMA_UR) {
cg.mark_pos.y -= 2 * font.GetHeight() / 3;
cg.mark_pos.x += cw - mw / 4;
cg.mark_char = ',';
if(font.IsItalic())
cg.mark_pos.x += mw / 3;
}
else
if(cg.mark_char == CG_COMMA_URI) {
cg.mark_pos.y -= 2 * font.GetHeight() / 3;
cg.mark_pos.x += cw - mw / 2;
cg.mark_char = ',';
if(font.IsItalic())
cg.mark_pos.x += mw / 3;
}
else
if(cg.mark_char != CG_STROKE) {
if(cg.mark_char != CG_OGONEK && cg.mark_char != CG_CEDILLA && f.type == CG_CAPITAL) {
cg.mark_font = font(9 * font.GetHeight() / 10);
mw = GetGlyphInfo(cg.mark_font, f.mark).width;
cg.mark_pos.y -= cg.mark_char == CG_RING_ABOVE ? font.GetHeight() / 19
: font.GetHeight() / 10;
}
cg.mark_pos.x += (cw - mw) / 2;
if(font.IsItalic())
cg.mark_pos.x += mw / 5;
}
return true;
}
#include "FontInfo.h"
enum {
CG_NONE,
CG_CAPITAL,
CG_SMALL
};
enum {
CG_GRAVE = 0x60,
CG_ACUTE = 0xb4,
CG_CEDILLA = 0xb8,
CG_MACRON = 175,
CG_CIRCUMFLEX = 0x2c6,
CG_TILDE = 0x2dc,
CG_DOT_ABOVE = 0x2d9,
CG_OGONEK = 0x2db,
CG_STROKE = '-',
CG_BREVE = 0x2d8,
CG_CARON = 0x2c7,
CG_MIDDLE_DOT = 0xb7,
CG_DOUBLE_ACUTE = 0x2dd,
CG_DIAERESIS = 0xa8,
CG_RING_ABOVE = 0x2da,
CG_COMMA_T = ',',
CG_COMMA_UR = 1,
CG_COMMA_URI,
};
struct CGInfo {
byte type;
char ascii;
wchar mark;
}
gc_info[128] = {
{ CG_CAPITAL, 'A', CG_MACRON },
{ CG_SMALL, 'a', CG_MACRON },
{ CG_CAPITAL, 'A', CG_BREVE },
{ CG_SMALL, 'a', CG_BREVE },
{ CG_CAPITAL, 'A', CG_OGONEK },
{ CG_SMALL, 'a', CG_OGONEK },
{ CG_CAPITAL, 'C', CG_ACUTE },
{ CG_SMALL, 'c', CG_ACUTE },
{ CG_CAPITAL, 'C', CG_CIRCUMFLEX },
{ CG_SMALL, 'c', CG_CIRCUMFLEX },
{ CG_CAPITAL, 'C', CG_DOT_ABOVE },
{ CG_SMALL, 'c', CG_DOT_ABOVE },
{ CG_CAPITAL, 'C', CG_CARON },
{ CG_SMALL, 'c', CG_CARON },
{ CG_CAPITAL, 'D', CG_CARON },
{ CG_SMALL, 'd', CG_COMMA_UR },
{ CG_CAPITAL, 'D', CG_STROKE },
{ CG_SMALL, 'd', CG_STROKE },
{ CG_CAPITAL, 'E', CG_MACRON },
{ CG_SMALL, 'e', CG_MACRON },
{ CG_CAPITAL, 'E', CG_BREVE },
{ CG_SMALL, 'e', CG_BREVE },
{ CG_CAPITAL, 'E', CG_DOT_ABOVE },
{ CG_SMALL, 'e', CG_DOT_ABOVE },
{ CG_CAPITAL, 'E', CG_OGONEK },
{ CG_SMALL, 'e', CG_OGONEK },
{ CG_CAPITAL, 'E', CG_CARON },
{ CG_SMALL, 'e', CG_CARON },
{ CG_CAPITAL, 'G', CG_CIRCUMFLEX },
{ CG_SMALL, 'g', CG_CIRCUMFLEX },
{ CG_CAPITAL, 'G', CG_BREVE },
{ CG_SMALL, 'g', CG_BREVE },
{ CG_CAPITAL, 'G', CG_DOT_ABOVE },
{ CG_SMALL, 'g', CG_DOT_ABOVE },
{ CG_CAPITAL, 'G', CG_CEDILLA },
{ CG_SMALL, 'g', CG_CEDILLA },
{ CG_CAPITAL, 'H', CG_CIRCUMFLEX },
{ CG_SMALL, 'h', CG_CIRCUMFLEX },
{ CG_CAPITAL, 'H', CG_STROKE },
{ CG_SMALL, 'h', CG_STROKE },
{ CG_CAPITAL, 'I', CG_TILDE },
{ CG_SMALL, 'i', CG_TILDE },
{ CG_CAPITAL, 'I', CG_MACRON },
{ CG_SMALL, 'i', CG_MACRON },
{ CG_CAPITAL, 'I', CG_BREVE },
{ CG_SMALL, 'i', CG_BREVE },
{ CG_CAPITAL, 'I', CG_OGONEK },
{ CG_SMALL, 'i', CG_OGONEK },
{ CG_CAPITAL, 'I', CG_DOT_ABOVE },
{ CG_NONE, 0, 0 }, // , CG_SMALL, 'DOTLESS I
{ CG_NONE, 0, 0 }, // LATIN CAPITAL LIGATURE IJ
{ CG_NONE, 0, 0 }, // LATIN SMALL LIGATURE IJ
{ CG_CAPITAL, 'J', CG_CIRCUMFLEX },
{ CG_SMALL, 'j', CG_CIRCUMFLEX },
{ CG_CAPITAL, 'K', CG_CEDILLA },
{ CG_SMALL, 'k', CG_CEDILLA },
{ CG_NONE, 0, 0 }, // CG_SMALL, 'KRA
{ CG_CAPITAL, 'L', CG_ACUTE },
{ CG_SMALL, 'l', CG_ACUTE },
{ CG_CAPITAL, 'L', CG_CEDILLA },
{ CG_SMALL, 'l', CG_CEDILLA },
{ CG_CAPITAL, 'L', CG_COMMA_URI },
{ CG_SMALL, 'l', CG_COMMA_UR },
{ CG_CAPITAL, 'L', CG_MIDDLE_DOT },
{ CG_SMALL, 'l', CG_MIDDLE_DOT },
{ CG_CAPITAL, 'L', CG_STROKE },
{ CG_SMALL, 'l', CG_STROKE },
{ CG_CAPITAL, 'N', CG_ACUTE },
{ CG_SMALL, 'n', CG_ACUTE },
{ CG_CAPITAL, 'N', CG_CEDILLA },
{ CG_SMALL, 'n', CG_CEDILLA },
{ CG_CAPITAL, 'N', CG_CARON },
{ CG_SMALL, 'n', CG_CARON },
{ CG_NONE, 0, 0 }, // CG_SMALL, 'N PRECEDED BY APOSTROPHE
{ CG_NONE, 0, 0 }, //CG_CAPITAL, 'ENG
{ CG_NONE, 0, 0 }, //CG_SMALL, 'ENG
{ CG_CAPITAL, 'O', CG_MACRON },
{ CG_SMALL, 'o', CG_MACRON },
{ CG_CAPITAL, 'O', CG_BREVE },
{ CG_SMALL, 'o', CG_BREVE },
{ CG_CAPITAL, 'O', CG_DOUBLE_ACUTE },
{ CG_SMALL, 'o', CG_DOUBLE_ACUTE },
{ CG_NONE, 0, 0 }, // LATIN CAPITAL LIGATURE OE
{ CG_NONE, 0, 0 }, // LATIN SMALL LIGATURE OE
{ CG_CAPITAL, 'R', CG_ACUTE },
{ CG_SMALL, 'r', CG_ACUTE },
{ CG_CAPITAL, 'R', CG_CEDILLA },
{ CG_SMALL, 'r', CG_CEDILLA },
{ CG_CAPITAL, 'R', CG_CARON },
{ CG_SMALL, 'r', CG_CARON },
{ CG_CAPITAL, 'S', CG_ACUTE },
{ CG_SMALL, 's', CG_ACUTE },
{ CG_CAPITAL, 'S', CG_CIRCUMFLEX },
{ CG_SMALL, 's', CG_CIRCUMFLEX },
{ CG_CAPITAL, 'S', CG_CEDILLA },
{ CG_SMALL, 's', CG_CEDILLA },
{ CG_CAPITAL, 'S', CG_CARON },
{ CG_SMALL, 's', CG_CARON },
{ CG_CAPITAL, 'T', CG_CEDILLA },
{ CG_SMALL, 't', CG_CEDILLA },
{ CG_CAPITAL, 'T', CG_CARON },
{ CG_SMALL, 't', CG_COMMA_T },
{ CG_CAPITAL, 'T', CG_STROKE },
{ CG_SMALL, 't', CG_STROKE },
{ CG_CAPITAL, 'U', CG_TILDE },
{ CG_SMALL, 'u', CG_TILDE },
{ CG_CAPITAL, 'U', CG_MACRON },
{ CG_SMALL, 'u', CG_MACRON },
{ CG_CAPITAL, 'U', CG_BREVE },
{ CG_SMALL, 'u', CG_BREVE },
{ CG_CAPITAL, 'U', CG_RING_ABOVE },
{ CG_SMALL, 'u', CG_RING_ABOVE },
{ CG_CAPITAL, 'U', CG_DOUBLE_ACUTE },
{ CG_SMALL, 'u', CG_DOUBLE_ACUTE },
{ CG_CAPITAL, 'U', CG_OGONEK },
{ CG_SMALL, 'u', CG_OGONEK },
{ CG_CAPITAL, 'W', CG_CIRCUMFLEX },
{ CG_SMALL, 'w', CG_CIRCUMFLEX },
{ CG_CAPITAL, 'Y', CG_CIRCUMFLEX },
{ CG_SMALL, 'y', CG_CIRCUMFLEX },
{ CG_CAPITAL, 'Y', CG_DIAERESIS },
{ CG_CAPITAL, 'Z', CG_ACUTE },
{ CG_SMALL, 'z', CG_ACUTE },
{ CG_CAPITAL, 'Z', CG_DOT_ABOVE },
{ CG_SMALL, 'z', CG_DOT_ABOVE },
{ CG_CAPITAL, 'Z', CG_CARON },
{ CG_SMALL, 'z', CG_CARON },
{ CG_NONE, 0, 0 } // CG_SMALL, 'LONG S
};
bool Compose(Font font, int chr, ComposedGlyph& cg)
{
if(chr < 256 || chr > 256 + 128)
return false;
CGInfo f = gc_info[chr - 256];
if(f.type == CG_NONE)
return false;
GlyphInfo gi = GetGlyphInfo(font, f.ascii);
if(!gi.IsNormal())
return false;
int cw = gi.width;
CommonFontInfo fi = GetFontInfo(font);
gi = GetGlyphInfo(font, f.mark);
if(!gi.IsNormal())
return false;
int mw = gi.width;
cg.mark_font = font;
cg.mark_pos.x = cg.mark_pos.y = 0;
cg.basic_char = f.ascii;
cg.mark_char = f.mark;
if(cg.mark_char == CG_COMMA_UR && fi.fixedpitch)
cg.mark_char = CG_CARON;
if(cg.mark_char == CG_COMMA_T) {
cg.mark_pos.y -= 3 * font.GetHeight() / 4;
cg.mark_pos.x += 4 * cw / 10;
if(font.IsItalic())
cg.mark_pos.x += mw / 2;
}
else
if(cg.mark_char == CG_COMMA_UR) {
cg.mark_pos.y -= 2 * font.GetHeight() / 3;
cg.mark_pos.x += cw - mw / 4;
cg.mark_char = ',';
if(font.IsItalic())
cg.mark_pos.x += mw / 3;
}
else
if(cg.mark_char == CG_COMMA_URI) {
cg.mark_pos.y -= 2 * font.GetHeight() / 3;
cg.mark_pos.x += cw - mw / 2;
cg.mark_char = ',';
if(font.IsItalic())
cg.mark_pos.x += mw / 3;
}
else
if(cg.mark_char != CG_STROKE) {
if(cg.mark_char != CG_OGONEK && cg.mark_char != CG_CEDILLA && f.type == CG_CAPITAL) {
cg.mark_font = font(9 * font.GetHeight() / 10);
mw = GetGlyphInfo(cg.mark_font, f.mark).width;
cg.mark_pos.y -= cg.mark_char == CG_RING_ABOVE ? font.GetHeight() / 19
: font.GetHeight() / 10;
}
cg.mark_pos.x += (cw - mw) / 2;
if(font.IsItalic())
cg.mark_pos.x += mw / 5;
}
return true;
}

View file

@ -1,117 +1,170 @@
#include "FontInfo.h"
struct CharEntry {
GlyphInfo info;
word chr;
Font font;
};
CharEntry fc_cache_global[4096];
CharEntry GetGlyphEntry(Font font, int chr)
{
DrawLock __;
unsigned hash = CombineHash(font.GetHashValue(), chr) & 511;
CharEntry& e = fc_cache_global[hash];
if(e.font != font || e.chr != chr) {
e.font = font;
e.chr = chr;
e.info = GetGlyphInfoSys(font, chr);
if(!e.info.IsNormal()) {
ComposedGlyph cg;
Font rfnt;
if(Compose(font, chr, cg)) {
e.info.lspc = -1;
e.info.rspc = cg.basic_char;
}
else
if(Replace(font, chr, rfnt))
e.info.lspc = rfnt.GetFace();
else
e.info.lspc = -2;
}
}
return e;
}
thread__ CharEntry fc_cache[512];
GlyphInfo GetGlyphInfo(Font font, int chr)
{
unsigned hash = CombineHash(font.GetHashValue(), chr) & 511;
CharEntry& e = fc_cache[hash];
if(e.font != font || e.chr != chr)
e = GetGlyphEntry(font, chr);
return e.info;
}
void GlyphMetrics(GlyphInfo& f, Font font, int chr)
{
if(f.IsReplaced())
f = GetGlyphInfo(font().Face(f.lspc), chr);
if(f.IsComposed()) {
f = GetGlyphInfo(font, f.rspc);
if(f.IsComposedLM())
f.rspc += f.width / 2;
}
}
GlyphInfo GetGlyphMetrics(Font font, int chr)
{
GlyphInfo f = GetGlyphInfo(font, chr);
if(f.IsMissing())
f = GetGlyphInfo(font, '?');
GlyphMetrics(f, font, chr);
return f;
}
struct FontEntry {
CommonFontInfo info;
Font font;
};
thread__ FontEntry fi_cache[64];
const CommonFontInfo& GetFontInfo(Font font)
{
unsigned hash = font.GetHashValue() & 63;
FontEntry& e = fi_cache[hash];
if(e.font != font) {
DrawLock __;
e.font = font;
e.info = GetFontInfoSys(font);
}
return e.info;
}
void DoDrawText(Draw& w, int x, int y, int angle, const wchar *text, Font font, Color ink,
int n, const int *dx)
{
for(int i = 0; i < n; i++) {
wchar chr = text[i];
GlyphInfo gi = GetGlyphInfo(font, chr);
if(gi.IsNormal())
w.DrawTextOp(x, y, angle, &chr, font, ink, 1, NULL);
else
if(gi.IsReplaced()) {
FontInfo fi = font.Info();
Font fnt = font;
fnt.Face(gi.lspc);
FontInfo fi2 = fnt.Info();
w.DrawTextOp(x, y + fi.GetAscent() - fi2.GetAscent(), angle, &chr, fnt, ink, 1, NULL);
GlyphMetrics(gi, font, chr);
}
else
if(gi.IsComposed()) {
ComposedGlyph cg;
Compose(font, chr, cg);
w.DrawTextOp(x, y, angle, &cg.basic_char, font, ink, 1, NULL);
w.DrawTextOp(x + cg.mark_pos.x, y + cg.mark_pos.y, angle, &cg.mark_char, cg.mark_font, ink, 1, NULL);
GlyphMetrics(gi, font, chr);
}
if(dx)
x += *dx++;
else
x += gi.width;
}
}
#include "FontInfo.h"
void Std(Font& font)
{
if(IsNull(font))
font = StdFont();
if(font.GetFace() == 0)
font.Face(GetStdFont.GetFace());
if(font.GetHeight() == 0)
font.Height(GetStdFont.GetHeight());
}
struct CharEntry {
GlyphInfo info;
word chr;
Font font;
};
CharEntry fc_cache_global[4093];
bool IsNormal(Font font, int chr)
{
DrawLock __;
CharEntry& e = fc_cache_global[CombineHash(font.GetHashValue(), chr) % 4093];
if(e.font == font || e.chr == chr)
return e.info.IsNormal();
return GetGlyphInfoSys(font, chr).IsNormal();
}
CharEntry GetGlyphEntry(Font font, int chr, unsigned hash)
{
DrawLock __;
CharEntry& e = fc_cache_global[hash % 4093];
if(e.font != font || e.chr != chr) {
e.font = font;
e.chr = chr;
e.info = GetGlyphInfoSys(font, chr);
if(!e.info.IsNormal()) {
ComposedGlyph cg;
Font rfnt;
if(Compose(font, chr, cg)) {
e.info.lspc = -1;
e.info.rspc = cg.basic_char;
}
else
if(Replace(font, chr, rfnt))
e.info.lspc = rfnt.GetFace();
else
e.info.lspc = -2;
}
}
return e;
}
thread__ CharEntry fc_cache[512];
GlyphInfo GetGlyphInfo(Font font, int chr)
{
Std(font);
unsigned hash = CombineHash(font.GetHashValue(), chr);
CharEntry& e = fc_cache[hash & 511];
if(e.font != font || e.chr != chr)
e = GetGlyphEntry(font, chr, hash);
return e.info;
}
void GlyphMetrics(GlyphInfo& f, Font font, int chr)
{
if(f.IsReplaced())
f = GetGlyphInfo(font().Face(f.lspc), chr);
if(f.IsComposed()) {
f = GetGlyphInfo(font, f.rspc);
if(f.IsComposedLM())
f.rspc += f.width / 2;
}
}
GlyphInfo GetGlyphMetrics(Font font, int chr)
{
GlyphInfo f = GetGlyphInfo(font, chr);
if(f.IsMissing())
f = GetGlyphInfo(font, '?');
GlyphMetrics(f, font, chr);
return f;
}
struct FontEntry {
CommonFontInfo info;
Font font;
};
thread__ FontEntry fi_cache[64];
const CommonFontInfo& GetFontInfo(Font font)
{
Std(font);
unsigned hash = font.GetHashValue() & 63;
FontEntry& e = fi_cache[hash];
if(e.font != font) {
DrawLock __;
e.font = font;
e.info = GetFontInfoSys(font);
}
return e.info;
}
void DoDrawText(Draw& w, int x, int y, int angle, const wchar *text, Font font, Color ink,
int n, const int *dx)
{
Std(font);
double sina;
double cosa;
int d = 0;
if(angle)
Draw::SinCos(angle, sina, cosa); //TODO global sin tables!
for(int i = 0; i < n; i++) {
wchar chr = text[i];
GlyphInfo gi = GetGlyphInfo(font, chr);
if(gi.IsNormal())
if(angle)
w.DrawTextOp(int(x + cosa * d), int(y - sina * d), angle, &chr, font, ink, 1, NULL);
else {
int c = 1;
int dd = 0;
if(!dx)
while(c < n) {
GlyphInfo gi2 = GetGlyphInfo(font, text[i + c]);
if(!gi2.IsNormal())
break;
c++;
dd += gi.width;
gi = gi2;
}
w.DrawTextOp(x + d, y, 0, text + i, font, ink, c, NULL);
i += c - 1;
d += dd;
}
else
if(gi.IsReplaced()) {
FontInfo fi = font.Info();
Font fnt = font;
fnt.Face(gi.lspc);
FontInfo fi2 = fnt.Info();
if(angle)
w.DrawTextOp(int(x + cosa * d), int(y - sina * (fi.GetAscent() - fi2.GetAscent() + d)),
angle, &chr, fnt, ink, 1, NULL);
else
w.DrawTextOp(x + d, y + fi.GetAscent() - fi2.GetAscent(), 0, &chr, fnt, ink, 1, NULL);
GlyphMetrics(gi, font, chr);
}
else
if(gi.IsComposed()) {
ComposedGlyph cg;
Compose(font, chr, cg);
if(angle) {
w.DrawTextOp(int(x + cosa * d), int(y - sina * d), angle, &cg.basic_char, font, ink, 1, NULL);
w.DrawTextOp(int(x + cosa * (d + cg.mark_pos.x)), int(y - sina * (cg.mark_pos.y + d)), angle, &cg.mark_char, cg.mark_font, ink, 1, NULL);
}
else {
w.DrawTextOp(x + d, y, angle, &cg.basic_char, font, ink, 1, NULL);
w.DrawTextOp(x + cg.mark_pos.x + d, y + cg.mark_pos.y, angle, &cg.mark_char, cg.mark_font, ink, 1, NULL);
}
GlyphMetrics(gi, font, chr);
}
if(dx)
d += *dx++;
else
d += gi.width;
}
}

View file

@ -1,75 +1,79 @@
#ifndef _FontInfo_FontInfo_h_
#define _FontInfo_FontInfo_h_
#include <CtrlLib/CtrlLib.h>
using namespace Upp;
struct CommonFontInfo : Moveable<CommonFontInfo> {
int ascent;
int descent;
int external;
int internal;
int height;
int lineheight;
int overhang;
int avewidth;
int maxwidth;
int firstchar;
int charcount;
int default_char;
int spacebefore;
int spaceafter;
bool fixedpitch;
bool scaleable;
#ifdef PLATFORM_POSIX
int underline_thickness;
int underline_position;
String path;
#endif
};
CommonFontInfo GetFontInfoSys(Font font);
struct GlyphInfo : Moveable<GlyphInfo> {
int16 width;
int16 lspc;
int16 rspc;
bool IsNormal() const { return (word)width != 0x8000; }
bool IsComposed() const { return !IsNormal() && (lspc == -1 || lspc == -11); }
bool IsComposedLM() const { return !IsNormal() && lspc == -11; }
bool IsReplaced() const { return !IsNormal() && lspc >= 0; }
bool IsMissing() const { return !IsNormal() && lspc == -2; }
};
GlyphInfo GetGlyphInfoSys(Font font, int chr);
struct FaceInfo : Moveable<FaceInfo> {
String name;
dword info;
};
Vector<FaceInfo> GetAllFacesSys();
GlyphInfo GetGlyphInfo(Font font, int chr);
void GlyphMetrics(GlyphInfo& f, Font font, int chr);
GlyphInfo GetGlyphMetrics(Font font, int chr);
const CommonFontInfo& GetFontInfo(Font font);
struct ComposedGlyph {
wchar basic_char;
Point mark_pos;
wchar mark_char;
Font mark_font;
};
bool Compose(Font fnt, int chr, ComposedGlyph& cs);
bool Replace(Font fnt, int chr, Font& rfnt);
void DoDrawText(Draw& w, int x, int y, int angle, const wchar *text, Font font, Color ink,
int n, const int *dx);
#endif
#ifndef _FontInfo_FontInfo_h_
#define _FontInfo_FontInfo_h_
#include <CtrlLib/CtrlLib.h>
using namespace Upp;
struct CommonFontInfo : Moveable<CommonFontInfo> {
int ascent;
int descent;
int external;
int internal;
int height;
int lineheight;
int overhang;
int avewidth;
int maxwidth;
int firstchar;
int charcount;
int default_char;
int spacebefore;
int spaceafter;
bool fixedpitch;
bool scaleable;
#ifdef PLATFORM_POSIX
int underline_thickness;
int underline_position;
String path;
#endif
};
CommonFontInfo GetFontInfoSys(Font font);
struct GlyphInfo : Moveable<GlyphInfo> {
int16 width;
int16 lspc;
int16 rspc;
bool IsNormal() const { return (word)width != 0x8000; }
bool IsComposed() const { return !IsNormal() && (lspc == -1 || lspc == -11); }
bool IsComposedLM() const { return !IsNormal() && lspc == -11; }
bool IsReplaced() const { return !IsNormal() && lspc >= 0; }
bool IsMissing() const { return !IsNormal() && lspc == -2; }
};
GlyphInfo GetGlyphInfoSys(Font font, int chr);
struct FaceInfo : Moveable<FaceInfo> {
String name;
dword info;
};
Vector<FaceInfo> GetAllFacesSys();
void Std(Font& font);
bool IsNormal(Font font, int chr);
GlyphInfo GetGlyphInfo(Font font, int chr);
void GlyphMetrics(GlyphInfo& f, Font font, int chr);
GlyphInfo GetGlyphMetrics(Font font, int chr);
const CommonFontInfo& GetFontInfo(Font font);
struct ComposedGlyph {
wchar basic_char;
Point mark_pos;
wchar mark_char;
Font mark_font;
};
bool Compose(Font fnt, int chr, ComposedGlyph& cs);
bool Replace(Font fnt, int chr, Font& rfnt);
void DoDrawText(Draw& w, int x, int y, int angle, const wchar *text, Font font, Color ink,
int n, const int *dx);
#endif

View file

@ -8,7 +8,7 @@ file
Win32.cpp,
X11.cpp,
Compose.cpp,
FontReplace.cpp,
FontReplace.cpp charset "UTF-8",
FontInfo.cpp,
main.cpp charset "UTF-8",
app.tpp;

View file

@ -1,44 +1,46 @@
#include "FontInfo.h"
static const char *sFontReplacements[] = {
"Symbol",
"Arial Unicode MS",
"Arial",
"MS UI Gothic",
"MS Mincho",
"Arial",
"Gulim",
"SimSun",
"PMingLiU",
};
bool Replace(Font fnt, int chr, Font& rfnt)
{
static Vector<int> rface;
ONCELOCK {
for(int i = 0; i < __countof(sFontReplacements); i++) {
int q = Font::FindFaceNameIndex(sFontReplacements[i]);
if(q > 0)
rface.Add(q);
}
DDUMP(rface.GetCount());
}
static Font last;
static Font lastR;
if(last == fnt && GetGlyphInfo(lastR, chr).IsNormal()) {
rfnt = lastR;
return true;
}
Font f = fnt;
for(int i = 0; i < rface.GetCount(); i++) {
f.Face(rface[i]);
DDUMP(f.GetFaceName());
if(GetGlyphInfo(f, chr).IsNormal()) {
LOG("-----------");
last = fnt;
rfnt = lastR = f;
return true;
}
}
return false;
}
#include "FontInfo.h"
static const char *sFontReplacements[] = {
"sans-serif",
"Arial",
"Arial Unicode MS",
"Symbol",
"文泉驛等寬正黑",
"文泉驛正黑",
"MS UI Gothic",
"MS Mincho",
"Arial",
"AlArabiya"
"FreeSerif",
"Kochi Mincho",
"Kochi Gothic",
"Sazanami Mincho",
"Sazanami Gothic",
"Gulim",
"SimSun",
"PMingLiU",
};
bool Replace(Font fnt, int chr, Font& rfnt)
{
// ASSERT(chr >= 0 && chr <
LOG("Replace: " << fnt << ": " << chr);
static Vector<int> rface;
ONCELOCK {
for(int i = 0; i < __countof(sFontReplacements) && rface.GetCount() < 20; i++) {
int q = Font::FindFaceNameIndex(sFontReplacements[i]);
if(q > 0)
rface.Add(q);
}
}
Font f = fnt;
for(int i = 0; i < rface.GetCount(); i++)
if(IsNormal(f.Face(rface[i]), chr)) {
rfnt = f;
LOG("Replacement: " << rfnt);
return true;
}
LOG("No replace");
return false;
}

View file

@ -1,188 +1,222 @@
#include "FontInfo.h"
#define LTIMING(x)
#ifdef PLATFORM_X11
static FT_Library sFTlib;
bool sInitFt(void)
{
if(sFTlib)
return true;
return FT_Init_FreeType(&sFTlib) == 0;
}
FcPattern *CreateFcPattern(Font font, int angle)
{
LTIMING("CreateXftFont");
double sina, cosa;
int hg = abs(font.GetHeight());
if(hg == 0) hg = 10;
String n = font.GetFaceName();
const char *face = n;
FcPattern *p = FcPatternCreate();
FcPatternAddString(p, FC_FAMILY, (FcChar8*)face);
FcPatternAddInteger(p, FC_SLANT, font.IsItalic() ? 110 : 0);
FcPatternAddInteger(p, FC_PIXEL_SIZE, hg);
FcPatternAddInteger(p, FC_WEIGHT, font.IsBold() ? 200 : 100);
FcPatternAddBool(p, FC_MINSPACE, 1);
if(angle) {
FcMatrix mx;
Draw::SinCos(angle, sina, cosa);
mx.xx = cosa;
mx.xy = -sina;
mx.yx = sina;
mx.yy = cosa;
FcPatternAddMatrix(p, FC_MATRIX, &mx);
}
FcResult result;
FcPattern *m = XftFontMatch(Xdisplay, Xscreenno, p, &result);
FcPatternDestroy(p);
return m;
}
FT_Face CreateFTFace(const FcPattern *pattern, String *rpath) {
FT_Face face = NULL;
int id;
double dsize;
double aspect;
FcChar8 *filename;
if(!sInitFt())
return NULL;
if(FcPatternGetString(pattern, FC_FILE, 0, &filename) != FcResultMatch)
return NULL;
if(rpath)
*rpath = (const char *)filename;
if(FcPatternGetDouble(pattern, FC_PIXEL_SIZE, 0, &dsize) != FcResultMatch)
dsize = 16;
if (FcPatternGetDouble(pattern, FC_ASPECT, 0, &aspect) != FcResultMatch)
aspect = 1.0;
FT_F26Dot6 ysize = (FT_F26Dot6) (dsize * 64.0);
FT_F26Dot6 xsize = (FT_F26Dot6) (dsize * aspect * 64.0);
if(FT_New_Face (sFTlib, (const char *)filename, 0, &face))
return NULL;
FT_Set_Char_Size(face, xsize, ysize, 0, 0);
return face;
}
FT_Face FTFace(Font fnt, String *rpath = NULL)
{
static FT_Face cached_face;
static Font cached_font;
if(cached_font != fnt) {
cached_font = fnt;
if(cached_face)
FT_Done_Face(cached_face);
FcPattern *p = CreateFcPattern(cached_font, 0);
cached_face = CreateFTFace(p, rpath);
FcPatternDestroy(p);
}
return cached_face;
}
CommonFontInfo GetFontInfoSys(Font font)
{
CommonFontInfo fi;
FT_Face face = FTFace(font, &fi.path);
if(face) {
fi.ascent = face->size->metrics.ascender >> 6;
fi.descent = -(face->size->metrics.descender >> 6);
fi.height = fi.ascent + fi.descent;
fi.lineheight = face->size->metrics.height >> 6;
fi.external = 0;
fi.internal = 0;
fi.overhang = 0;
fi.maxwidth = face->size->metrics.max_advance >> 6;
fi.avewidth = fi.maxwidth;
fi.default_char = '?';
fi.fixedpitch = font.GetFaceInfo() & Font::FIXEDPITCH;
int hg = abs(font.GetHeight());
if(hg == 0) hg = 10;
fi.underline_thickness = max(hg / 20, 1);
fi.underline_position = max(hg / 15, int(fi.descent > 0));
}
return fi;
}
#define FLOOR(x) ((x) & -64)
#define CEIL(x) (((x)+63) & -64)
#define TRUNC(x) ((x) >> 6)
#define ROUND(x) (((x)+32) & -64)
GlyphInfo GetGlyphInfoSys(Font font, int chr)
{
GlyphInfo gi;
FT_Face face = FTFace(font, NULL);
gi.lspc = gi.rspc = 0;
gi.width = 0x8000;
if(face) {
int glyph_index = FT_Get_Char_Index(face, chr);
if(glyph_index &&
(FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT|FT_LOAD_NO_BITMAP) == 0 ||
FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT) == 0)) {
FT_Glyph_Metrics& m = face->glyph->metrics;
int left = FLOOR(m.horiBearingX);
int width = TRUNC(CEIL(m.horiBearingX + m.width) - left);
gi.width = TRUNC(ROUND(face->glyph->advance.x));
gi.lspc = TRUNC(left);
gi.rspc = gi.width - width - gi.lspc;
}
}
return gi;
}
Vector<FaceInfo> GetAllFacesSys()
{
static const char *basic_fonts[] = {
"sans-serif",
"serif",
"sans-serif",
"monospace",
"serif",
"sans-serif",
"monospace",
};
Vector<FaceInfo> list;
for(int i = 0; i < __countof(basic_fonts); i++) {
FaceInfo& fi = list.Add();
f.name = basic_fonts[i];
f.scaleable = true;
f.fixed = i == 3 || i == 6;
f.compose = sCheckComposed(basic_fonts[i]);
}
FcPattern *p = FcPatternCreate();
FcObjectSet *os = FcObjectSetBuild(XFT_FAMILY, XFT_SPACING, XFT_SCALABLE, (void *)0);
FcFontSet *fs = FcFontList(NULL, p, os);
FcPatternDestroy(p);
FcObjectSetDestroy(os);
for(int i = 0; i < fs->nfont; i++) {
FcChar8 *family = NULL;
if(FcPatternGetString(fs->fonts[i], FC_FAMILY, 0, &family) == 0 && family) {
FaceInfo& fi = list.Add();
fi.name = (const char *)family;
fi.type = 0;
int iv;
if(FcPatternGetInteger(fs->fonts[i], FC_SPACING, 0, &iv) == 0 && iv == FC_MONO)
fi.type |= Font::FIXEDPITCH;
FcBool bv;
if(FcPatternGetBool(fs->fonts[i], FC_SCALABLE, 0, &bv) == 0 && bv)
fi.type |= Font::SCALEABLE;
}
}
FcFontSetDestroy(fs);
return list;
}
#endif
#include "FontInfo.h"
#define LTIMING(x)
#ifdef PLATFORM_X11
static FT_Library sFTlib;
bool sInitFt(void)
{
if(sFTlib)
return true;
return FT_Init_FreeType(&sFTlib) == 0;
}
FcPattern *CreateFcPattern(Font font, int angle)
{
LTIMING("CreateXftFont");
double sina, cosa;
int hg = abs(font.GetHeight());
if(hg == 0) hg = 10;
String n = font.GetFaceName();
const char *face = n;
FcPattern *p = FcPatternCreate();
FcPatternAddString(p, FC_FAMILY, (FcChar8*)face);
FcPatternAddInteger(p, FC_SLANT, font.IsItalic() ? 110 : 0);
FcPatternAddInteger(p, FC_PIXEL_SIZE, hg);
FcPatternAddInteger(p, FC_WEIGHT, font.IsBold() ? 200 : 100);
FcPatternAddBool(p, FC_MINSPACE, 1);
if(angle) {
FcMatrix mx;
Draw::SinCos(angle, sina, cosa);
mx.xx = cosa;
mx.xy = -sina;
mx.yx = sina;
mx.yy = cosa;
FcPatternAddMatrix(p, FC_MATRIX, &mx);
}
FcResult result;
FcPattern *m = XftFontMatch(Xdisplay, Xscreenno, p, &result);
FcPatternDestroy(p);
return m;
}
FT_Face CreateFTFace(const FcPattern *pattern, String *rpath) {
FT_Face face = NULL;
int id;
double dsize;
double aspect;
FcChar8 *filename;
if(!sInitFt())
return NULL;
if(FcPatternGetString(pattern, FC_FILE, 0, &filename) != FcResultMatch)
return NULL;
if(rpath)
*rpath = (const char *)filename;
if(FcPatternGetDouble(pattern, FC_PIXEL_SIZE, 0, &dsize) != FcResultMatch)
dsize = 16;
if (FcPatternGetDouble(pattern, FC_ASPECT, 0, &aspect) != FcResultMatch)
aspect = 1.0;
FT_F26Dot6 ysize = (FT_F26Dot6) (dsize * 64.0);
FT_F26Dot6 xsize = (FT_F26Dot6) (dsize * aspect * 64.0);
if(FT_New_Face (sFTlib, (const char *)filename, 0, &face))
return NULL;
FT_Set_Char_Size(face, xsize, ysize, 0, 0);
return face;
}
#define FONTCACHE 37
struct FtFaceEntry {
Font font;
FT_Face face;
String path;
};
FT_Face FTFace(Font fnt, String *rpath = NULL)
{
RTIMING("FTFace");
DDUMP(fnt);
static FtFaceEntry cache[FONTCACHE];
ONCELOCK {
for(int i = 0; i < FONTCACHE; i++)
cache[i].font.Height(-30000);
}
FtFaceEntry be;
be = cache[0];
for(int i = 0; i < FONTCACHE; i++) {
FtFaceEntry e = cache[i];
if(i)
cache[i] = be;
if(e.font == fnt) {
if(rpath)
*rpath = e.path;
if(i)
cache[0] = e;
return e.face;
}
be = e;
}
RTIMING("FTFace2");
if(be.face) {
LOG("Removing " << be.font << " - " << (void *)be.face);
FT_Done_Face(be.face);
}
be.font = fnt;
FcPattern *p = CreateFcPattern(fnt, 0);
be.face = CreateFTFace(p, &be.path);
FcPatternDestroy(p);
cache[0] = be;
if(rpath)
*rpath = be.path;
for(int i = 0; i < FONTCACHE; i++)
LOG(i << ": " << cache[i].font);
return be.face;
}
CommonFontInfo GetFontInfoSys(Font font)
{
CommonFontInfo fi;
FT_Face face = FTFace(font, &fi.path);
if(face) {
fi.ascent = face->size->metrics.ascender >> 6;
fi.descent = -(face->size->metrics.descender >> 6);
fi.height = fi.ascent + fi.descent;
fi.lineheight = face->size->metrics.height >> 6;
fi.external = 0;
fi.internal = 0;
fi.overhang = 0;
fi.maxwidth = face->size->metrics.max_advance >> 6;
fi.avewidth = fi.maxwidth;
fi.default_char = '?';
fi.fixedpitch = font.GetFaceInfo() & Font::FIXEDPITCH;
int hg = abs(font.GetHeight());
if(hg == 0) hg = 10;
fi.underline_thickness = max(hg / 20, 1);
fi.underline_position = max(hg / 15, int(fi.descent > 0));
}
return fi;
}
#define FLOOR(x) ((x) & -64)
#define CEIL(x) (((x)+63) & -64)
#define TRUNC(x) ((x) >> 6)
#define ROUND(x) (((x)+32) & -64)
GlyphInfo GetGlyphInfoSys(Font font, int chr)
{
RTIMING("GetGlyphInfoSys");
GlyphInfo gi;
FT_Face face = FTFace(font, NULL);
gi.lspc = gi.rspc = 0;
gi.width = 0x8000;
if(face) {
RTIMING("GetGlyphInfoSys 2");
int glyph_index = FT_Get_Char_Index(face, chr);
if(glyph_index &&
(FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT|FT_LOAD_NO_BITMAP) == 0 ||
FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT) == 0)) {
FT_Glyph_Metrics& m = face->glyph->metrics;
int left = FLOOR(m.horiBearingX);
int width = TRUNC(CEIL(m.horiBearingX + m.width) - left);
gi.width = TRUNC(ROUND(face->glyph->advance.x));
gi.lspc = TRUNC(left);
gi.rspc = gi.width - width - gi.lspc;
}
}
return gi;
}
Vector<FaceInfo> GetAllFacesSys()
{
static const char *basic_fonts[] = {
"sans-serif",
"serif",
"sans-serif",
"monospace",
"serif",
"sans-serif",
"monospace",
};
Vector<FaceInfo> list;
for(int i = 0; i < __countof(basic_fonts); i++) {
FaceInfo& fi = list.Add();
fi.name = basic_fonts[i];
fi.info = (i == 3 || i == 6) ? Font::SCALEABLE|Font::FIXEDPITCH : Font::SCALEABLE;
}
FcPattern *p = FcPatternCreate();
FcObjectSet *os = FcObjectSetBuild(XFT_FAMILY, XFT_SPACING, XFT_SCALABLE, (void *)0);
FcFontSet *fs = FcFontList(NULL, p, os);
FcPatternDestroy(p);
FcObjectSetDestroy(os);
for(int i = 0; i < fs->nfont; i++) {
FcChar8 *family = NULL;
if(FcPatternGetString(fs->fonts[i], FC_FAMILY, 0, &family) == 0 && family) {
FaceInfo& fi = list.Add();
fi.name = (const char *)family;
fi.info = 0;
int iv;
if(FcPatternGetInteger(fs->fonts[i], FC_SPACING, 0, &iv) == 0 && iv == FC_MONO)
fi.info |= Font::FIXEDPITCH;
FcBool bv;
if(FcPatternGetBool(fs->fonts[i], FC_SCALABLE, 0, &bv) == 0 && bv)
fi.info |= Font::SCALEABLE;
}
}
FcFontSetDestroy(fs);
return list;
}
#endif

View file

@ -1,150 +1,179 @@
#include "FontInfo.h"
using namespace Upp;
struct CharDisplay : public Display {
virtual void Paint(Draw& w, const Rect& r, const Value& q, Color ink, Color paper, dword style) const
{
w.DrawRect(r, paper);
w.DrawText(r.left, r.top, AsString(q), StdFont(), ink);
Font fnt;
fnt.FaceName("Bitstream Vera Sans Mono");
fnt.Height(13);
WString txt((int)q, 1);
w.DrawText(r.left + 40, r.top, txt, Courier(20), ink);
GlyphInfo gi = GetGlyphMetrics(fnt, q);
w.DrawRect(r.left + 60, r.top, gi.width, r.GetHeight(), LtCyan());
gi = GetGlyphInfo(fnt, q);
if(!gi.IsMissing()) {
if(gi.IsNormal())
w.DrawText(r.left + 60, r.top, txt, fnt);
else
if(gi.IsReplaced())
w.DrawText(r.left + 60, r.top, txt, fnt().Face(gi.lspc), Magenta());
else
if(gi.IsComposed()) {
ComposedGlyph g;
if(Compose(fnt, q, g)) {
w.DrawText(r.left + 60, r.top, WString(g.basic_char, 1), fnt, LtBlue());
w.DrawText(r.left + 60 + g.mark_pos.x, r.top + g.mark_pos.y,
WString(g.mark_char, 1), g.mark_font, LtRed());
}
}
}
else
w.DrawText(r.left + 60, r.top, txt, fnt, ink);
w.DrawRect(r.left, r.bottom - 1, r.Width(), 1, Black());
}
};
Size GetTextSizeNew(const wchar *text, Font font, int n)
{
int cx = 0;
const wchar *wtext = (const wchar *)text;
while(n > 0) {
cx += GetGlyphInfo(font, *wtext++).width;
n--;
}
return Size(cx, GetFontInfo(font).height);
}
struct App : TopWindow {
virtual void Paint(Draw& w) {
WString h = "Hello ěščřžýáíéúů ";
for(int i = 0; i < 10; i++)
h.Cat(20000 + i);
w.DrawRect(GetSize(), White());
Font fnt;
fnt.FaceName("Bitstream Vera Sans Mono");
fnt.Height(13);
DoDrawText(w, 100, 100, 0, h, fnt, Black(), h.GetCount(), NULL);
}
};
GUI_APP_MAIN
{
SetDefaultCharset(CHARSET_UTF8);
App app;
app.Open();
GetGlyphInfo(Font().FaceName("Bitstream Vera Sans Mono").Height(20), 461);
ColumnList list;
list.SetDisplay(Single<CharDisplay>());
list.ItemHeight(25).Columns(12);
for(int i = 32; i < 65536; i++)
list.Add(i);
TopWindow win;
// list.SetCursor(461);
win.Add(list.SizePos());
win.Run();
return;
FontInfo fi = Arial(100).Bold().Info();
DUMP(fi.GetAscent());
DUMP(fi.GetDescent());
DUMP(fi.GetMaxWidth());
{
int from = 0;
for(int i = 0; i < 65536; i++) {
GlyphInfo g = GetGlyphInfoSys(Arial(100).Bold(), i);
if(g.IsMissing()) {
// if(!fi.HasChar(i)) {
if(from < i)
LOG(from << " - " << i - 1);
from = i + 1;
}
}
if(from < 65535)
LOG(from << " - " << 65535);
}
Vector<FaceInfo> fa = GetAllFacesSys();
for(int i = 0; i < fa.GetCount(); i++)
LOG(fa[i].name << ": scalable: " << (fa[i].info & Font::SCALEABLE) << ", fixed: " << (fa[i].info & Font::FIXEDPITCH));
CommonFontInfo f = GetFontInfoSys(Arial(100).Bold());
DUMP(f.ascent);
DUMP(f.descent);
DUMP(f.lineheight);
DUMP(f.maxwidth);
// DUMP(f.path);
for(int c = '0'; c < 'z'; c++) {
LOG("-------------------------");
DUMP((char)c);
GlyphInfo g = GetGlyphInfoSys(Arial(100).Bold(), c);
DUMP(g.width);
DUMP(fi[c]);
DUMP(g.lspc);
DUMP(fi.GetLeftSpace(c));
DUMP(g.rspc);
DUMP(fi.GetRightSpace(c));
}
#if 0
WString x = "Just a little test!";
RDUMP(GetTextSize(x, Arial(20), x.GetCount()));
RDUMP(GetTextSizeNew(x, Arial(20), x.GetCount()));
RDUMP(sizeof(GlyphInfo));
RDUMP(sizeof(CharEntry));
RDUMP(sizeof(CommonFontInfo));
#ifndef _DEBUG
int cx = 0;
{
RTIMING("Old");
for(int i = 0; i < 1000000; i++) {
cx += GetTextSize(x, Arial(20), x.GetCount()).cx;
}
}
{
RTIMING("New");
for(int i = 0; i < 1000000; i++) {
cx += GetTextSizeNew(x, Arial(20), x.GetCount()).cx;
}
}
#endif
#endif
}
#include "FontInfo.h"
using namespace Upp;
struct CharDisplay : public Display {
virtual void Paint(Draw& w, const Rect& r, const Value& q, Color ink, Color paper, dword style) const
{
w.DrawRect(r, paper);
w.DrawText(r.left, r.top, AsString(q), StdFont(), ink);
Font fnt;
fnt.FaceName("Bitstream Vera Sans Mono");
// fnt.Face(Font::ARIAL);
fnt.Height(20);
WString txt((int)q, 1);
w.DrawText(r.left + 40, r.top, txt, Courier(20), ink);
GlyphInfo gi = GetGlyphMetrics(fnt, q);
w.DrawRect(r.left + 60, r.top, gi.width, r.GetHeight(), LtCyan());
gi = GetGlyphInfo(fnt, q);
if(!gi.IsMissing()) {
if(gi.IsNormal())
w.DrawText(r.left + 60, r.top, txt, fnt);
else
if(gi.IsReplaced())
w.DrawText(r.left + 60, r.top, txt, fnt().Face(gi.lspc), Magenta());
else
if(gi.IsComposed()) {
ComposedGlyph g;
if(Compose(fnt, q, g)) {
w.DrawText(r.left + 60, r.top, WString(g.basic_char, 1), fnt, LtBlue());
w.DrawText(r.left + 60 + g.mark_pos.x, r.top + g.mark_pos.y,
WString(g.mark_char, 1), g.mark_font, LtRed());
}
}
}
else
w.DrawText(r.left + 60, r.top, txt, fnt, ink);
w.DrawRect(r.left, r.bottom - 1, r.Width(), 1, Black());
}
};
Size GetTextSizeNew(const wchar *text, Font font, int n)
{
int cx = 0;
const wchar *wtext = (const wchar *)text;
while(n > 0) {
cx += GetGlyphInfo(font, *wtext++).width;
n--;
}
return Size(cx, GetFontInfo(font).height);
}
struct App : TopWindow {
virtual void Paint(Draw& w) {
WString h = "Hello ěščřžýáíéúů ";
for(int i = 0; i < 10; i++)
h.Cat(20000 + i);
w.DrawRect(GetSize(), White());
Font fnt;
fnt.FaceName("Bitstream Vera Sans Mono");
fnt.Height(13);
DoDrawText(w, 100, 100, 0, h, fnt, Black(), h.GetCount(), NULL);
DoDrawText(w, 100, 300, 300, h, fnt, Black(), h.GetCount(), NULL);
}
};
GUI_APP_MAIN
{
SetDefaultCharset(CHARSET_UTF8);
#if 0
static dword t[] = { 0x9d15, 0x3092,0x4e26,0xe20,0xf780,0x994,0x636,0xfeb2,0xfebe,0x2126 };
VectorMap<String, int> stat;
for(int i = 0; i < __countof(t); i++) {
int chr = t[i];
LOG("-------- " << chr);
for(int i = 0; i < Font::GetFaceCount(); i++) {
Font fnt;
fnt.Height(20);
fnt.Face(i);
if(GetGlyphInfoSys(fnt, chr).IsNormal()) {
LOG(fnt.GetFaceName());
stat.GetAdd(fnt.GetFaceName(), 0)++;
}
}
}
LOG("--------------------------");
Vector<int> o = GetSortOrder(stat.GetValues());
for(int i = 0; i < o.GetCount(); i++)
LOG(stat.GetKey(o[i]) << ": " << stat[o[i]]);
LOG("--------------------------");
#endif
App app;
app.Open();
GetGlyphInfo(Font().FaceName("Bitstream Vera Sans Mono").Height(20), 461);
ColumnList list;
list.SetDisplay(Single<CharDisplay>());
list.ItemHeight(25).Columns(12);
for(int i = 32; i < 65536; i++)
list.Add(i);
TopWindow win;
// list.SetCursor(461);
win.Add(list.SizePos());
win.Run();
return;
FontInfo fi = Arial(100).Bold().Info();
DUMP(fi.GetAscent());
DUMP(fi.GetDescent());
DUMP(fi.GetMaxWidth());
{
int from = 0;
for(int i = 0; i < 65536; i++) {
GlyphInfo g = GetGlyphInfoSys(Arial(100).Bold(), i);
if(g.IsMissing()) {
// if(!fi.HasChar(i)) {
if(from < i)
LOG(from << " - " << i - 1);
from = i + 1;
}
}
if(from < 65535)
LOG(from << " - " << 65535);
}
Vector<FaceInfo> fa = GetAllFacesSys();
for(int i = 0; i < fa.GetCount(); i++)
LOG(fa[i].name << ": scalable: " << (fa[i].info & Font::SCALEABLE) << ", fixed: " << (fa[i].info & Font::FIXEDPITCH));
CommonFontInfo f = GetFontInfoSys(Arial(100).Bold());
DUMP(f.ascent);
DUMP(f.descent);
DUMP(f.lineheight);
DUMP(f.maxwidth);
// DUMP(f.path);
for(int c = '0'; c < 'z'; c++) {
LOG("-------------------------");
DUMP((char)c);
GlyphInfo g = GetGlyphInfoSys(Arial(100).Bold(), c);
DUMP(g.width);
DUMP(fi[c]);
DUMP(g.lspc);
DUMP(fi.GetLeftSpace(c));
DUMP(g.rspc);
DUMP(fi.GetRightSpace(c));
}
#if 0
WString x = "Just a little test!";
RDUMP(GetTextSize(x, Arial(20), x.GetCount()));
RDUMP(GetTextSizeNew(x, Arial(20), x.GetCount()));
RDUMP(sizeof(GlyphInfo));
RDUMP(sizeof(CharEntry));
RDUMP(sizeof(CommonFontInfo));
#ifndef _DEBUG
int cx = 0;
{
RTIMING("Old");
for(int i = 0; i < 1000000; i++) {
cx += GetTextSize(x, Arial(20), x.GetCount()).cx;
}
}
{
RTIMING("New");
for(int i = 0; i < 1000000; i++) {
cx += GetTextSizeNew(x, Arial(20), x.GetCount()).cx;
}
}
#endif
#endif
}