2017-06-25 17:14:03 +02:00
using System.Globalization ;
2014-10-27 21:12:57 +01:00
using System.IO ;
2014-11-05 22:39:31 +01:00
using System.IO.Compression ;
using System.Reflection ;
2017-06-25 20:09:54 +02:00
using System.Runtime.CompilerServices ;
2014-10-27 21:12:57 +01:00
using System.Text ;
2014-11-03 00:05:54 +01:00
namespace System.Unicode
2014-10-27 21:12:57 +01:00
{
2014-12-15 01:57:09 +01:00
/// <summary>Provides access to unicode information.</summary>
2014-11-15 13:36:19 +01:00
public static class UnicodeInfo
2014-10-27 21:12:57 +01:00
{
2017-06-25 02:36:38 +02:00
// NB: These fields will be used as a default value for UnicodeCharacterData and UnihanCharacterData, and passed by reference.
// They should be considered as being redonly, even if they aren't explicitely.
private static /*readonly*/ UnicodeCharacterData DefaultUnicodeCharacterData = new UnicodeCharacterData ( default ( UnicodeCodePointRange ), null , null , UnicodeCategory . OtherNotAssigned , 0 , 0 , 0 , null , 0 , default ( UnicodeRationalNumber ), false , null , null , null , null , 0 , 0 , null );
private static /*readonly*/ UnihanCharacterData DefaultUnihanCharacterData = default ( UnihanCharacterData );
2014-12-15 01:57:09 +01:00
/// <summary>The block name returned when no block is assigned to a specific code point.</summary>
2014-11-20 22:00:07 +01:00
public const string DefaultBlock = "No_Block" ;
2014-11-24 00:24:55 +01:00
private static readonly Version unicodeVersion ;
2014-11-15 13:36:19 +01:00
private static readonly UnicodeCharacterData [] unicodeCharacterData ;
private static readonly UnihanCharacterData [] unihanCharacterData ;
private static readonly UnicodeBlock [] blocks ;
2014-11-24 20:09:59 +01:00
private static readonly CjkRadicalData [] radicals ;
2014-11-23 16:38:52 +01:00
private static readonly int maxContiguousIndex ;
2017-09-07 23:10:17 +02:00
private static readonly string [] emojiSequences ;
2014-11-05 22:39:31 +01:00
2014-11-15 13:36:19 +01:00
static UnicodeInfo ()
2014-11-05 22:39:31 +01:00
{
2014-11-08 01:43:17 +01:00
using ( var stream = new DeflateStream ( typeof ( UnicodeInfo ). GetTypeInfo (). Assembly . GetManifestResourceStream ( "ucd.dat" ), CompressionMode . Decompress , false ))
2014-11-05 22:39:31 +01:00
{
2014-11-24 20:09:59 +01:00
ReadFromStream ( stream , out unicodeVersion , out unicodeCharacterData , out unihanCharacterData , out radicals , out blocks , out maxContiguousIndex );
2014-11-05 22:39:31 +01:00
}
}
2014-11-24 20:09:59 +01:00
internal static void ReadFromStream ( Stream stream , out Version unicodeVersion , out UnicodeCharacterData [] unicodeCharacterData , out UnihanCharacterData [] unihanCharacterData , out CjkRadicalData [] radicals , out UnicodeBlock [] blocks , out int maxContiguousIndex )
2014-10-27 21:12:57 +01:00
{
2014-11-03 03:44:41 +01:00
using ( var reader = new BinaryReader ( stream , Encoding . UTF8 ))
{
2014-11-23 16:38:52 +01:00
int i ;
2014-11-24 00:24:55 +01:00
if ( reader . ReadByte () != 'U'
2014-11-03 03:44:41 +01:00
| reader . ReadByte () != 'C'
| reader . ReadByte () != 'D' )
throw new InvalidDataException ();
2014-10-27 21:12:57 +01:00
2014-11-03 03:44:41 +01:00
byte formatVersion = reader . ReadByte ();
2014-10-27 21:12:57 +01:00
2015-07-19 01:56:28 +02:00
if ( formatVersion != 2 ) throw new InvalidDataException ();
2014-10-27 21:12:57 +01:00
2015-07-19 02:28:53 +02:00
var fileUnicodeVersion = new Version ( reader . ReadUInt16 (), reader . ReadByte (), reader . ReadByte ());
2017-09-07 23:10:17 +02:00
2017-06-25 02:36:38 +02:00
var unicodeCharacterDataEntries = new UnicodeCharacterData [ ReadCodePoint ( reader )]; // Allocate one extra entry to act as a dummy entry.
2014-11-21 16:02:19 +01:00
byte [] nameBuffer = new byte [ 128 ];
2014-11-23 16:38:52 +01:00
int mci = 0 ;
2014-10-27 21:12:57 +01:00
2014-11-23 16:38:52 +01:00
for ( i = 0 ; i < unicodeCharacterDataEntries . Length ; ++ i )
{
2017-06-25 02:36:38 +02:00
ReadUnicodeCharacterDataEntry ( reader , nameBuffer , out unicodeCharacterDataEntries [ i ]);
if ( unicodeCharacterDataEntries [ i ]. CodePointRange . Contains ( i ))
{
mci = i ;
}
2014-11-23 16:38:52 +01:00
else
{
++ i ;
break ;
}
2014-11-24 00:24:55 +01:00
}
2014-11-23 16:38:52 +01:00
maxContiguousIndex = mci ;
for (; i < unicodeCharacterDataEntries . Length ; ++ i )
2014-11-03 03:44:41 +01:00
{
2017-09-07 23:10:17 +02:00
ReadUnicodeCharacterDataEntry ( reader , nameBuffer , out unicodeCharacterDataEntries [ i ]);
2014-11-03 03:44:41 +01:00
}
2014-10-27 21:12:57 +01:00
2015-07-19 01:56:28 +02:00
var blockEntries = new UnicodeBlock [ reader . ReadUInt16 ()];
2014-11-08 01:43:17 +01:00
2014-11-23 16:38:52 +01:00
for ( i = 0 ; i < blockEntries . Length ; ++ i )
2014-11-08 01:43:17 +01:00
{
2017-06-25 17:14:03 +02:00
ReadBlockEntry ( reader , out blockEntries [ i ]);
2014-11-15 11:50:43 +01:00
}
2014-11-08 01:43:17 +01:00
2014-11-24 20:09:59 +01:00
var cjkRadicalEntries = new CjkRadicalData [ reader . ReadByte ()];
for ( i = 0 ; i < cjkRadicalEntries . Length ; ++ i )
{
2017-06-25 17:14:03 +02:00
ReadCjkRadicalInfo ( reader , out cjkRadicalEntries [ i ]);
2015-07-19 02:28:53 +02:00
}
2014-11-24 20:09:59 +01:00
2014-11-15 11:50:43 +01:00
var unihanCharacterDataEntries = new UnihanCharacterData [ ReadCodePoint ( reader )];
2014-11-23 16:38:52 +01:00
for ( i = 0 ; i < unihanCharacterDataEntries . Length ; ++ i )
2014-11-15 11:50:43 +01:00
{
2017-06-25 02:36:38 +02:00
ReadUnihanCharacterDataEntry ( reader , out unihanCharacterDataEntries [ i ]);
2014-11-15 11:50:43 +01:00
}
2014-11-15 13:36:19 +01:00
unicodeVersion = fileUnicodeVersion ;
unicodeCharacterData = unicodeCharacterDataEntries ;
unihanCharacterData = unihanCharacterDataEntries ;
2014-11-24 20:09:59 +01:00
radicals = cjkRadicalEntries ;
2014-11-15 13:36:19 +01:00
blocks = blockEntries ;
2014-11-03 03:44:41 +01:00
}
}
2017-06-25 02:36:38 +02:00
private static void ReadUnicodeCharacterDataEntry ( BinaryReader reader , byte [] nameBuffer , out UnicodeCharacterData value )
2014-11-03 03:44:41 +01:00
{
var fields = ( UcdFields ) reader . ReadUInt16 ();
2014-11-24 21:47:06 +01:00
var codePointRange = ( fields & UcdFields . CodePointRange ) != 0 ? new UnicodeCodePointRange ( ReadCodePoint ( reader ), ReadCodePoint ( reader )) : new UnicodeCodePointRange ( ReadCodePoint ( reader ));
2014-11-03 03:44:41 +01:00
2014-11-21 15:41:27 +01:00
string name = null ;
UnicodeNameAlias [] nameAliases = UnicodeNameAlias . EmptyArray ;
// Read all the official names of the character.
if (( fields & UcdFields . Name ) != 0 )
{
int length = reader . ReadByte ();
byte @case = ( byte )( length & 0xC0 );
2014-11-24 00:24:55 +01:00
if ( @case < 0x80 ) // Handles the case where only the name is present.
2014-11-21 15:41:27 +01:00
{
2014-11-21 16:02:19 +01:00
length = ( length & 0x7F ) + 1 ;
2014-11-21 15:41:27 +01:00
if ( reader . Read ( nameBuffer , 0 , length ) != length ) throw new EndOfStreamException ();
name = Encoding . UTF8 . GetString ( nameBuffer , 0 , length );
}
2014-11-21 16:02:19 +01:00
else
2014-11-21 15:41:27 +01:00
{
2014-11-21 16:02:19 +01:00
nameAliases = new UnicodeNameAlias [( length & 0x3F ) + 1 ];
if (( @case & 0x40 ) != 0 )
{
length = reader . ReadByte () + 1 ;
if ( length > 128 ) throw new InvalidDataException ( "Did not expect names longer than 128 bytes." );
if ( reader . Read ( nameBuffer , 0 , length ) != length ) throw new EndOfStreamException ();
name = Encoding . UTF8 . GetString ( nameBuffer , 0 , length );
}
2014-11-21 15:41:27 +01:00
for ( int i = 0 ; i < nameAliases . Length ; ++ i )
{
2014-11-21 17:04:55 +01:00
nameAliases [ i ] = new UnicodeNameAlias ( reader . ReadString (), ( UnicodeNameAliasKind )( reader . ReadByte ()));
2014-11-21 16:02:19 +01:00
}
2014-11-21 15:41:27 +01:00
}
}
2014-11-03 03:44:41 +01:00
var category = ( fields & UcdFields . Category ) != 0 ? ( UnicodeCategory ) reader . ReadByte () : UnicodeCategory . OtherNotAssigned ;
var canonicalCombiningClass = ( fields & UcdFields . CanonicalCombiningClass ) != 0 ? ( CanonicalCombiningClass ) reader . ReadByte () : CanonicalCombiningClass . NotReordered ;
var bidirectionalClass = ( fields & UcdFields . BidirectionalClass ) != 0 ? ( BidirectionalClass ) reader . ReadByte () : 0 ;
2014-11-07 01:05:40 +01:00
CompatibilityFormattingTag decompositionType = ( fields & UcdFields . DecompositionMapping ) != 0 ? ( CompatibilityFormattingTag ) reader . ReadByte () : CompatibilityFormattingTag . Canonical ;
string decompositionMapping = ( fields & UcdFields . DecompositionMapping ) != 0 ? reader . ReadString () : null ;
2014-11-03 03:44:41 +01:00
var numericType = ( UnicodeNumericType )(( int )( fields & UcdFields . NumericNumeric ) >> 6 );
UnicodeRationalNumber numericValue = numericType != UnicodeNumericType . None ?
new UnicodeRationalNumber ( reader . ReadInt64 (), reader . ReadByte ()) :
default ( UnicodeRationalNumber );
string oldName = ( fields & UcdFields . OldName ) != 0 ? reader . ReadString () : null ;
string simpleUpperCaseMapping = ( fields & UcdFields . SimpleUpperCaseMapping ) != 0 ? reader . ReadString () : null ;
string simpleLowerCaseMapping = ( fields & UcdFields . SimpleLowerCaseMapping ) != 0 ? reader . ReadString () : null ;
string simpleTitleCaseMapping = ( fields & UcdFields . SimpleTitleCaseMapping ) != 0 ? reader . ReadString () : null ;
ContributoryProperties contributoryProperties = ( fields & UcdFields . ContributoryProperties ) != 0 ? ( ContributoryProperties ) reader . ReadInt32 () : 0 ;
2017-06-25 02:36:38 +02:00
int corePropertiesAndEmojiProperties = ( fields & UcdFields . CorePropertiesAndEmojiProperties ) != 0 ? ReadInt24 ( reader ) : 0 ;
2014-11-24 00:24:55 +01:00
int [] crossReferences = ( fields & UcdFields . CrossRerefences ) != 0 ? new int [ reader . ReadByte () + 1 ] : null ;
2014-11-23 18:44:32 +01:00
2014-11-24 00:24:55 +01:00
if ( crossReferences != null )
2014-11-23 18:44:32 +01:00
{
2014-11-24 00:24:55 +01:00
for ( int i = 0 ; i < crossReferences . Length ; ++ i )
crossReferences [ i ] = ReadCodePoint ( reader );
}
2014-11-03 03:44:41 +01:00
2017-06-25 02:36:38 +02:00
value = new UnicodeCharacterData
2014-11-03 03:44:41 +01:00
(
codePointRange ,
name ,
2014-11-21 15:41:27 +01:00
nameAliases ,
2014-11-03 03:44:41 +01:00
category ,
canonicalCombiningClass ,
bidirectionalClass ,
decompositionType ,
2014-11-07 01:05:40 +01:00
decompositionMapping ,
2014-11-03 03:44:41 +01:00
numericType ,
numericValue ,
( fields & UcdFields . BidirectionalMirrored ) != 0 ,
oldName ,
simpleUpperCaseMapping ,
simpleLowerCaseMapping ,
simpleTitleCaseMapping ,
contributoryProperties ,
2017-06-25 02:36:38 +02:00
corePropertiesAndEmojiProperties ,
2014-11-24 00:24:55 +01:00
crossReferences
2014-11-03 03:44:41 +01:00
);
2014-11-16 21:27:57 +01:00
}
2014-11-03 03:44:41 +01:00
2017-06-25 02:36:38 +02:00
private static void ReadUnihanCharacterDataEntry ( BinaryReader reader , out UnihanCharacterData value )
2014-11-15 11:50:43 +01:00
{
var fields = ( UnihanFields ) reader . ReadUInt16 ();
int codePoint = UnihanCharacterData . UnpackCodePoint ( ReadCodePoint ( reader ));
var numericType = ( UnihanNumericType )(( int )( fields & UnihanFields . OtherNumeric ));
long numericValue = numericType != UnihanNumericType . None ?
reader . ReadInt64 () :
0 ;
2014-11-24 02:36:09 +01:00
UnicodeRadicalStrokeCount [] unicodeRadicalStrokeCounts = ( fields & UnihanFields . UnicodeRadicalStrokeCountMore ) != 0 ?
new UnicodeRadicalStrokeCount
[
(fields & UnihanFields.UnicodeRadicalStrokeCountMore) == UnihanFields.UnicodeRadicalStrokeCountMore ?
reader.ReadByte() + 3 :
((byte)(fields & UnihanFields.UnicodeRadicalStrokeCountMore) >> 2)
] :
UnicodeRadicalStrokeCount . EmptyArray ;
for ( int i = 0 ; i < unicodeRadicalStrokeCounts . Length ; ++ i )
unicodeRadicalStrokeCounts [ i ] = new UnicodeRadicalStrokeCount ( reader . ReadByte (), reader . ReadByte ());
2014-11-15 11:50:43 +01:00
string definition = ( fields & UnihanFields . Definition ) != 0 ? reader . ReadString () : null ;
string mandarinReading = ( fields & UnihanFields . MandarinReading ) != 0 ? reader . ReadString () : null ;
string cantoneseReading = ( fields & UnihanFields . CantoneseReading ) != 0 ? reader . ReadString () : null ;
string japaneseKunReading = ( fields & UnihanFields . JapaneseKunReading ) != 0 ? reader . ReadString () : null ;
string japaneseOnReading = ( fields & UnihanFields . JapaneseOnReading ) != 0 ? reader . ReadString () : null ;
string koreanReading = ( fields & UnihanFields . KoreanReading ) != 0 ? reader . ReadString () : null ;
string hangulReading = ( fields & UnihanFields . HangulReading ) != 0 ? reader . ReadString () : null ;
string vietnameseReading = ( fields & UnihanFields . VietnameseReading ) != 0 ? reader . ReadString () : null ;
string simplifiedVariant = ( fields & UnihanFields . SimplifiedVariant ) != 0 ? reader . ReadString () : null ;
string traditionalVariant = ( fields & UnihanFields . TraditionalVariant ) != 0 ? reader . ReadString () : null ;
2017-06-25 02:36:38 +02:00
value = new UnihanCharacterData
2014-11-15 11:50:43 +01:00
(
codePoint ,
numericType ,
numericValue ,
2014-11-24 02:36:09 +01:00
unicodeRadicalStrokeCounts ,
2014-11-15 11:50:43 +01:00
definition ,
mandarinReading ,
cantoneseReading ,
japaneseKunReading ,
japaneseOnReading ,
koreanReading ,
hangulReading ,
vietnameseReading ,
simplifiedVariant ,
traditionalVariant
);
}
2017-06-25 17:14:03 +02:00
private static void ReadCjkRadicalInfo ( BinaryReader reader , out CjkRadicalData value )
2014-11-24 20:09:59 +01:00
{
char tr ;
char tc ;
tr = ( char ) reader . ReadUInt16 ();
tc = ( char ) reader . ReadUInt16 ();
2017-06-25 17:14:03 +02:00
value = ( tr & 0x8000 ) == 0 ?
new CjkRadicalData ( tr , tc ) :
new CjkRadicalData (( char )( tr & 0x7FFF ), tc , ( char ) reader . ReadUInt16 (), ( char ) reader . ReadUInt16 ());
2014-11-24 20:09:59 +01:00
}
2017-06-25 17:14:03 +02:00
private static void ReadBlockEntry ( BinaryReader reader , out UnicodeBlock value )
=> value = new UnicodeBlock ( new UnicodeCodePointRange ( ReadCodePoint ( reader ), ReadCodePoint ( reader )), reader . ReadString ());
2014-11-08 01:43:17 +01:00
2017-06-25 02:36:38 +02:00
private static int ReadInt24 ( BinaryReader reader ) => reader . ReadByte () | (( reader . ReadByte () | ( reader . ReadByte () << 8 )) << 8 );
2014-11-08 22:00:12 +01:00
2014-11-04 00:50:42 +01:00
#if DEBUG
internal static int ReadCodePoint ( BinaryReader reader )
#else
2014-11-03 03:44:41 +01:00
private static int ReadCodePoint ( BinaryReader reader )
2014-11-04 00:50:42 +01:00
#endif
2014-11-03 03:44:41 +01:00
{
byte b = reader . ReadByte ();
2014-11-04 00:00:08 +01:00
if ( b < 0xA0 ) return b ;
else if ( b < 0xC0 )
2014-11-03 03:44:41 +01:00
{
2014-11-04 00:00:08 +01:00
return 0xA0 + ((( b & 0x1F ) << 8 ) | reader . ReadByte ());
}
else if ( b < 0xE0 )
{
return 0x20A0 + ((( b & 0x1F ) << 8 ) | reader . ReadByte ());
}
2014-11-03 03:44:41 +01:00
else
{
2014-11-04 00:00:08 +01:00
return 0x40A0 + ((((( b & 0x1F ) << 8 ) | reader . ReadByte ()) << 8 ) | reader . ReadByte ());
2014-11-16 21:27:57 +01:00
}
2014-10-27 21:12:57 +01:00
}
2014-12-15 01:57:09 +01:00
/// <summary>Gets the version of the Unicode standard supported by the class.</summary>
2014-11-15 13:36:19 +01:00
public static Version UnicodeVersion { get { return unicodeVersion ; } }
2014-11-15 11:50:43 +01:00
2017-06-25 02:36:38 +02:00
private static int FindUnicodeCodePointIndex ( int codePoint )
=> codePoint <= maxContiguousIndex ?
codePoint : // For the first code points (this includes all of ASCII, and quite a bit more), the index in the table will be the code point itself.
BinarySearchUnicodeCodePointIndex ( codePoint ); // For other code points, we will use a classic binary search with adjusted search indexes.
2014-11-23 16:38:52 +01:00
2017-06-25 02:36:38 +02:00
private static int BinarySearchUnicodeCodePointIndex ( int codePoint )
2014-11-23 16:38:52 +01:00
{
// NB: Due to the strictly ordered nature of the table, we know that a code point can never happen after the index which is the code point itself.
// This will greatly reduce the range to scan for characters close to maxContiguousIndex, and will have a lesser impact on other characters.
int minIndex = maxContiguousIndex + 1 ;
int maxIndex = codePoint < unicodeCharacterData . Length ? codePoint - 1 : unicodeCharacterData . Length - 1 ;
2014-11-03 00:05:54 +01:00
do
{
int index = ( minIndex + maxIndex ) >> 1 ;
2014-11-15 11:50:43 +01:00
int Δ = unicodeCharacterData [ index ]. CodePointRange . CompareCodePoint ( codePoint );
2014-11-03 00:05:54 +01:00
2017-06-25 02:36:38 +02:00
if ( Δ == 0 ) return index ;
2014-11-15 11:50:43 +01:00
else if ( Δ < 0 ) maxIndex = index - 1 ;
else minIndex = index + 1 ;
} while ( minIndex <= maxIndex );
2017-06-25 02:36:38 +02:00
return - 1 ;
2014-11-15 11:50:43 +01:00
}
2017-06-25 02:36:38 +02:00
private static int FindUnihanCodePointIndex ( int codePoint )
2014-11-15 11:50:43 +01:00
{
int minIndex ;
int maxIndex ;
2014-11-24 20:09:59 +01:00
if ( unihanCharacterData . Length == 0 || codePoint < unihanCharacterData [ minIndex = 0 ]. CodePoint || codePoint > unihanCharacterData [ maxIndex = unihanCharacterData . Length - 1 ]. CodePoint )
2014-11-15 11:50:43 +01:00
{
2017-06-25 02:36:38 +02:00
return - 1 ;
2014-11-15 11:50:43 +01:00
}
do
{
int index = ( minIndex + maxIndex ) >> 1 ;
int Δ = codePoint - unihanCharacterData [ index ]. CodePoint ;
2017-06-25 02:36:38 +02:00
if ( Δ == 0 ) return index ;
2014-11-03 00:05:54 +01:00
else if ( Δ < 0 ) maxIndex = index - 1 ;
else minIndex = index + 1 ;
} while ( minIndex <= maxIndex );
2017-06-25 02:36:38 +02:00
return - 1 ;
2014-11-03 00:05:54 +01:00
}
2014-11-15 13:36:19 +01:00
private static int FindBlockIndex ( int codePoint )
2014-10-27 21:12:57 +01:00
{
2014-11-08 01:43:17 +01:00
int minIndex = 0 ;
2014-11-15 13:36:19 +01:00
int maxIndex = blocks . Length - 1 ;
2014-11-08 01:43:17 +01:00
do
{
int index = ( minIndex + maxIndex ) >> 1 ;
2014-11-15 13:36:19 +01:00
int Δ = blocks [ index ]. CodePointRange . CompareCodePoint ( codePoint );
2014-11-08 01:43:17 +01:00
if ( Δ == 0 ) return index ;
else if ( Δ < 0 ) maxIndex = index - 1 ;
else minIndex = index + 1 ;
} while ( minIndex <= maxIndex );
return - 1 ;
2014-10-27 21:12:57 +01:00
}
2014-11-08 01:43:17 +01:00
2017-06-25 02:36:38 +02:00
internal static ref UnicodeCharacterData GetUnicodeCharacterData ( int index )
{
if ( index >= 0 ) return ref unicodeCharacterData [ index ];
return ref DefaultUnicodeCharacterData ;
}
internal static ref UnihanCharacterData GetUnihanCharacterData ( int index )
{
if ( index >= 0 ) return ref unihanCharacterData [ index ];
return ref DefaultUnihanCharacterData ;
}
2014-11-24 20:09:59 +01:00
/// <summary>Gets the name of the Unicode block containing the character.</summary>
/// <remarks>If the character has not been assigned to a block, the value of <see cref="DefaultBlock"/> will be returned.</remarks>
/// <param name="codePoint">The Unicode code point whose block should be retrieved.</param>
/// <returns>The name of the block the code point was assigned to.</returns>
2014-11-20 22:00:07 +01:00
public static string GetBlockName ( int codePoint )
2017-06-25 02:36:38 +02:00
=> FindBlockIndex ( codePoint ) is int i && i >= 0 ?
blocks [ i ]. Name :
DefaultBlock ;
2014-11-08 01:43:17 +01:00
2014-11-24 20:09:59 +01:00
/// <summary>Gets Unicode information on the specified code point.</summary>
/// <remarks>
/// This method will consolidate the data from a few different sources.
/// There are more efficient way of retrieving the data for some properties if only one of those is needed at a time:
/// <list type="bullet">
/// <listheader>
/// <term>Property</term>
/// <description>Method</description>
/// </listheader>
/// <item>
/// <term>Name</term>
/// <description><see cref="GetName(int)"/></description>
/// </item>
/// <item>
/// <term>Category</term>
/// <description><see cref="GetCategory(int)"/></description>
/// </item>
/// <item>
/// <term>Block</term>
/// <description><see cref="GetBlockName(int)"/></description>
/// </item>
/// </list>
/// </remarks>
/// <param name="codePoint">The Unicode code point for which the data must be retrieved.</param>
/// <returns>The name of the code point, if defined; <see langword="null"/> otherwise.</returns>
2014-11-15 13:36:19 +01:00
public static UnicodeCharInfo GetCharInfo ( int codePoint )
2017-06-25 02:36:38 +02:00
=> new UnicodeCharInfo ( codePoint , FindUnicodeCodePointIndex ( codePoint ), FindUnihanCodePointIndex ( codePoint ), GetBlockName ( codePoint ));
2014-11-08 01:43:17 +01:00
2014-11-24 20:09:59 +01:00
/// <summary>Gets the category of the specified code point.</summary>
/// <remarks>
/// The name referred to is the unicode General_Category property.
/// If you only need the category of a character, calling this method is faster than calling <see cref="GetCharInfo(int)"/> and retrieving <see cref="UnicodeCharInfo.Category"/>, because there is less information to lookup.
/// </remarks>
/// <param name="codePoint">The Unicode code point for which the category must be retrieved.</param>
/// <returns>The category of the code point.</returns>
2014-11-15 15:09:12 +01:00
public static UnicodeCategory GetCategory ( int codePoint )
2017-06-25 02:36:38 +02:00
=> FindUnicodeCodePointIndex ( codePoint ) is int unicodeCharacterDataIndex && unicodeCharacterDataIndex >= 0 ?
GetUnicodeCharacterData ( unicodeCharacterDataIndex ). Category :
UnicodeCategory . OtherNotAssigned ;
2014-11-15 15:09:12 +01:00
2014-11-24 20:09:59 +01:00
/// <summary>Gets a display text for the specified code point.</summary>
/// <param name="charInfo">The information for the code point.</param>
/// <returns>A display text for the code point, which may be the representation of the code point itself.</returns>
2017-06-25 20:09:54 +02:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string GetDisplayText ( this UnicodeCharInfo charInfo )
=> GetDisplayText ( charInfo . CodePoint , charInfo . unicodeCharacterDataIndex );
2014-11-15 15:09:12 +01:00
2014-11-24 20:09:59 +01:00
/// <summary>Gets a display text for the specified code point.</summary>
/// <param name="codePoint">The Unicode code point, for which a display text should be returned.</param>
/// <returns>A display text for the code point, which may be the representation of the code point itself.</returns>
2014-11-15 15:09:12 +01:00
public static string GetDisplayText ( int codePoint )
{
2017-09-07 23:10:17 +02:00
if ( codePoint <= 0x0020 ) return (( char )( 0x2400 + codePoint )). ToString (); // Provide a display text for control characters, including space.
2017-06-25 20:09:54 +02:00
else if ( GetCategory ( codePoint ) == UnicodeCategory . NonSpacingMark ) return "\u25CC" + char . ConvertFromUtf32 ( codePoint );
2017-09-07 23:10:17 +02:00
else if ( codePoint >= 0xD800 && codePoint <= 0xDFFF ) return "\xFFFD" ;
2017-06-25 20:09:54 +02:00
else if ( codePoint >= 0xE0020 && codePoint < 0xE007F ) return char . ConvertFromUtf32 ( codePoint - 0xE0000 ); // Handle "TAG" ASCII subset by remapping it to regular ASCII
2017-09-07 23:10:17 +02:00
else return char . ConvertFromUtf32 ( codePoint );
2014-11-16 21:27:57 +01:00
}
2014-11-15 15:09:12 +01:00
2017-06-25 20:09:54 +02:00
private static string GetDisplayText ( int codePoint , int unicodeCharacterDataIndex )
{
if ( codePoint <= 0x0020 ) return (( char )( 0x2400 + codePoint )). ToString (); // Provide a display text for control characters, including space.
else if ( GetUnicodeCharacterData ( unicodeCharacterDataIndex ). Category == UnicodeCategory . NonSpacingMark ) return "\u25CC" + char . ConvertFromUtf32 ( codePoint );
else if ( codePoint >= 0xD800 && codePoint <= 0xDFFF ) return "\xFFFD" ;
else if ( codePoint >= 0xE0020 && codePoint < 0xE007F ) return char . ConvertFromUtf32 ( codePoint - 0xE0000 ); // Handle "TAG" ASCII subset by remapping it to regular ASCII
else return char . ConvertFromUtf32 ( codePoint );
}
2014-11-24 20:09:59 +01:00
/// <summary>Gets the name of the specified code point.</summary>
/// <remarks>
/// The name referred to is the unicode Name property.
/// If you only need the name of a character, calling this method is faster than calling <see cref="GetCharInfo(int)"/> and retrieving <see cref="UnicodeCharInfo.Name"/>, because there is less information to lookup.
/// </remarks>
/// <param name="codePoint">The Unicode code point for which the name must be retrieved.</param>
/// <returns>The name of the code point, if defined; <see langword="null"/> otherwise.</returns>
2014-11-20 21:26:50 +01:00
public static string GetName ( int codePoint )
2017-06-25 02:36:38 +02:00
=> HangulInfo . IsHangul ( codePoint ) ?
HangulInfo . GetHangulName (( char ) codePoint ) :
GetNameInternal ( codePoint );
2014-11-20 21:26:50 +01:00
2017-09-07 23:10:17 +02:00
private static string GetNameInternal ( int codePoint )
2017-06-25 02:36:38 +02:00
=> FindUnicodeCodePointIndex ( codePoint ) is int codePointInfoIndex && codePointInfoIndex >= 0 ?
GetName ( codePoint , ref GetUnicodeCharacterData ( codePointInfoIndex )) :
null ;
2016-01-14 01:31:36 +01:00
2017-06-25 02:36:38 +02:00
internal static string GetName ( int codePoint , ref UnicodeCharacterData characterData )
2014-11-20 21:26:50 +01:00
{
2017-09-07 23:10:17 +02:00
if ( characterData . CodePointRange . IsSingleCodePoint ) return characterData . Name ;
else if ( HangulInfo . IsHangul ( codePoint )) return HangulInfo . GetHangulName (( char ) codePoint );
else if ( characterData . Name != null ) return characterData . Name + "-" + codePoint . ToString ( "X4" );
else return null ;
}
2014-11-20 21:26:50 +01:00
2014-11-24 20:09:59 +01:00
/// <summary>Returns information for a CJK radical.</summary>
/// <param name="radicalIndex">The index of the radical. Must be between 1 and <see cref="CjkRadicalCount"/>.</param>
/// <returns>Information on the specified radical.</returns>
/// <exception cref="IndexOutOfRangeException">The <paramref name="radicalIndex"/> parameter is out of range.</exception>
public static CjkRadicalInfo GetCjkRadicalInfo ( int radicalIndex )
2017-06-25 02:36:38 +02:00
=> new CjkRadicalInfo ( checked (( byte ) radicalIndex ), radicals [ radicalIndex - 1 ]);
2014-11-24 20:09:59 +01:00
/// <summary>Returns the number of CJK radicals in the Unicode data.</summary>
/// <remarks>This value will be 214 for the foreseeable future.</remarks>
2017-06-25 02:36:38 +02:00
public static int CjkRadicalCount => radicals . Length ;
2014-11-24 20:09:59 +01:00
/// <summary>Gets all the blocks defined in the Unicode data.</summary>
/// <remarks><see cref="DefaultBlock"/> is not the name of a block, but only a value indicating the abscence of block information for a given code point.</remarks>
/// <returns>An array containing an entry for every block defined in the Unicode data.</returns>
2017-06-25 02:36:38 +02:00
public static UnicodeBlock [] GetBlocks () => ( UnicodeBlock []) blocks . Clone ();
2014-10-27 21:12:57 +01:00
}
}