diff --git a/UnicodeCharacterInspector.sln b/UnicodeCharacterInspector.sln index 78d373f..e7be7ef 100644 --- a/UnicodeCharacterInspector.sln +++ b/UnicodeCharacterInspector.sln @@ -5,6 +5,10 @@ VisualStudioVersion = 14.0.22129.1 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnicodeCharacterInspector", "UnicodeCharacterInspector\UnicodeCharacterInspector.csproj", "{04E97F21-EF04-441F-83CF-2E71F3AAB089}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnicodeInformation", "UnicodeInformation\UnicodeInformation.csproj", "{CB722958-A1C4-4121-804B-7D5A671491B1}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnicodeInformation.Tests", "UnicodeInformation.Tests\UnicodeInformation.Tests.csproj", "{50337426-E884-4394-9E1A-F6F7A555F5D9}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -15,6 +19,14 @@ Global {04E97F21-EF04-441F-83CF-2E71F3AAB089}.Debug|Any CPU.Build.0 = Debug|Any CPU {04E97F21-EF04-441F-83CF-2E71F3AAB089}.Release|Any CPU.ActiveCfg = Release|Any CPU {04E97F21-EF04-441F-83CF-2E71F3AAB089}.Release|Any CPU.Build.0 = Release|Any CPU + {CB722958-A1C4-4121-804B-7D5A671491B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CB722958-A1C4-4121-804B-7D5A671491B1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CB722958-A1C4-4121-804B-7D5A671491B1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CB722958-A1C4-4121-804B-7D5A671491B1}.Release|Any CPU.Build.0 = Release|Any CPU + {50337426-E884-4394-9E1A-F6F7A555F5D9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50337426-E884-4394-9E1A-F6F7A555F5D9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50337426-E884-4394-9E1A-F6F7A555F5D9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50337426-E884-4394-9E1A-F6F7A555F5D9}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/UnicodeInformation.Tests/Properties/AssemblyInfo.cs b/UnicodeInformation.Tests/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..8735c03 --- /dev/null +++ b/UnicodeInformation.Tests/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("UnicodeInformation.Tests")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("UnicodeInformation.Tests")] +[assembly: AssemblyCopyright("Copyright © 2014")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("50337426-e884-4394-9e1a-f6f7a555f5d9")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/UnicodeInformation.Tests/UnicodeDataManagerTests.cs b/UnicodeInformation.Tests/UnicodeDataManagerTests.cs new file mode 100644 index 0000000..4031145 --- /dev/null +++ b/UnicodeInformation.Tests/UnicodeDataManagerTests.cs @@ -0,0 +1,16 @@ +using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.Threading.Tasks; + +namespace UnicodeInformation.Tests +{ + [TestClass] + public class UnicodeDataManagerTests + { + [TestMethod] + public async Task DownloadAndBuildDataAsync() + { + await UnicodeDataManager.DownloadAndBuildDataAsync(); + } + } +} diff --git a/UnicodeInformation.Tests/UnicodeInformation.Tests.csproj b/UnicodeInformation.Tests/UnicodeInformation.Tests.csproj new file mode 100644 index 0000000..99a8747 --- /dev/null +++ b/UnicodeInformation.Tests/UnicodeInformation.Tests.csproj @@ -0,0 +1,107 @@ + + + + Debug + AnyCPU + {50337426-E884-4394-9E1A-F6F7A555F5D9} + Library + Properties + UnicodeInformation.Tests + UnicodeInformation.Tests + v4.5 + 512 + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 10.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages + False + UnitTest + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + ..\packages\Microsoft.Net.Http.2.2.28\lib\net45\System.Net.Http.Extensions.dll + + + ..\packages\Microsoft.Net.Http.2.2.28\lib\net45\System.Net.Http.Primitives.dll + + + + + + + + + + + + + + + + + + + + + + {cb722958-a1c4-4121-804b-7d5a671491b1} + UnicodeInformation + + + + + + + + + + False + + + False + + + False + + + False + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + \ No newline at end of file diff --git a/UnicodeInformation.Tests/packages.config b/UnicodeInformation.Tests/packages.config new file mode 100644 index 0000000..cba8b3a --- /dev/null +++ b/UnicodeInformation.Tests/packages.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/UnicodeInformation/BidirectionalClass.cs b/UnicodeInformation/BidirectionalClass.cs new file mode 100644 index 0000000..4287dca --- /dev/null +++ b/UnicodeInformation/BidirectionalClass.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace UnicodeInformation +{ + public enum BidirectionalClass : byte + { + [Display(Name = "Left_To_Right", Description = "any strong left-to-right character")] + LeftToRight, + [Display(Name = "Right_To_Left", Description = "any strong right-to-left (non-Arabic-type) character")] + RightToLeft, + [Display(Name = "Arabic_Letter", Description = "any strong right-to-left (Arabic-type) character")] + ArabicLetter, + [Display(Name = "European_Number", Description = "any ASCII digit or Eastern Arabic-Indic digit")] + EuropeanNumber, + [Display(Name = "European_Separator", Description = "plus and minus signs")] + EuropeanSeparator, + [Display(Name = "European_Terminator", Description = "a terminator in a numeric format context, includes currency signs")] + EuropeanTerminator, + [Display(Name = "Arabic_Number", Description = "any Arabic-Indic digit")] + ArabicNumber, + [Display(Name = "Common_Separator", Description = "commas, colons, and slashes")] + CommonSeparator, + [Display(Name = "Nonspacing_Mark", Description = "any nonspacing mark")] + NonspacingMark, + [Display(Name = "Boundary_Neutral", Description = "most format characters, control codes, or noncharacters")] + BoundaryNeutral, + [Display(Name = "Paragraph_Separator", Description = "various newline characters")] + ParagraphSeparator, + [Display(Name = "Segment_Separator", Description = "various segment-related control codes")] + SegmentSeparator, + [Display(Name = "White_Space", Description = "spaces")] + WhiteSpace, + [Display(Name = "Other_Neutral", Description = "most other symbols and punctuation marks")] + OtherNeutral, + [Display(Name = "Left_To_Right_Embedding", Description = "U+202A: the LR embedding control")] + LeftToRightEmbedding, + [Display(Name = "Left_To_Right_Override", Description = "U+202D: the LR override control")] + LeftToRightOverride, + [Display(Name = "Right_To_Left_Embedding", Description = "U+202B: the RL embedding control")] + RightToLeftEmbedding, + [Display(Name = "Right_To_Left_Override", Description = "U+202E: the RL override control")] + RightToLeftOverride, + [Display(Name = "Pop_Directional_Format", Description = "U+202C: terminates an embedding or override control")] + PopDirectionalFormat, + [Display(Name = "Left_To_Right_Isolate", Description = "U+2066: the LR isolate control")] + LeftToRightIsolate, + [Display(Name = "Right_To_Left_Isolate", Description = "U+2067: the RL isolate control")] + RightToLeftIsolate, + [Display(Name = "First_Strong_Isolate", Description = "U+2068: the first strong isolate control")] + FirstStrongIsolate, + [Display(Name = "Pop_Directional_Isolate", Description = "U+2069: terminates an isolate control")] + PopDirectionalIsolate, + } +} diff --git a/UnicodeInformation/CanonicalCombiningClass.cs b/UnicodeInformation/CanonicalCombiningClass.cs new file mode 100644 index 0000000..4d0d8c5 --- /dev/null +++ b/UnicodeInformation/CanonicalCombiningClass.cs @@ -0,0 +1,436 @@ +using System.ComponentModel.DataAnnotations; + +namespace UnicodeInformation +{ + public enum CanonicalCombiningClass : byte + { + [Display(Name = "Not_Reordered", Description = "Spacing and enclosing marks; also many vowel and consonant signs, even if nonspacing")] + NotReordered = 0, + [Display(Name = "Overlay", Description = "Marks which overlay a base letter or symbol")] + Overlay = 1, + [Display(Name = "Nukta", Description = "Diacritic nukta marks in Brahmi-derived scripts")] + Nukta = 7, + [Display(Name = "Kana_Voicing", Description = "Hiragana/Katakana voicing marks")] + KanaVoicing = 8, + [Display(Name = "Virama", Description = "Viramas")] + Virama = 9, + [Display(Name = "Ccc10", Description = "Start of fixed position classes")] + Ccc10 = 10, + [Display(Name = "Ccc11")] + Ccc11 = 11, + [Display(Name = "Ccc12")] + Ccc12 = 12, + [Display(Name = "Ccc13")] + Ccc13 = 13, + [Display(Name = "Ccc14")] + Ccc14 = 14, + [Display(Name = "Ccc15")] + Ccc15 = 15, + [Display(Name = "Ccc16")] + Ccc16 = 16, + [Display(Name = "Ccc17")] + Ccc17 = 17, + [Display(Name = "Ccc18")] + Ccc18 = 18, + [Display(Name = "Ccc19")] + Ccc19 = 19, + [Display(Name = "Ccc20")] + Ccc20 = 20, + [Display(Name = "Ccc21")] + Ccc21 = 21, + [Display(Name = "Ccc22")] + Ccc22 = 22, + [Display(Name = "Ccc23")] + Ccc23 = 23, + [Display(Name = "Ccc24")] + Ccc24 = 24, + [Display(Name = "Ccc25")] + Ccc25 = 25, + [Display(Name = "Ccc26")] + Ccc26 = 26, + [Display(Name = "Ccc27")] + Ccc27 = 27, + [Display(Name = "Ccc28")] + Ccc28 = 28, + [Display(Name = "Ccc29")] + Ccc29 = 29, + [Display(Name = "Ccc30")] + Ccc30 = 30, + [Display(Name = "Ccc31")] + Ccc31 = 31, + [Display(Name = "Ccc32")] + Ccc32 = 32, + [Display(Name = "Ccc33")] + Ccc33 = 33, + [Display(Name = "Ccc34")] + Ccc34 = 34, + [Display(Name = "Ccc35")] + Ccc35 = 35, + [Display(Name = "Ccc36")] + Ccc36 = 36, + [Display(Name = "Ccc37")] + Ccc37 = 37, + [Display(Name = "Ccc38")] + Ccc38 = 38, + [Display(Name = "Ccc39")] + Ccc39 = 39, + [Display(Name = "Ccc40")] + Ccc40 = 40, + [Display(Name = "Ccc41")] + Ccc41 = 41, + [Display(Name = "Ccc42")] + Ccc42 = 42, + [Display(Name = "Ccc43")] + Ccc43 = 43, + [Display(Name = "Ccc44")] + Ccc44 = 44, + [Display(Name = "Ccc45")] + Ccc45 = 45, + [Display(Name = "Ccc46")] + Ccc46 = 46, + [Display(Name = "Ccc47")] + Ccc47 = 47, + [Display(Name = "Ccc48")] + Ccc48 = 48, + [Display(Name = "Ccc49")] + Ccc49 = 49, + [Display(Name = "Ccc50")] + Ccc50 = 50, + [Display(Name = "Ccc51")] + Ccc51 = 51, + [Display(Name = "Ccc52")] + Ccc52 = 52, + [Display(Name = "Ccc53")] + Ccc53 = 53, + [Display(Name = "Ccc54")] + Ccc54 = 54, + [Display(Name = "Ccc55")] + Ccc55 = 55, + [Display(Name = "Ccc56")] + Ccc56 = 56, + [Display(Name = "Ccc57")] + Ccc57 = 57, + [Display(Name = "Ccc58")] + Ccc58 = 58, + [Display(Name = "Ccc59")] + Ccc59 = 59, + [Display(Name = "Ccc60")] + Ccc60 = 60, + [Display(Name = "Ccc61")] + Ccc61 = 61, + [Display(Name = "Ccc62")] + Ccc62 = 62, + [Display(Name = "Ccc63")] + Ccc63 = 63, + [Display(Name = "Ccc64")] + Ccc64 = 64, + [Display(Name = "Ccc65")] + Ccc65 = 65, + [Display(Name = "Ccc66")] + Ccc66 = 66, + [Display(Name = "Ccc67")] + Ccc67 = 67, + [Display(Name = "Ccc68")] + Ccc68 = 68, + [Display(Name = "Ccc69")] + Ccc69 = 69, + [Display(Name = "Ccc70")] + Ccc70 = 70, + [Display(Name = "Ccc71")] + Ccc71 = 71, + [Display(Name = "Ccc72")] + Ccc72 = 72, + [Display(Name = "Ccc73")] + Ccc73 = 73, + [Display(Name = "Ccc74")] + Ccc74 = 74, + [Display(Name = "Ccc75")] + Ccc75 = 75, + [Display(Name = "Ccc76")] + Ccc76 = 76, + [Display(Name = "Ccc77")] + Ccc77 = 77, + [Display(Name = "Ccc78")] + Ccc78 = 78, + [Display(Name = "Ccc79")] + Ccc79 = 79, + [Display(Name = "Ccc80")] + Ccc80 = 80, + [Display(Name = "Ccc81")] + Ccc81 = 81, + [Display(Name = "Ccc82")] + Ccc82 = 82, + [Display(Name = "Ccc83")] + Ccc83 = 83, + [Display(Name = "Ccc84")] + Ccc84 = 84, + [Display(Name = "Ccc85")] + Ccc85 = 85, + [Display(Name = "Ccc86")] + Ccc86 = 86, + [Display(Name = "Ccc87")] + Ccc87 = 87, + [Display(Name = "Ccc88")] + Ccc88 = 88, + [Display(Name = "Ccc89")] + Ccc89 = 89, + [Display(Name = "Ccc90")] + Ccc90 = 90, + [Display(Name = "Ccc91")] + Ccc91 = 91, + [Display(Name = "Ccc92")] + Ccc92 = 92, + [Display(Name = "Ccc93")] + Ccc93 = 93, + [Display(Name = "Ccc94")] + Ccc94 = 94, + [Display(Name = "Ccc95")] + Ccc95 = 95, + [Display(Name = "Ccc96")] + Ccc96 = 96, + [Display(Name = "Ccc97")] + Ccc97 = 97, + [Display(Name = "Ccc98")] + Ccc98 = 98, + [Display(Name = "Ccc99")] + Ccc99 = 99, + [Display(Name = "Ccc100")] + Ccc100 = 100, + [Display(Name = "Ccc101")] + Ccc101 = 101, + [Display(Name = "Ccc102")] + Ccc102 = 102, + [Display(Name = "Ccc103")] + Ccc103 = 103, + [Display(Name = "Ccc104")] + Ccc104 = 104, + [Display(Name = "Ccc105")] + Ccc105 = 105, + [Display(Name = "Ccc106")] + Ccc106 = 106, + [Display(Name = "Ccc107")] + Ccc107 = 107, + [Display(Name = "Ccc108")] + Ccc108 = 108, + [Display(Name = "Ccc109")] + Ccc109 = 109, + [Display(Name = "Ccc110")] + Ccc110 = 110, + [Display(Name = "Ccc111")] + Ccc111 = 111, + [Display(Name = "Ccc112")] + Ccc112 = 112, + [Display(Name = "Ccc113")] + Ccc113 = 113, + [Display(Name = "Ccc114")] + Ccc114 = 114, + [Display(Name = "Ccc115")] + Ccc115 = 115, + [Display(Name = "Ccc116")] + Ccc116 = 116, + [Display(Name = "Ccc117")] + Ccc117 = 117, + [Display(Name = "Ccc118")] + Ccc118 = 118, + [Display(Name = "Ccc119")] + Ccc119 = 119, + [Display(Name = "Ccc120")] + Ccc120 = 120, + [Display(Name = "Ccc121")] + Ccc121 = 121, + [Display(Name = "Ccc122")] + Ccc122 = 122, + [Display(Name = "Ccc123")] + Ccc123 = 123, + [Display(Name = "Ccc124")] + Ccc124 = 124, + [Display(Name = "Ccc125")] + Ccc125 = 125, + [Display(Name = "Ccc126")] + Ccc126 = 126, + [Display(Name = "Ccc127")] + Ccc127 = 127, + [Display(Name = "Ccc128")] + Ccc128 = 128, + [Display(Name = "Ccc129")] + Ccc129 = 129, + [Display(Name = "Ccc130")] + Ccc130 = 130, + [Display(Name = "Ccc131")] + Ccc131 = 131, + [Display(Name = "Ccc132")] + Ccc132 = 132, + [Display(Name = "Ccc133")] + Ccc133 = 133, + [Display(Name = "Ccc134")] + Ccc134 = 134, + [Display(Name = "Ccc135")] + Ccc135 = 135, + [Display(Name = "Ccc136")] + Ccc136 = 136, + [Display(Name = "Ccc137")] + Ccc137 = 137, + [Display(Name = "Ccc138")] + Ccc138 = 138, + [Display(Name = "Ccc139")] + Ccc139 = 139, + [Display(Name = "Ccc140")] + Ccc140 = 140, + [Display(Name = "Ccc141")] + Ccc141 = 141, + [Display(Name = "Ccc142")] + Ccc142 = 142, + [Display(Name = "Ccc143")] + Ccc143 = 143, + [Display(Name = "Ccc144")] + Ccc144 = 144, + [Display(Name = "Ccc145")] + Ccc145 = 145, + [Display(Name = "Ccc146")] + Ccc146 = 146, + [Display(Name = "Ccc147")] + Ccc147 = 147, + [Display(Name = "Ccc148")] + Ccc148 = 148, + [Display(Name = "Ccc149")] + Ccc149 = 149, + [Display(Name = "Ccc150")] + Ccc150 = 150, + [Display(Name = "Ccc151")] + Ccc151 = 151, + [Display(Name = "Ccc152")] + Ccc152 = 152, + [Display(Name = "Ccc153")] + Ccc153 = 153, + [Display(Name = "Ccc154")] + Ccc154 = 154, + [Display(Name = "Ccc155")] + Ccc155 = 155, + [Display(Name = "Ccc156")] + Ccc156 = 156, + [Display(Name = "Ccc157")] + Ccc157 = 157, + [Display(Name = "Ccc158")] + Ccc158 = 158, + [Display(Name = "Ccc159")] + Ccc159 = 159, + [Display(Name = "Ccc160")] + Ccc160 = 160, + [Display(Name = "Ccc161")] + Ccc161 = 161, + [Display(Name = "Ccc162")] + Ccc162 = 162, + [Display(Name = "Ccc163")] + Ccc163 = 163, + [Display(Name = "Ccc164")] + Ccc164 = 164, + [Display(Name = "Ccc165")] + Ccc165 = 165, + [Display(Name = "Ccc166")] + Ccc166 = 166, + [Display(Name = "Ccc167")] + Ccc167 = 167, + [Display(Name = "Ccc168")] + Ccc168 = 168, + [Display(Name = "Ccc169")] + Ccc169 = 169, + [Display(Name = "Ccc170")] + Ccc170 = 170, + [Display(Name = "Ccc171")] + Ccc171 = 171, + [Display(Name = "Ccc172")] + Ccc172 = 172, + [Display(Name = "Ccc173")] + Ccc173 = 173, + [Display(Name = "Ccc174")] + Ccc174 = 174, + [Display(Name = "Ccc175")] + Ccc175 = 175, + [Display(Name = "Ccc176")] + Ccc176 = 176, + [Display(Name = "Ccc177")] + Ccc177 = 177, + [Display(Name = "Ccc178")] + Ccc178 = 178, + [Display(Name = "Ccc179")] + Ccc179 = 179, + [Display(Name = "Ccc180")] + Ccc180 = 180, + [Display(Name = "Ccc181")] + Ccc181 = 181, + [Display(Name = "Ccc182")] + Ccc182 = 182, + [Display(Name = "Ccc183")] + Ccc183 = 183, + [Display(Name = "Ccc184")] + Ccc184 = 184, + [Display(Name = "Ccc185")] + Ccc185 = 185, + [Display(Name = "Ccc186")] + Ccc186 = 186, + [Display(Name = "Ccc187")] + Ccc187 = 187, + [Display(Name = "Ccc188")] + Ccc188 = 188, + [Display(Name = "Ccc189")] + Ccc189 = 189, + [Display(Name = "Ccc190")] + Ccc190 = 190, + [Display(Name = "Ccc191")] + Ccc191 = 191, + [Display(Name = "Ccc192")] + Ccc192 = 192, + [Display(Name = "Ccc193")] + Ccc193 = 193, + [Display(Name = "Ccc194")] + Ccc194 = 194, + [Display(Name = "Ccc195")] + Ccc195 = 195, + [Display(Name = "Ccc196")] + Ccc196 = 196, + [Display(Name = "Ccc197")] + Ccc197 = 197, + [Display(Name = "Ccc198")] + Ccc198 = 198, + [Display(Name = "Ccc199", Description = "End of fixed position classes")] + Ccc199 = 199, + [Display(Name = "Attached_Below_Left", Description = "Marks attached at the bottom left")] + AttachedBelowLeft = 200, + [Display(Name = "Attached_Below", Description = "Marks attached directly below")] + AttachedBelow = 202, + [Display(Name = "Attached_Below_Right", Description = "Marks attached at the bottom right")] + AttachedBelowRight = 204, + [Display(Name = "Attached_Left", Description = "Marks attached to the left")] + AttachedLeft = 208, + [Display(Name = "Attached_Right", Description = "Marks attached to the right")] + AttachedRight = 210, + [Display(Name = "Attached_Above_Left", Description = "Marks attached at the top left")] + AttachedAboveLeft = 212, + [Display(Name = "Attached_Above", Description = "Marks attached directly above")] + AttachedAbove = 214, + [Display(Name = "Attached_Above_Right", Description = "Marks attached at the top right")] + AttachedAboveRight = 216, + [Display(Name = "Below_Left", Description = "Distinct marks at the bottom left")] + BelowLeft = 218, + [Display(Name = "Below", Description = "Distinct marks directly below")] + Below = 220, + [Display(Name = "Below_Right", Description = "Distinct marks at the bottom right")] + BelowRight = 222, + [Display(Name = "Left", Description = "Distinct marks to the left")] + Left = 224, + [Display(Name = "Right", Description = "Distinct marks to the right")] + Right = 226, + [Display(Name = "Above_Left", Description = "Distinct marks at the top left")] + AboveLeft = 228, + [Display(Name = "Above", Description = "Distinct marks directly above")] + Above = 230, + [Display(Name = "Above_Right", Description = "Distinct marks at the top right")] + AboveRight = 232, + [Display(Name = "Double_Below", Description = "Distinct marks subtending two bases")] + DoubleBelow = 233, + [Display(Name = "Double_Above", Description = "Distinct marks extending above two bases")] + DoubleAbove = 234, + [Display(Name = "Iota_Subscript", Description = "Greek iota subscript only")] + IotaSubscript = 240, + } +} diff --git a/UnicodeInformation/Properties/AssemblyInfo.cs b/UnicodeInformation/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..9b03d22 --- /dev/null +++ b/UnicodeInformation/Properties/AssemblyInfo.cs @@ -0,0 +1,30 @@ +using System.Resources; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("UnicodeInformation")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("UnicodeInformation")] +[assembly: AssemblyCopyright("Copyright © 2014")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] +[assembly: NeutralResourcesLanguage("en")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/UnicodeInformation/UnicodeCategoryExtensions.cs b/UnicodeInformation/UnicodeCategoryExtensions.cs new file mode 100644 index 0000000..f5b74c6 --- /dev/null +++ b/UnicodeInformation/UnicodeCategoryExtensions.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace System.Globalization +{ + public static class UnicodeCategoryExtensions + { + public static string GetShortName(this UnicodeCategory category) + { + return UnicodeInformation.UnicodeCategoryInfo.Get(category).ShortName; + } + + public static string GetLongName(this UnicodeCategory category) + { + return UnicodeInformation.UnicodeCategoryInfo.Get(category).LongName; + } + } +} diff --git a/UnicodeInformation/UnicodeCategoryInfo.cs b/UnicodeInformation/UnicodeCategoryInfo.cs new file mode 100644 index 0000000..6e85feb --- /dev/null +++ b/UnicodeInformation/UnicodeCategoryInfo.cs @@ -0,0 +1,134 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace UnicodeInformation +{ + public struct UnicodeCategoryInfo : IEquatable + { + private static readonly UnicodeCategoryInfo[] categories = + { + new UnicodeCategoryInfo(UnicodeCategory.UppercaseLetter, "Lu", "Uppercase_Letter"), + new UnicodeCategoryInfo(UnicodeCategory.LowercaseLetter, "Ll", "Lowercase_Letter"), + new UnicodeCategoryInfo(UnicodeCategory.TitlecaseLetter, "Lt", "Titlecase_Letter"), + new UnicodeCategoryInfo(UnicodeCategory.ModifierLetter, "Lm", "Modifier_Letter"), + new UnicodeCategoryInfo(UnicodeCategory.OtherLetter, "Lo", "Other_Letter"), + new UnicodeCategoryInfo(UnicodeCategory.NonSpacingMark, "Mn", "Nonspacing_Mark"), + new UnicodeCategoryInfo(UnicodeCategory.SpacingCombiningMark, "Mc", "Spacing_Mark"), + new UnicodeCategoryInfo(UnicodeCategory.EnclosingMark, "Me", "Enclosing_Mark"), + new UnicodeCategoryInfo(UnicodeCategory.DecimalDigitNumber, "Nd", "Decimal_Number"), + new UnicodeCategoryInfo(UnicodeCategory.LetterNumber, "Nl", "Letter_Number"), + new UnicodeCategoryInfo(UnicodeCategory.OtherNumber, "No", "Other_Number"), + new UnicodeCategoryInfo(UnicodeCategory.SpaceSeparator, "Zs", "Space_Separator"), + new UnicodeCategoryInfo(UnicodeCategory.LineSeparator, "Zl", "Line_Separator"), + new UnicodeCategoryInfo(UnicodeCategory.ParagraphSeparator, "Zp", "Paragraph_Separator"), + new UnicodeCategoryInfo(UnicodeCategory.Control, "Cc", "Control"), + new UnicodeCategoryInfo(UnicodeCategory.Format, "Cf", "Format"), + new UnicodeCategoryInfo(UnicodeCategory.Surrogate, "Cs", "Surrogate"), + new UnicodeCategoryInfo(UnicodeCategory.PrivateUse, "Co", "Private_Use"), + new UnicodeCategoryInfo(UnicodeCategory.ConnectorPunctuation, "Pc", "Connector_Punctuation"), + new UnicodeCategoryInfo(UnicodeCategory.DashPunctuation, "Pd", "Dash_Punctuation"), + new UnicodeCategoryInfo(UnicodeCategory.OpenPunctuation, "Ps", "Open_Punctuation"), + new UnicodeCategoryInfo(UnicodeCategory.ClosePunctuation, "Pe", "Close_Punctuation"), + new UnicodeCategoryInfo(UnicodeCategory.InitialQuotePunctuation, "Pi", "Initial_Punctuation"), + new UnicodeCategoryInfo(UnicodeCategory.FinalQuotePunctuation, "Pf", "Final_Punctuation"), + new UnicodeCategoryInfo(UnicodeCategory.OtherPunctuation, "Po", "Other_Punctuation"), + new UnicodeCategoryInfo(UnicodeCategory.MathSymbol, "Sm", "Math_Symbol"), + new UnicodeCategoryInfo(UnicodeCategory.CurrencySymbol, "Sc", "Currency_Symbol"), + new UnicodeCategoryInfo(UnicodeCategory.ModifierSymbol, "Sk", "Modifier_Symbol"), + new UnicodeCategoryInfo(UnicodeCategory.OtherSymbol, "So", "Other_Symbol"), + new UnicodeCategoryInfo(UnicodeCategory.OtherNotAssigned, "Cn", "Unassigned"), + }; + + private static readonly Dictionary unicodeShortNameToCategoryDictionary = BuildShortNameDictionary(); + private static readonly Dictionary unicodeLongNameToCategoryDictionary = BuildLongNameDictionary(); + + private static Dictionary BuildShortNameDictionary() + { + var dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); + + foreach (var info in categories) + { + dictionary.Add(info.ShortName, info.Category); + } + + return dictionary; + } + + private static Dictionary BuildLongNameDictionary() + { + var dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); + + foreach (var info in categories) + { + dictionary.Add(info.LongName, info.Category); + } + + return dictionary; + } + + private static UnicodeCategory GetCategoryFromShortName(string name) + { + return unicodeShortNameToCategoryDictionary[name]; + } + + private static UnicodeCategory GetCategoryFromLongName(string name) + { + return unicodeLongNameToCategoryDictionary[name]; + } + + public static UnicodeCategoryInfo Get(UnicodeCategory category) + { + return categories[(int)category]; + } + + public static UnicodeCategoryInfo FromShortName(string name) + { + return Get(GetCategoryFromShortName(name)); + } + + public static UnicodeCategoryInfo FromLongName(string name) + { + return Get(GetCategoryFromLongName(name)); + } + + public readonly UnicodeCategory Category; + public readonly string ShortName; + public readonly string LongName; + + private UnicodeCategoryInfo(UnicodeCategory category, string shortName, string longName) + { + this.Category = category; + this.ShortName = shortName; + this.LongName = longName; + } + + public override string ToString() + { + return Category.ToString(); + } + + public override bool Equals(object obj) + { + return obj is UnicodeCategoryInfo && Equals((UnicodeCategoryInfo)obj); + } + + public bool Equals(UnicodeCategoryInfo other) + { + return other.Category == Category && (other.Category != 0 || other.ShortName != null); + } + + public override int GetHashCode() + { + return (int)Category; + } + + public static implicit operator UnicodeCategory(UnicodeCategoryInfo info) + { + return info.Category; + } + } +} diff --git a/UnicodeInformation/UnicodeCharacterData.cs b/UnicodeInformation/UnicodeCharacterData.cs new file mode 100644 index 0000000..5e63874 --- /dev/null +++ b/UnicodeInformation/UnicodeCharacterData.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace UnicodeInformation +{ + public sealed class UnicodeCharacterData + { + public readonly int CodePoint; + public readonly string Name; + public readonly UnicodeCategory Category; + public readonly CanonicalCombiningClass CanonicalCombiningClass; + public readonly string BidirectionalClass; + public readonly string DecompositionType; + public readonly UnicodeNumericType NumericType; + public readonly UnicodeRationalNumber NumericValue; + public readonly bool BidirectionalMirrored; + public readonly string OldName; + public readonly string SimpleUpperCaseMapping; + public readonly string SimpleLowerCaseMapping; + public readonly string SimpleTitleCaseMapping; + + public readonly int[] RelatedCodePoints; + + public UnicodeCharacterData + ( + int codePoint, + string name, + UnicodeCategory category, + CanonicalCombiningClass canonicalCombiningClass, + string bidirectionalClass, + string decompositionType, + UnicodeNumericType numericType, + UnicodeRationalNumber numericValue, + bool bidirectionalMirrored, + string oldName, + string simpleUpperCaseMapping, + string simpleLowerCaseMapping, + string simpleTitleCaseMapping, + int[] relatedCodePoints + ) + { + this.CodePoint = codePoint; + this.Name = name; + this.Category = category; + this.CanonicalCombiningClass = canonicalCombiningClass; + this.BidirectionalClass = bidirectionalClass; + this.DecompositionType = decompositionType; + this.NumericType = numericType; + this.NumericValue = numericValue; + this.BidirectionalMirrored = bidirectionalMirrored; + this.OldName = oldName; + this.SimpleUpperCaseMapping = simpleUpperCaseMapping; + this.SimpleLowerCaseMapping = simpleLowerCaseMapping; + this.SimpleTitleCaseMapping = simpleTitleCaseMapping; + this.RelatedCodePoints = relatedCodePoints; + } + } +} diff --git a/UnicodeInformation/UnicodeCharacterDataBuilder.cs b/UnicodeInformation/UnicodeCharacterDataBuilder.cs new file mode 100644 index 0000000..2389123 --- /dev/null +++ b/UnicodeInformation/UnicodeCharacterDataBuilder.cs @@ -0,0 +1,137 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace UnicodeInformation +{ + public sealed class UnicodeCharacterDataBuilder + { + private readonly int codePoint; + private string name; + private UnicodeCategory category = UnicodeCategory.OtherNotAssigned; + private CanonicalCombiningClass canonicalCombiningClass; + private string bidirectionalClass; + private string decompositionType; + private UnicodeNumericType numericType; + private UnicodeRationalNumber numericValue; + private bool bidirectionalMirrored; + private string oldName; + private string simpleUpperCaseMapping; + private string simpleLowerCaseMapping; + private string simpleTitleCaseMapping; + + private List relatedCodePoints = new List(); + + public int CodePoint { get { return codePoint; } } + + public string Name + { + get { return name; } + set { name = value; } + } + + public UnicodeCategory Category + { + get { return category; } + set + { + if (!Enum.IsDefined(typeof(UnicodeCategory), value)) + { + throw new ArgumentOutOfRangeException("value"); + } + category = value; + } + } + + public CanonicalCombiningClass CanonicalCombiningClass + { + get { return canonicalCombiningClass; } + set { canonicalCombiningClass = value; } // Even values not defined in the enum are allowed here. + } + + public string BidirectionalClass + { + get { return bidirectionalClass; } + set { bidirectionalClass = value; } + } + + public string DecompositionType + { + get { return decompositionType; } + set { decompositionType = value; } + } + + public UnicodeNumericType NumericType + { + get { return numericType; } + set { numericType = value; } + } + + public UnicodeRationalNumber NumericValue + { + get { return numericValue; } + set { numericValue = value; } + } + + public string OldName + { + get { return oldName; } + set { oldName = value; } + } + + public bool BidirectionalMirrored + { + get { return bidirectionalMirrored; } + set { bidirectionalMirrored = value; } + } + + public string SimpleUpperCaseMapping + { + get { return simpleUpperCaseMapping; } + set { simpleUpperCaseMapping = value; } + } + + public string SimpleLowerCaseMapping + { + get { return simpleLowerCaseMapping; } + set { simpleLowerCaseMapping = value; } + } + + public string SimpleTitleCaseMapping + { + get { return simpleTitleCaseMapping; } + set { simpleTitleCaseMapping = value; } + } + + public ICollection RelatedCodePoints { get { return relatedCodePoints; } } + + public UnicodeCharacterDataBuilder(int codePoint) + { + this.codePoint = codePoint; + } + + public UnicodeCharacterData ToCharacterData() + { + return new UnicodeCharacterData + ( + codePoint, + Name, + category, + canonicalCombiningClass, + bidirectionalClass, + decompositionType, + numericType, + numericValue, + bidirectionalMirrored, + oldName, + simpleUpperCaseMapping, + simpleLowerCaseMapping, + simpleTitleCaseMapping, + relatedCodePoints.Count > 0 ? relatedCodePoints.ToArray() : null + ); + } + } +} diff --git a/UnicodeInformation/UnicodeData.cs b/UnicodeInformation/UnicodeData.cs new file mode 100644 index 0000000..50e9cc3 --- /dev/null +++ b/UnicodeInformation/UnicodeData.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace UnicodeInformation +{ + public sealed class UnicodeData + { + private readonly Version unicodeVersion; + private readonly UnicodeCharacterData[] characterData; + + public static async Task FromStreamAsync(Stream stream) + { + var buffer = new byte[4096]; + + if (await stream.ReadAsync(buffer, 0, 6).ConfigureAwait(false) != 6) + throw new EndOfStreamException(); + + if (buffer[0] != (byte)'U' + || buffer[1] != (byte)'C' + || buffer[2] != (byte)'D') + throw new InvalidDataException(); + + int formatVersion = buffer[3]; + + if (formatVersion != 1) throw new InvalidDataException(); + + var unicodeVersion = new Version(buffer[4], buffer[6]); + + return new UnicodeData(unicodeVersion, null); + } + + internal UnicodeData(Version unicodeVersion, UnicodeCharacterData[] characterData) + { + this.unicodeVersion = unicodeVersion; + this.characterData = characterData; + } + + public Version UnicodeVersion { get { return unicodeVersion; } } + + public UnicodeCharacterData GetUnicodeData(int codePoint) + { + return default(UnicodeCharacterData); + } + + private void LoadData(Stream stream) + { + } + } +} diff --git a/UnicodeInformation/UnicodeDataManager.cs b/UnicodeInformation/UnicodeDataManager.cs new file mode 100644 index 0000000..a443aaa --- /dev/null +++ b/UnicodeInformation/UnicodeDataManager.cs @@ -0,0 +1,98 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; + +namespace UnicodeInformation +{ + public class UnicodeDataManager + { + public static readonly Uri UnicodeCharacterDataUri = new Uri("http://www.unicode.org/Public/UCD/latest/ucd/", UriKind.Absolute); + public const string UnicodeDataFileName = "UnicodeData.txt"; + public const string PropListFileName = "PropList.txt"; + + public static async Task DownloadAndBuildDataAsync() + { + var characterDataBuilders = new List(); + + using (var httpClient = new HttpClient()) + { + using (var reader = new StreamReader(await httpClient.GetStreamAsync(UnicodeCharacterDataUri + UnicodeDataFileName).ConfigureAwait(false), Encoding.UTF8, false)) + { + string line; + + while (!string.IsNullOrEmpty((line = await reader.ReadLineAsync().ConfigureAwait(false)))) + { + var fields = line.Split(';'); + + if (fields.Length < 15) throw new InvalidDataException(); + + // NB: Fields 10 and 11 are deemed obsolete. Field 11 should always be empty, and will be ignored here. + var characterData = new UnicodeCharacterDataBuilder(int.Parse(fields[0], NumberStyles.HexNumber)) + { + Name = fields[1], + Category = UnicodeCategoryInfo.FromShortName(fields[2]).Category, + CanonicalCombiningClass = (CanonicalCombiningClass)byte.Parse(fields[3]), + BidirectionalClass = fields[4], + DecompositionType = fields[5], + BidirectionalMirrored = fields[9] == "Y", + OldName = fields[10], + SimpleUpperCaseMapping = fields[12], + SimpleLowerCaseMapping = fields[13], + SimpleTitleCaseMapping = fields[14] + }; + + // Handle Numeric_Type & Numeric_Value: + // If field 6 is set, fields 7 and 8 should have the same value, and Numeric_Type is Decimal. + // If field 6 is not set but field 7 is set, field 8 should be set and have the same value. Then, the type is Digit. + // If field 6 and 7 are not set, but field 8 is set, then Numeric_Type is Numeric. + if (!string.IsNullOrEmpty(fields[8])) + { + characterData.NumericValue = UnicodeRationalNumber.Parse(fields[8]); + + if (!string.IsNullOrEmpty(fields[7])) + { + if (fields[7] != fields[8]) + { + throw new InvalidDataException("Invalid value for field 7 of character U+" + characterData.CodePoint.ToString("X4") + "."); + } + + if (!string.IsNullOrEmpty(fields[6])) + { + if (fields[6] != fields[7]) + { + throw new InvalidDataException("Invalid value for field 6 of character U+" + characterData.CodePoint.ToString("X4") + "."); + } + } + else + { + characterData.NumericType = UnicodeNumericType.Digit; + } + } + else + { + characterData.NumericType = UnicodeNumericType.Numeric; + } + } + + characterDataBuilders.Add(characterData); + } + } + + //using (var reader = new StreamReader(await httpClient.GetStreamAsync(UnicodeCharacterDataUri + PropListFileName))) + //{ + //} + } + + var finalData = new UnicodeCharacterData[characterDataBuilders.Count]; + + for (int i = 0; i < finalData.Length; ++i) + finalData[i] = characterDataBuilders[i].ToCharacterData(); + + return new UnicodeData(new Version(7, 0), finalData); + } + } +} diff --git a/UnicodeInformation/UnicodeInformation.csproj b/UnicodeInformation/UnicodeInformation.csproj new file mode 100644 index 0000000..25075d6 --- /dev/null +++ b/UnicodeInformation/UnicodeInformation.csproj @@ -0,0 +1,82 @@ + + + + + 11.0 + Debug + AnyCPU + {CB722958-A1C4-4121-804B-7D5A671491B1} + Library + Properties + UnicodeInformation + UnicodeInformation + en-US + 512 + {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + Profile7 + v4.5 + <_WindowsKitBinPath>C:\Program Files (x86)\Windows Kits\8.1\bin\x86 + <_WindowsPhoneKitBinPath>C:\Program Files (x86)\Windows Phone Kits\8.1\bin + $(_WindowsKitBinPath)\makepri.exe + $(_WindowsKitBinPath)\makeappx.exe + $(_WindowsKitBinPath)\signtool.exe + $(_WindowsPhoneKitBinPath)\x86\MrmEnvironmentExtDl.dll + $(_WindowsPhoneKitBinPath)\x64\MrmEnvironmentExtDl.dll + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + ..\packages\Microsoft.Net.Http.2.2.28\lib\portable-net45+win8\System.Net.Http.Extensions.dll + + + ..\packages\Microsoft.Net.Http.2.2.28\lib\portable-net45+win8\System.Net.Http.Primitives.dll + + + + + + + + + + \ No newline at end of file diff --git a/UnicodeInformation/UnicodeNumericType.cs b/UnicodeInformation/UnicodeNumericType.cs new file mode 100644 index 0000000..1abd7f7 --- /dev/null +++ b/UnicodeInformation/UnicodeNumericType.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace UnicodeInformation +{ + public enum UnicodeNumericType : byte + { + None = 0, + Decimal = 1, + Digit = 2, + Numeric = 3 + } +} diff --git a/UnicodeInformation/UnicodeRationalNumber.cs b/UnicodeInformation/UnicodeRationalNumber.cs new file mode 100644 index 0000000..8c4197c --- /dev/null +++ b/UnicodeInformation/UnicodeRationalNumber.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace UnicodeInformation +{ + public struct UnicodeRationalNumber : IEquatable + { + public static UnicodeRationalNumber Parse(string s) + { + if (s == null) throw new ArgumentNullException("s"); + if (s.Length == 0) throw new ArgumentException(); + + int fractionBarIndex = s.IndexOf('/'); + + try + { + return new UnicodeRationalNumber(long.Parse(fractionBarIndex >= 0 ? s.Substring(0, fractionBarIndex) : s), fractionBarIndex >= 0 ? byte.Parse(s.Substring(fractionBarIndex + 1)) : (byte)1); + } + catch (Exception ex) + { + throw; + } + } + + public readonly long Numerator; + public readonly byte Denominator; + + public UnicodeRationalNumber(long number) + { + this.Numerator = number; + this.Denominator = 1; + } + + public UnicodeRationalNumber(long numerator, byte denominator) + { + this.Numerator = numerator; + this.Denominator = denominator; + } + + public bool IsDefaultValue { get { return Numerator == 0 && Denominator == 0; } } + + public override string ToString() + { + return !IsDefaultValue ? Numerator.ToString() + "/" + Denominator.ToString() : string.Empty; + } + + public bool Equals(UnicodeRationalNumber other) + { + // We don't consider 1/2 and 2/4 equal here, as, that wouldn't be the same character. + return other.Numerator == Numerator && other.Denominator == Denominator; + } + + public override bool Equals(object obj) + { + return base.Equals(obj); + } + + public override int GetHashCode() + { + return (int)(Numerator << 8) | (Denominator) ^ (byte)(Numerator >> 56); + } + } +} diff --git a/UnicodeInformation/app.config b/UnicodeInformation/app.config new file mode 100644 index 0000000..108b695 --- /dev/null +++ b/UnicodeInformation/app.config @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/UnicodeInformation/packages.config b/UnicodeInformation/packages.config new file mode 100644 index 0000000..eeb8ea0 --- /dev/null +++ b/UnicodeInformation/packages.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file