http://www.mzwu.com/article.asp?id=2976
根据所选择的 TrueType 字体生成点阵数据
2006-07-19 21:18:57 发表评论
TrueType字体在Windows平台下的应用很多,但是涉及到具体的操作层面上中文资料还是很少,遇到了不少问题苦恼了一阵子。
1、通过 CFontDialog 进行字体选择,但是正常情况下得到的字体列表示当前系统中所有支持的字体,当然也包括其它一些非 TrueType 字体,要在 CFontDialog 的列表中剔出非 TrueType 的字体很简单,只需在配置 CFontDialog 时如下设置:
1
2
|
CFontDialog dlg;
dlg.m_cf.Flags |= CF_TTONLY; //only enum TrueType
|
2、要遍历所选择的字体中所包含的所有字符,这个我们不禁想到可以通过 .ttf文件的解析来完成,但是如何得到选择的字体所对应的 .ttf文件哪?同实际的操作,得知在CfontDialog中显示的字体的名称和所对应的.ttf文件不是一一对应的关系,例如:选择的字体为 Arial 那么在 System/Fonts/下却没有 Arial.ttf 的文件,在网上找了很多例子都不能百分百无误的实现,在 codeproject 中有一个例子 Sample 通过注册表的方法来查找,但是同样存在上面的问题。而且即使能够找到相对应的.ttf文件,要解析这个文件也很有困难,因为存在 ttc的问题(即:一个是一个ttf的集合,一个文件里面有可能定义几种 TrueType 字体),这样还要根据选择的字体然后在读取ttf文件的过程中进行比较,找到描述这种这种字体的部分,后来发现选择的字体有可能和文档中定义的TrueType 字体名称不一致,因为有中文字体的存在,这一部分我并没有进行测试。因为在之后的解决过程找到了另外一种解决的方法。
1
2
3
4
5
6
7
|
DWORD CDC::GetFontData( DWORD dwTable, DWORD dwOffset, LPVOID lpData, DWORD cbData ) const;
Remarks
Retrieves font-metric information from a scalable font file. The information to retrieve is identified
by specifying an offset into the font file and the length of the information to return. An application
can sometimes use the GetFontData member function to save a TrueType font with a document. To do this,
the application determines whether the font can be embedded and then retrieves the entire font file,
specifying 0 for the dwTable, dwOffset, and cbData parameters.
|
通过上面的函数可以轻易的得到选入DC中的TrueType字体的 ttf文件中的数据,但是如果想得到整个的字体文件还是会存在ttc的问题,因为这个函数得到的数据是其中选择的那种字体的数据,但是对于各个数据段offset的定义却是针对于整个文档的,如果直接引用就会有问题,也没有找到其他更好的解决方法,但是却可以准确地得到关于其中任意一个Table的数据,刚刚好对于字符集中所包含的字符的定于存在于"cmap" Table中,问题解决了,如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
// Macro to pack a TrueType table name into a DWORD.
#define MAKETABLENAME(ch1, ch2, ch3, ch4) (
(((DWORD)(ch4)) <<
24
) |
(((DWORD)(ch3)) << 16) |
(((DWORD)(ch2)) << 8) |
((DWORD)(ch1))
)
DWORD
tag
=
MAKETABLENAME
( ''c'',''m'',''a'',''p'' );
DWORD
size
=
m_pFontDC
->GetFontData(tag,0, NULL, 0);
unsigned char *pBuffer = new unsigned char[size];
m_pFontDC->GetFontData(tag,0,pBuffer,size);
//do something with pBuffer
delete[] pBuffer;
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
|
/*
read a short(2 bytes) from a stream
**/
static int Getushort(unsigned char **ttf) {
unsigned char ch1 = **(ttf);
(*ttf)++;
unsigned char ch2 = **(ttf);
(*ttf)++;
return( (ch1<<8)|ch2 );
}
/*
read a long(4 bytes) from a stream
**/
static int Getlong(unsigned char **ttf) {
unsigned char ch1 = **(ttf);
(*ttf)++;
unsigned char ch2 = **(ttf);
(*ttf)++;
unsigned char ch3 = **(ttf);
(*ttf)++;
unsigned char ch4 = **(ttf);
(*ttf)++;
return( (ch1<<24)|(ch2<<16)|(ch3<<8)|ch4 );
}
/*
Parse the table of "cmap"
**/
BOOL ReadttfEncodings(unsigned char *start_addr)
{
BOOL bResult = TRUE;
unsigned char *ttf = start_addr;
//local variable
int i;
int nencs, version;
int enc = 0;
int platform, specific;
int offset, encoff;
int format, len;
int segCount;
unsigned short *endchars,*startchars;
version = Getushort(&ttf);
nencs = Getushort(&ttf);
if ( version!=0 && nencs==0 )
nencs = version;/* Sometimes they are backwards */
for ( i=0; i < nencs; ++i )
{
platform = Getushort(&ttf);
specific = Getushort(&ttf);
offset = Getlong(&ttf);
if ( platform==3 /*&& (specific==1 || specific==5)*/)
{
enc = 1;
encoff = offset;
} else if ( platform==1 && specific==0 && enc!=1 )
{
enc = 2;
encoff = offset;
} else if ( platform==1 && specific==1 )
{
enc = 1;
encoff = offset;
} else if ( platform==0 ) {
enc = 1;
encoff = offset;
}
if ( platform==3 )
{
//MS Symbol
}
else if ( platform==1 )
{
//Mac Roman;
}
else if ( platform==0 )
{
//Unicode Default
}
else{}
}
if ( enc!=0 )
{
//reset pointer address
ttf = start_addr + encoff;
format = Getushort(&ttf);
if ( format!=12 && format!=10 && format!=8 )
{
len = Getushort(&ttf);
/*Language*/ Getushort(&ttf);
}
else
{
/* padding */ Getushort(&ttf);
len = Getlong(&ttf);
/*Format*/ Getlong(&ttf);
}
if ( format==0 )
{
//can''t be supported
bResult = FALSE;
}
else if ( format==4 )
{
//Format 4 (Windows unicode),only supported Format 4
segCount = Getushort(&ttf)/2;
/* searchRange = */ Getushort(&ttf);
/* entrySelector = */ Getushort(&ttf);
/* rangeShift = */ Getushort(&ttf);
endchars = new unsigned short[segCount];
for ( i=0; i < segCount; ++i )
endchars[i] = Getushort(&ttf);
if ( Getushort(&ttf)!=0 )
{
//Expected 0 in true type font;
}
startchars = new unsigned short[segCount];
for ( i=0; i < segCount; ++i )
startchars[i] = Getushort(&ttf);
//do something with endchars & startchars
delete[] startchars;
delete[] endchars;
}
else if ( format==6 )
{
/* Apple''s unicode format */
/* Well, the docs say it''s for 2byte encodings, but Apple actually*/
/* uses it for 1 byte encodings which don''t fit into the require-*/
/* ments for a format 0 sub-table. See Zapfino.dfont */
//can''t be supported
bResult = FALSE;
}
else if ( format==2 )
{
//can''t be supported
bResult = FALSE;
}
else if ( format==12 )
{
//can''t be supported
bResult = FALSE;
}
else if ( format==8 )
{
// fprintf(stderr,"I don''t support mixed 16/32 bit
// characters (no unicode surogates), format=%d", format);
// can''t be supported
bResult = FALSE;
}
else if ( format==10 )
{
//fprintf(stderr,"I don''t support 32 bit characters format=%d", format);
//can''t be supported
bResult = FALSE;
}
else
{
//fprintf(stderr,"Eh? Unknown encoding format=%d", format);
//can''t be supported
bResult = FALSE;
}
}
return bResult;
}
|
1
2
3
4
5
6
7
8
|
DWORD GetGlyphOutline( UINT nChar,
UINT nFormat,
LPGLYPHMETRICS lpgm,
DWORD cbBuffer,
LPVOID lpBuffer,
const MAT2 FAR* lpmat2 ) const;
Remarks
Retrieves the outline curve or bitmap for an outline character in the current font.
|
具体请参考 msdn 可以得到 anti-alias 等多种格式的数据。但是这个有一个问题就是如果对应一个字体的size的各个参数,这里面的麻烦很多,后来总结如下如下:
还有补充的就是 GetGlyphOutline 数据是有一定的对齐方式的,要进行一些处理,代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
|
BOOL CreateFontMatrix(int iAA,
UINT nChar,
unsigned char **pOutPut,
int *iBytesPreLine,
int *iLine,
int *iBaseLine,
int *iBox_x,
int *iBox_y)
{
unsigned char *pBuf;
TEXTMETRIC tm;
GLYPHMETRICS glyph;
int width,height,box_x,box_y,ori_x,ori_y,size,iAdjust;
BOOL bRel = TRUE;
MAT2 mat2 =
{
{ 0, 1, },
{ 0, 0, },
{ 0, 0, },
{ 0, 1, }
};
//Get glyph outline
memset(&glyph,0,sizeof(GLYPHMETRICS));
size = m_pFontDC->GetGlyphOutline(nChar,m_nFormat,&glyph,0,NULL,&mat2);
if (size >= 0) // if char is space, the size may be zero
{
int count = 0;
int data = 0;
pBuf = new unsigned char[size];
m_pFontDC->GetTextMetrics(&tm);
m_pFontDC->GetGlyphOutline(nChar,m_nFormat,&glyph,size,pBuf,&mat2);
ori_x = glyph.gmptGlyphOrigin.x;
//if ori_x is negative,set ori_x zero
ori_x = (ori_x <
0
) ? 0 : ori_x;
ori_y
=
tm
.tmAscent - glyph.gmptGlyphOrigin.y;
box_x
=
glyph
.gmBlackBoxX;
box_y
=
glyph
.gmBlackBoxY;
width
=
glyph
.gmCellIncX;
iAdjust = (box_x+3)&0xfffc; //DWORD align
if((box_x + ori_x) > width)
box_x = width - ori_x;
height= m_pLf->lfHeight;
//convert
int index = 0;
if (iAA == AA_2)
{
width = (width%4 == 0)?width/4:(width/4+1); //here,to 2bits/pix
*pOutPut = new unsigned char[width*height + 1];
memset(*pOutPut,0,width*height + 1);
//if size == 0 all data is 0
if(size > 0)
{
for (int i = 0; i <
box_y
; i++)
{
for (int
j
=
0
; j < box_x; j++)
{
//int
k
=
pBuf
[i*iAdjust + j];
data
=
AA2_GRAG_MATRIX
[pBuf[i*iAdjust + j]];
index = (i + ori_y)*width + (j + ori_x)/4;
switch((j + ori_x)%4)
{
case 0:
(*pOutPut)[index] |= (data<<6)&0xC0;
break;
case 1:
(*pOutPut)[index] |= (data<<4)&0x30;
break;
case 2:
(*pOutPut)[index] |= (data<<2)&0x0C;
break;
case 3:
(*pOutPut)[index] |= data&0x03;
break;
default:
{}
}
}//end j
}//end i
}
}//end AA 2*2
else if (iAA == AA_4)
{
width = (width%2 == 0)?width/2:(width/2+1); //here,to 4bits/pix
*
pOutPut
=
new
unsigned char[width*height + 1];
memset(*pOutPut,0,width*height + 1);
//if size == 0 all data is 0
if(size > 0)
{
for (int i = 0; i <
box_y
; i++)
{
for (int
j
=
0
; j < box_x; j++)
{
ASSERT(pBuf[i*iAdjust + j] <= 17);
data
=
AA4_GRAG_MATRIX
[pBuf[i*iAdjust + j]];
index = (i + ori_y)*width + (j + ori_x)/2;
switch((j + ori_x)%2)
{
case 0:
(*pOutPut)[index] |= (data<<4)&0xF0;
break;
case 1:
(*pOutPut)[index] |= data&0x0F;
break;
default:
{}
}
}//end j
}//end i
}
}//end AA 4*4
else //start Normal
{
//Note: monochrome bitmap,the first data in pBuff is on the Left-bottom
// one bit per pix
width = (width%8 == 0)?width/8:(width/8+1); //here,to 4bits/pix
if (width == 0)
width
=
1
;
*
pOutPut
=
new
unsigned char[width*height + 1];
memset(*pOutPut,0,width*height + 1);
//if size == 0 all data is 0
if(size > 0)
{
for (int i = 0; i <
box_y
; i++)
{
for (int
j
=
0
; j < width; j++)
{
(*pOutPut)[(i + ori_y)*width + j] |= data<<(8-ori_x);
data
=
pBuf
[i*(size/box_y) + j];
(*pOutPut)[(i + ori_y)*width + j] |= data>>ori_x;
}//end j
}//end i
}
}//end else(normal bitmap)
if(pBuf != NULL)
delete[] pBuf;
//set return result
*iBytesPreLine = width;
*iLine = height;
*iBaseLine = tm.tmAscent;
*iBox_x = glyph.gmCellIncX;//box_x;
*iBox_y = box_y;
bRel = TRUE;
}
else//if size
bRel = FALSE;
return bRel;
}
|
得于8小时之内,写于8小时之外。