OSDN Git Service

NSDフォントプラグイン登録
[kimikage-nscr/kimikage-nscr.git] / nsdfont / src / CharacterMap.cpp
diff --git a/nsdfont/src/CharacterMap.cpp b/nsdfont/src/CharacterMap.cpp
new file mode 100755 (executable)
index 0000000..9277180
--- /dev/null
@@ -0,0 +1,313 @@
+/*\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\82\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