--- /dev/null
+/*\r
+ * Kimikage NScripter Plugins Project\r
+ *\r
+ * This software is distributed under a BSD-style license.\r
+ * See license.txt for more information.\r
+ */\r
+\r
+#include "CharacterMap.h"\r
+\r
+// This code is based on a sample code of\r
+// KB\81F241020 (http://support.microsoft.com/kb/241020/)\r
+\r
+namespace nsdfont\r
+{\r
+ CharacterMap::CharacterMap( void )\r
+ {\r
+ hdc = CreateCompatibleDC( NULL );\r
+ hFont = NULL;\r
+ cmapTable = NULL;\r
+ }\r
+\r
+ CharacterMap::~CharacterMap( void )\r
+ {\r
+ if ( NULL != hdc )\r
+ {\r
+ DeleteDC( hdc );\r
+ }\r
+ clearFont();\r
+ }\r
+\r
+ bool CharacterMap::selectFont( const LOGFONTA *font )\r
+ {\r
+ clearFont();\r
+ HFONT hFont = CreateFontIndirectA( font );\r
+ SelectObject( hdc, hFont );\r
+\r
+ if ( !fetchUnicodeCMapTable() )\r
+ {\r
+ clearFont();\r
+ return false;\r
+ }\r
+ characterCount = getCharacterCount( cmapTable );\r
+ return true;\r
+ }\r
+\r
+ void CharacterMap::clearFont( void )\r
+ {\r
+ if ( NULL != hFont )\r
+ {\r
+ DeleteObject( hFont );\r
+ hFont = NULL;\r
+ }\r
+\r
+ delete [] cmapTable;\r
+ cmapTable = NULL;\r
+\r
+ characterCount = 0;\r
+ }\r
+\r
+ void CharacterMap::swapArrays( CMap4 *format4 )\r
+ {\r
+ const USHORT *startCount = getStartCountArray( format4 );\r
+ const USHORT *idDelta = getIdDeltaArray( format4 );\r
+ const USHORT *idRangeOffset = getIdRangeOffsetArray( format4 );\r
+ const USHORT *endCount = getEndCountArray( format4 );\r
+ const USHORT *endOfBuffer = reinterpret_cast<const USHORT *>( \r
+ reinterpret_cast<LPBYTE>( format4 ) + format4->length );\r
+ \r
+ const DWORD segCount = format4->segCountX2 / 2;\r
+ for( DWORD i = 0; i < segCount; ++i )\r
+ {\r
+ swap( static_cast<USHORT>( endCount[i] ) );\r
+ swap( static_cast<USHORT>( startCount[i] ) );\r
+ swap( static_cast<USHORT>( idDelta[i] ) );\r
+ swap( static_cast<USHORT>( idRangeOffset[i] ) );\r
+ }\r
+\r
+ USHORT *glyphId = const_cast<USHORT *>( &idRangeOffset[segCount] );\r
+ while( glyphId < endOfBuffer )\r
+ {\r
+ swap( *glyphId++ );\r
+ }\r
+ }\r
+\r
+\r
+ bool CharacterMap::getEncoding( CMapEncoding *encoding, int index ) const\r
+ {\r
+ DWORD result = GetFontData( hdc, cmapName, \r
+ cmapHeaderSize + encodingSize * index, \r
+ encoding, sizeof( CMapEncoding ) );\r
+ if ( result != sizeof( CMapEncoding ) ) return false;\r
+\r
+ swap( encoding->PlatformId );\r
+ swap( encoding->EncodingId );\r
+ swap( encoding->Offset );\r
+\r
+ return true;\r
+ }\r
+\r
+ bool CharacterMap::getFormat4Header( CMap4 *header, DWORD offset ) const\r
+ {\r
+ DWORD written = GetFontData( hdc, cmapName, offset, header, format4HeaderSize );\r
+ if ( written != format4HeaderSize ) return false;\r
+ \r
+ USHORT *fields = reinterpret_cast<USHORT *>( header );\r
+ for( int i = 0; i < 7; ++i )\r
+ {\r
+ swap( fields[i] );\r
+ }\r
+ return true;\r
+ }\r
+\r
+\r
+ bool CharacterMap::getFormat4Subtable( CMap4 *subtable, DWORD offset ) const\r
+ {\r
+ if ( !getFormat4Header( subtable, offset ) ) return false;\r
+\r
+ const DWORD length = subtable->length - format4HeaderSize;\r
+ DWORD written = GetFontData( hdc, cmapName, offset + format4HeaderSize,\r
+ subtable->Arrays, length );\r
+\r
+ if ( written != length ) return false;\r
+ \r
+ swapArrays( subtable );\r
+\r
+ return true;\r
+ }\r
+\r
+\r
+ unsigned int CharacterMap::getCharacterCount( const CMap4 *format4 )\r
+ { \r
+ if ( format4 == NULL ) return 0;\r
+\r
+ const USHORT *endCount = getEndCountArray( format4 );\r
+ const USHORT *startCount = getStartCountArray( format4 );\r
+ const USHORT *idRangeOffset = getIdRangeOffsetArray( format4 );\r
+ const USHORT segCount = format4->segCountX2 / 2;\r
+\r
+ unsigned int numberOfGlyphs = 0;\r
+\r
+ // by adding up the coverage of each segment\r
+ for ( USHORT i = 0; i < segCount; ++i )\r
+ {\r
+ if ( idRangeOffset[i] == 0 )\r
+ {\r
+ // if per the TT spec, the idRangeOffset element is zero,\r
+ // all of the characters in this segment exist.\r
+ numberOfGlyphs += endCount[i] - startCount[i] + 1;\r
+ continue;\r
+ }\r
+\r
+ const USHORT *glyphId = &(idRangeOffset[i]) + idRangeOffset[i] / 2;\r
+\r
+ for (USHORT ch = startCount[i]; ch <= endCount[i]; ++ch )\r
+ {\r
+ USHORT idResult = glyphId[ch - startCount[i]];\r
+ if (idResult != 0) numberOfGlyphs++;\r
+ }\r
+ }\r
+ return numberOfGlyphs;\r
+ }\r
+\r
+ bool CharacterMap::fetchUnicodeCMapTable( void )\r
+ {\r
+ USHORT numberOfEncodings;\r
+ // Get the number of subtables in the CMAP table from the CMAP header\r
+ // The # of subtables is the second USHORT in the CMAP table, per the TT Spec.\r
+ DWORD written = GetFontData( hdc, cmapName, sizeof( USHORT ), &numberOfEncodings, sizeof( USHORT ) );\r
+ if ( written != sizeof( USHORT ) )\r
+ {\r
+ // Something is wrong, we probably got GDI_ERROR back\r
+ // Probably this means that the Device Context does not have\r
+ // a TrueType font selected into it.\r
+ return false;\r
+ }\r
+ swap( numberOfEncodings );\r
+ \r
+ CMapEncoding encoding = {}; // The current encoding\r
+\r
+ // Get the encodings and look for a Unicode Encoding\r
+ DWORD iUnicode = numberOfEncodings;\r
+ for ( DWORD i = 0; i < numberOfEncodings; ++i )\r
+ {\r
+ if ( !getEncoding( &encoding, i ) ) return false;\r
+\r
+ if (encoding.PlatformId == 3 && \r
+ (encoding.EncodingId == 1 || encoding.EncodingId == 0) )\r
+ {\r
+ iUnicode = i; // Set the index to the Unicode encoding\r
+ }\r
+ }\r
+\r
+ // index out of range means failure to find a Unicode mapping\r
+ if ( iUnicode >= numberOfEncodings )\r
+ {\r
+ // No Unicode encoding found.\r
+ return false;\r
+ }\r
+\r
+ CMap4 format4; // Unicode subtable format\r
+ // Get the header entries(first 7 USHORTs) for the Unicode encoding.\r
+ if ( !getFormat4Header( &format4, encoding.Offset ) )\r
+ {\r
+ return false;\r
+ }\r
+\r
+ if ( format4.format != 4 )\r
+ {\r
+ // Windows7\82Ì"\82l\82r \96¾\92©"\93\99\82Íformat4.format\82ª12\r
+ return false;\r
+ }\r
+\r
+ delete [] cmapTable;\r
+ cmapTable = reinterpret_cast<CMap4 *>( new unsigned char[format4.length] );\r
+ if ( cmapTable == NULL ) return false;\r
+\r
+ if ( !getFormat4Subtable( cmapTable, encoding.Offset )) return false;\r
+\r
+ return true;\r
+ }\r
+\r
+ bool CharacterMap::findFormat4Segment(\r
+ CMap4 *table, // a valid Format4 subtable buffer\r
+ USHORT ch, // Unicode character to search for\r
+ USHORT &index // out: index of segment containing ch\r
+ ) const\r
+ {\r
+ if ( NULL == table ) return FALSE;\r
+\r
+ const USHORT segCount = table->segCountX2 / 2;\r
+ const USHORT *startCount = getStartCountArray( table );\r
+ const USHORT *endCount = getEndCountArray( table );\r
+\r
+ // Find segment that could contain the Unicode character code\r
+ for( USHORT i = 0; i < segCount; ++i )\r
+ {\r
+ if ( endCount[i] >= ch ) \r
+ {\r
+ // character code not within the range of the segment\r
+ if ( startCount[i] > ch ) return false;\r
+ // this segment contains the character code\r
+ index = i;\r
+ return true;\r
+ }\r
+ }\r
+ // We looked in them all, ch not there\r
+ return false;\r
+ }\r
+\r
+\r
+ unsigned int CharacterMap::getGlyphIndex( unsigned int character )\r
+ /*\r
+ When the TrueType font contains a glyph for ch, the\r
+ function returns the glyph index for that character.\r
+\r
+ If an error occurs, or there is no glyph for ch, the\r
+ function will return the missing glyph index of zero.\r
+ */ \r
+ {\r
+ if ( NULL == cmapTable ) return 0;\r
+\r
+ USHORT i;\r
+ // Find the cmap segment that has the character code.\r
+ if ( !findFormat4Segment( cmapTable, static_cast<USHORT>( character ), i ) )\r
+ {\r
+ return 0; // \8c©\82Â\82©\82ç\82È\82©\82Á\82½\r
+ }\r
+\r
+ // Get pointers to the cmap data\r
+ \r
+ const USHORT *idRangeOffset = getIdRangeOffsetArray( cmapTable );\r
+ const USHORT *idDelta = getIdDeltaArray( cmapTable );\r
+ const USHORT *startCount = getStartCountArray( cmapTable );\r
+\r
+ if ( idRangeOffset[i] == 0 ) // Per TT spec, if the RangeOffset is zero,\r
+ {\r
+ // calculate the glyph index directly\r
+ return static_cast<unsigned int>( (idDelta[i] + character) & 0xFFFF );\r
+ }\r
+ \r
+ const USHORT *glyphId = &idRangeOffset[i] + idRangeOffset[i] / 2;\r
+ // otherwise, use the glyph id array to get the index\r
+ USHORT idResult = glyphId[character - startCount[i]]; // indexing equation from TT spec\r
+\r
+ if ( !idResult ) return 0; // return the missing glyph\r
+ // Per TT spec, nonzero means there is a glyph\r
+ return (idDelta[i] + idResult) & 0xFFFF;\r
+ }\r
+\r
+\r
+ /* CMAP table Data\r
+ From the TrueType Spec revision 1.66\r
+\r
+ USHORT Table Version #\r
+ USHORT Number of encoding tables\r
+ */ \r
+ const size_t CharacterMap::cmapHeaderSize = sizeof( USHORT ) * 2;\r
+\r
+ /* ENCODING entry Data aka CMAPENCODING\r
+ From the TrueType Spec revision 1.66\r
+\r
+ USHORT Platform Id\r
+ USHORT Platform Specific Encoding Id\r
+ ULONG Byte Offset from beginning of table\r
+ */ \r
+ const size_t CharacterMap::encodingSize = sizeof( USHORT ) * 2 + sizeof( ULONG );\r
+\r
+ const size_t CharacterMap::format4HeaderSize = sizeof( USHORT ) * 7;\r
+\r
+ // DWORD packed four letter table name for each GetFontData()\r
+ // function call when working with the CMAP TrueType table\r
+ const DWORD CharacterMap::cmapName = makeTableName( 'c','m','a','p' );\r
+}\r