OSDN Git Service

9277180d62bb0c27ee88d975e4437669e2417dff
[kimikage-nscr/kimikage-nscr.git] / nsdfont / src / CharacterMap.cpp
1 /*\r
2  *      Kimikage NScripter Plugins Project\r
3  *\r
4  *      This software is distributed under a BSD-style license.\r
5  *      See license.txt for more information.\r
6  */\r
7 \r
8 #include "CharacterMap.h"\r
9 \r
10 // This code is based on a sample code of\r
11 // KB\81F241020 (http://support.microsoft.com/kb/241020/)\r
12 \r
13 namespace nsdfont\r
14 {\r
15         CharacterMap::CharacterMap( void )\r
16         {\r
17                 hdc = CreateCompatibleDC( NULL );\r
18                 hFont = NULL;\r
19                 cmapTable = NULL;\r
20         }\r
21 \r
22         CharacterMap::~CharacterMap( void )\r
23         {\r
24                 if ( NULL != hdc )\r
25                 {\r
26                         DeleteDC( hdc );\r
27                 }\r
28                 clearFont();\r
29         }\r
30 \r
31         bool CharacterMap::selectFont( const LOGFONTA *font )\r
32         {\r
33                 clearFont();\r
34                 HFONT hFont = CreateFontIndirectA( font );\r
35                 SelectObject( hdc, hFont );\r
36 \r
37                 if ( !fetchUnicodeCMapTable() )\r
38                 {\r
39                         clearFont();\r
40                         return false;\r
41                 }\r
42                 characterCount = getCharacterCount( cmapTable );\r
43                 return true;\r
44         }\r
45 \r
46         void CharacterMap::clearFont( void )\r
47         {\r
48                 if ( NULL != hFont )\r
49                 {\r
50                         DeleteObject( hFont );\r
51                         hFont = NULL;\r
52                 }\r
53 \r
54                 delete [] cmapTable;\r
55                 cmapTable = NULL;\r
56 \r
57                 characterCount = 0;\r
58         }\r
59 \r
60         void CharacterMap::swapArrays( CMap4 *format4 )\r
61         {\r
62                 const USHORT *startCount = getStartCountArray( format4 );\r
63                 const USHORT *idDelta = getIdDeltaArray( format4 );\r
64                 const USHORT *idRangeOffset = getIdRangeOffsetArray( format4 );\r
65                 const USHORT *endCount = getEndCountArray( format4 );\r
66                 const USHORT *endOfBuffer = reinterpret_cast<const USHORT *>( \r
67                                                                                 reinterpret_cast<LPBYTE>( format4 ) + format4->length );\r
68                 \r
69                 const DWORD segCount = format4->segCountX2 / 2;\r
70                 for( DWORD i = 0; i < segCount; ++i )\r
71                 {\r
72                         swap( static_cast<USHORT>( endCount[i] ) );\r
73                         swap( static_cast<USHORT>( startCount[i] ) );\r
74                         swap( static_cast<USHORT>( idDelta[i] ) );\r
75                         swap( static_cast<USHORT>( idRangeOffset[i] ) );\r
76                 }\r
77 \r
78                 USHORT *glyphId = const_cast<USHORT *>( &idRangeOffset[segCount] );\r
79                 while( glyphId < endOfBuffer )\r
80                 {\r
81                         swap( *glyphId++ );\r
82                 }\r
83         }\r
84 \r
85 \r
86         bool CharacterMap::getEncoding( CMapEncoding *encoding, int index ) const\r
87         {\r
88                 DWORD result = GetFontData( hdc, cmapName, \r
89                                                                                 cmapHeaderSize + encodingSize * index, \r
90                                                                                 encoding, sizeof( CMapEncoding ) );\r
91                 if ( result != sizeof( CMapEncoding ) ) return false;\r
92 \r
93                 swap( encoding->PlatformId );\r
94                 swap( encoding->EncodingId );\r
95                 swap( encoding->Offset );\r
96 \r
97                 return true;\r
98         }\r
99 \r
100         bool CharacterMap::getFormat4Header( CMap4 *header, DWORD offset ) const\r
101         {\r
102                 DWORD written = GetFontData( hdc, cmapName, offset, header, format4HeaderSize );\r
103                 if ( written != format4HeaderSize ) return false;\r
104                 \r
105                 USHORT *fields = reinterpret_cast<USHORT *>( header );\r
106                 for( int i = 0; i < 7; ++i )\r
107                 {\r
108                         swap( fields[i] );\r
109                 }\r
110                 return true;\r
111         }\r
112 \r
113 \r
114         bool CharacterMap::getFormat4Subtable( CMap4 *subtable, DWORD offset ) const\r
115         {\r
116                 if ( !getFormat4Header( subtable, offset ) ) return false;\r
117 \r
118                 const DWORD length = subtable->length - format4HeaderSize;\r
119                 DWORD written = GetFontData( hdc, cmapName, offset + format4HeaderSize,\r
120                                                                                 subtable->Arrays, length );\r
121 \r
122                 if ( written != length ) return false;\r
123             \r
124                 swapArrays( subtable );\r
125 \r
126                 return true;\r
127         }\r
128 \r
129 \r
130         unsigned int CharacterMap::getCharacterCount( const CMap4 *format4 )\r
131         {       \r
132                 if ( format4 == NULL ) return 0;\r
133 \r
134                 const USHORT *endCount = getEndCountArray( format4 );\r
135                 const USHORT *startCount = getStartCountArray( format4 );\r
136                 const USHORT *idRangeOffset = getIdRangeOffsetArray( format4 );\r
137                 const USHORT segCount = format4->segCountX2 / 2;\r
138 \r
139                 unsigned int numberOfGlyphs = 0;\r
140 \r
141                 // by adding up the coverage of each segment\r
142                 for ( USHORT i = 0; i < segCount; ++i )\r
143                 {\r
144                         if ( idRangeOffset[i] == 0 )\r
145                         {\r
146                                 // if per the TT spec, the idRangeOffset element is zero,\r
147                                 // all of the characters in this segment exist.\r
148                                 numberOfGlyphs += endCount[i] - startCount[i] + 1;\r
149                                 continue;\r
150                         }\r
151 \r
152                         const USHORT *glyphId = &(idRangeOffset[i]) + idRangeOffset[i] / 2;\r
153 \r
154                         for (USHORT ch = startCount[i]; ch <= endCount[i]; ++ch )\r
155                         {\r
156                                 USHORT idResult = glyphId[ch - startCount[i]];\r
157                                 if (idResult != 0) numberOfGlyphs++;\r
158                         }\r
159                 }\r
160                 return numberOfGlyphs;\r
161         }\r
162 \r
163         bool CharacterMap::fetchUnicodeCMapTable( void )\r
164         {\r
165                 USHORT numberOfEncodings;\r
166                 // Get the number of subtables in the CMAP table from the CMAP header\r
167                 // The # of subtables is the second USHORT in the CMAP table, per the TT Spec.\r
168                 DWORD written = GetFontData( hdc, cmapName, sizeof( USHORT ), &numberOfEncodings, sizeof( USHORT ) );\r
169                 if ( written != sizeof( USHORT ) )\r
170                 {\r
171                         // Something is wrong, we probably got GDI_ERROR back\r
172                         // Probably this means that the Device Context does not have\r
173                         // a TrueType font selected into it.\r
174                         return false;\r
175                 }\r
176                 swap( numberOfEncodings );\r
177                 \r
178                 CMapEncoding encoding = {};       // The current encoding\r
179 \r
180                 // Get the encodings and look for a Unicode Encoding\r
181                 DWORD iUnicode = numberOfEncodings;\r
182                 for ( DWORD i = 0; i < numberOfEncodings; ++i )\r
183                 {\r
184                         if ( !getEncoding( &encoding, i ) ) return false;\r
185 \r
186                         if (encoding.PlatformId == 3 && \r
187                                 (encoding.EncodingId == 1 || encoding.EncodingId == 0) )\r
188                         {\r
189                                 iUnicode = i;       // Set the index to the Unicode encoding\r
190                         }\r
191                 }\r
192 \r
193                 // index out of range means failure to find a Unicode mapping\r
194                 if ( iUnicode >= numberOfEncodings )\r
195                 {\r
196                         // No Unicode encoding found.\r
197                         return false;\r
198                 }\r
199 \r
200                 CMap4 format4;        // Unicode subtable format\r
201                 // Get the header entries(first 7 USHORTs) for the Unicode encoding.\r
202                 if ( !getFormat4Header( &format4, encoding.Offset ) )\r
203                 {\r
204                         return false;\r
205                 }\r
206 \r
207                 if ( format4.format != 4 )\r
208                 {\r
209                         // Windows7\82Ì"\82l\82\96¾\92©"\93\99\82Íformat4.format\82ª12\r
210                         return false;\r
211                 }\r
212 \r
213                 delete [] cmapTable;\r
214                 cmapTable = reinterpret_cast<CMap4 *>( new unsigned char[format4.length] );\r
215                 if ( cmapTable == NULL ) return false;\r
216 \r
217                 if ( !getFormat4Subtable( cmapTable, encoding.Offset )) return false;\r
218 \r
219                 return true;\r
220         }\r
221 \r
222         bool CharacterMap::findFormat4Segment(\r
223                 CMap4 *table,     // a valid Format4 subtable buffer\r
224                 USHORT ch,          // Unicode character to search for\r
225                 USHORT &index       // out: index of segment containing ch\r
226                 ) const\r
227         {\r
228                 if ( NULL == table ) return FALSE;\r
229 \r
230                 const USHORT segCount = table->segCountX2 / 2;\r
231                 const USHORT *startCount = getStartCountArray( table );\r
232                 const USHORT *endCount = getEndCountArray( table );\r
233 \r
234                 // Find segment that could contain the Unicode character code\r
235                 for( USHORT i = 0; i < segCount; ++i )\r
236                 {\r
237                         if ( endCount[i] >= ch ) \r
238                         {\r
239                                 // character code not within the range of the segment\r
240                                 if ( startCount[i] > ch ) return false;\r
241                                 // this segment contains the character code\r
242                                 index = i;\r
243                                 return true;\r
244                         }\r
245                 }\r
246                 // We looked in them all, ch not there\r
247                 return false;\r
248         }\r
249 \r
250 \r
251         unsigned int CharacterMap::getGlyphIndex( unsigned int character )\r
252         /*\r
253                 When the TrueType font contains a glyph for ch, the\r
254                 function returns the glyph index for that character.\r
255 \r
256                 If an error occurs, or there is no glyph for ch, the\r
257                 function will return the missing glyph index of zero.\r
258         */ \r
259         {\r
260                 if ( NULL == cmapTable ) return 0;\r
261 \r
262                 USHORT i;\r
263                 // Find the cmap segment that has the character code.\r
264                 if ( !findFormat4Segment( cmapTable, static_cast<USHORT>( character ), i ) )\r
265                 {\r
266                         return 0;  // \8c©\82Â\82©\82ç\82È\82©\82Á\82½\r
267                 }\r
268 \r
269                 // Get pointers to the cmap data\r
270                 \r
271                 const USHORT *idRangeOffset = getIdRangeOffsetArray( cmapTable );\r
272                 const USHORT *idDelta = getIdDeltaArray( cmapTable );\r
273                 const USHORT *startCount = getStartCountArray( cmapTable );\r
274 \r
275                 if ( idRangeOffset[i] == 0 ) // Per TT spec, if the RangeOffset is zero,\r
276                 {\r
277                         // calculate the glyph index directly\r
278                         return static_cast<unsigned int>( (idDelta[i] + character) & 0xFFFF );\r
279                 }\r
280                 \r
281                 const USHORT *glyphId = &idRangeOffset[i] + idRangeOffset[i] / 2;\r
282                 // otherwise, use the glyph id array to get the index\r
283                 USHORT idResult = glyphId[character - startCount[i]];  // indexing equation from TT spec\r
284 \r
285                 if ( !idResult ) return 0; // return the missing glyph\r
286                 // Per TT spec, nonzero means there is a glyph\r
287                 return (idDelta[i] + idResult) & 0xFFFF;\r
288         }\r
289 \r
290 \r
291         /*  CMAP table Data\r
292                 From the TrueType Spec revision 1.66\r
293 \r
294                 USHORT  Table Version #\r
295                 USHORT  Number of encoding tables\r
296         */ \r
297         const size_t CharacterMap::cmapHeaderSize = sizeof( USHORT ) * 2;\r
298 \r
299         /*  ENCODING entry Data aka CMAPENCODING\r
300     From the TrueType Spec revision 1.66\r
301 \r
302     USHORT  Platform Id\r
303     USHORT  Platform Specific Encoding Id\r
304     ULONG   Byte Offset from beginning of table\r
305         */ \r
306         const size_t CharacterMap::encodingSize = sizeof( USHORT ) * 2 + sizeof( ULONG );\r
307 \r
308         const size_t CharacterMap::format4HeaderSize = sizeof( USHORT ) * 7;\r
309 \r
310         // DWORD packed four letter table name for each GetFontData()\r
311         // function call when working with the CMAP TrueType table\r
312         const DWORD CharacterMap::cmapName = makeTableName( 'c','m','a','p' );\r
313 }\r