public GameObject MakePrefab(Entity entity) { GameObject root = new GameObject(entity.Name); var mapBuilder = new CharacterMapBuilder(); charMap = mapBuilder.BuildMap(entity, root); foreach (var animation in entity.Animations) { MakePrefab(animation, root); } return root; }
public GameObject MakePrefab(Entity entity, GameObject root, string spriteFolder) { //Set the name (in case it changed) root.name = entity.Name; //Build the character map first var mapBuilder = new CharacterMapBuilder(); charMap = mapBuilder.BuildMap(entity, root, spriteFolder); //Build the GameObject hierarchy foreach (var animation in entity.Animations) { MakePrefab(animation, root); } return root; }
/// <summary> /// Draw the UI for this tool. /// </summary> void OnGUI () { Object fnt = (Object)NGUISettings.FMFont ?? (Object)NGUISettings.BMFont; UIFont uiFont = (fnt as UIFont); NGUIEditorTools.SetLabelWidth(80f); GUILayout.Space(3f); NGUIEditorTools.DrawHeader("Input", true); NGUIEditorTools.BeginContents(false); GUILayout.BeginHorizontal(); mType = (FontType)EditorGUILayout.EnumPopup("Type", mType, GUILayout.MinWidth(200f)); NGUIEditorTools.DrawPadding(); GUILayout.EndHorizontal(); Create create = Create.None; if (mType == FontType.ImportedBitmap) { NGUISettings.fontData = EditorGUILayout.ObjectField("Font Data", NGUISettings.fontData, typeof(TextAsset), false) as TextAsset; NGUISettings.fontTexture = EditorGUILayout.ObjectField("Texture", NGUISettings.fontTexture, typeof(Texture2D), false, GUILayout.Width(140f)) as Texture2D; NGUIEditorTools.EndContents(); // Draw the atlas selection only if we have the font data and texture specified, just to make it easier EditorGUI.BeginDisabledGroup(NGUISettings.fontData == null || NGUISettings.fontTexture == null); { NGUIEditorTools.DrawHeader("Output", true); NGUIEditorTools.BeginContents(false); ComponentSelector.Draw<UIAtlas>(NGUISettings.atlas, OnSelectAtlas, false); NGUIEditorTools.EndContents(); } EditorGUI.EndDisabledGroup(); if (NGUISettings.fontData == null) { EditorGUILayout.HelpBox("To create a font from a previously exported FNT file, you need to use BMFont on " + "Windows or your choice of Glyph Designer or the less expensive bmGlyph on the Mac.\n\n" + "Either of these tools will create a FNT file for you that you will drag & drop into the field above.", MessageType.Info); } else if (NGUISettings.fontTexture == null) { EditorGUILayout.HelpBox("When exporting your font, you should get two files: the FNT, and the texture. Only one texture can be used per font.", MessageType.Info); } else if (NGUISettings.atlas == null) { EditorGUILayout.HelpBox("You can create a font that doesn't use a texture atlas. This will mean that the text " + "labels using this font will generate an extra draw call.\n\nIf you do specify an atlas, the font's texture will be added to it automatically.", MessageType.Info); } EditorGUI.BeginDisabledGroup(NGUISettings.fontData == null || NGUISettings.fontTexture == null); { GUILayout.BeginHorizontal(); GUILayout.Space(20f); if (GUILayout.Button("Create the Font")) create = Create.Import; GUILayout.Space(20f); GUILayout.EndHorizontal(); } EditorGUI.EndDisabledGroup(); } else { GUILayout.BeginHorizontal(); if (NGUIEditorTools.DrawPrefixButton("Source")) ComponentSelector.Show<Font>(OnUnityFont, new string[] { ".ttf", ".otf" }); Font ttf = EditorGUILayout.ObjectField(NGUISettings.FMFont, typeof(Font), false) as Font; GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); { NGUISettings.FMSize = EditorGUILayout.IntField("Size", NGUISettings.FMSize, GUILayout.Width(120f)); if (mType == FontType.Dynamic) { NGUISettings.fontStyle = (FontStyle)EditorGUILayout.EnumPopup(NGUISettings.fontStyle); NGUIEditorTools.DrawPadding(); } } GUILayout.EndHorizontal(); // Choose the font style if there are multiple faces present if (mType == FontType.GeneratedBitmap) { if (!FreeType.isPresent) { #if UNITY_4_3 || UNITY_4_5 || UNITY_4_6 string filename = (Application.platform == RuntimePlatform.WindowsEditor) ? "FreeType.dll" : "FreeType.dylib"; #else string filename = (Application.platform == RuntimePlatform.WindowsEditor) ? "FreeType64.dll" : "FreeType64.dylib"; #endif EditorGUILayout.HelpBox("Assets/NGUI/Editor/" + filename + " is missing", MessageType.Error); GUILayout.BeginHorizontal(); GUILayout.Space(20f); if (GUILayout.Button("Find " + filename)) { string path = EditorUtility.OpenFilePanel("Find " + filename, NGUISettings.currentPath, (Application.platform == RuntimePlatform.WindowsEditor) ? "dll" : "dylib"); if (!string.IsNullOrEmpty(path)) { if (System.IO.Path.GetFileName(path) == filename) { NGUISettings.currentPath = System.IO.Path.GetDirectoryName(path); NGUISettings.pathToFreeType = path; } else Debug.LogError("The library must be named '" + filename + "'"); } } GUILayout.Space(20f); GUILayout.EndHorizontal(); } else if (ttf != null) { string[] faces = FreeType.GetFaces(ttf); if (faces != null) { if (mFaceIndex >= faces.Length) mFaceIndex = 0; if (faces.Length > 1) { GUILayout.Label("Style", EditorStyles.boldLabel); for (int i = 0; i < faces.Length; ++i) { GUILayout.BeginHorizontal(); GUILayout.Space(10f); if (DrawOption(i == mFaceIndex, " " + faces[i])) mFaceIndex = i; GUILayout.EndHorizontal(); } } } NGUISettings.fontKerning = EditorGUILayout.Toggle("Kerning", NGUISettings.fontKerning); GUILayout.Label("Characters", EditorStyles.boldLabel); CharacterMap cm = characterMap; GUILayout.BeginHorizontal(GUILayout.Width(100f)); GUILayout.BeginVertical(); GUI.changed = false; if (DrawOption(cm == CharacterMap.Numeric, " Numeric")) cm = CharacterMap.Numeric; if (DrawOption(cm == CharacterMap.Ascii, " ASCII")) cm = CharacterMap.Ascii; if (DrawOption(cm == CharacterMap.Latin, " Latin")) cm = CharacterMap.Latin; if (DrawOption(cm == CharacterMap.Custom, " Custom")) cm = CharacterMap.Custom; if (GUI.changed) characterMap = cm; GUILayout.EndVertical(); EditorGUI.BeginDisabledGroup(cm != CharacterMap.Custom); { if (cm != CharacterMap.Custom) { string chars = ""; if (cm == CharacterMap.Ascii) { for (int i = 33; i < 127; ++i) chars += System.Convert.ToChar(i); } else if (cm == CharacterMap.Numeric) { chars = "0123456789"; } else if (cm == CharacterMap.Latin) { for (int i = 33; i < 127; ++i) chars += System.Convert.ToChar(i); for (int i = 161; i < 256; ++i) chars += System.Convert.ToChar(i); } NGUISettings.charsToInclude = chars; } GUI.changed = false; string text = NGUISettings.charsToInclude; if (cm == CharacterMap.Custom) { text = EditorGUILayout.TextArea(text, GUI.skin.textArea, GUILayout.Height(80f), GUILayout.Width(Screen.width - 100f)); } else { GUILayout.Label(text, GUI.skin.textArea, GUILayout.Height(80f), GUILayout.Width(Screen.width - 100f)); } if (GUI.changed) { string final = ""; for (int i = 0; i < text.Length; ++i) { char c = text[i]; if (c < 33) continue; string s = c.ToString(); if (!final.Contains(s)) final += s; } if (final.Length > 0) { char[] chars = final.ToCharArray(); System.Array.Sort(chars); final = new string(chars); } else final = ""; NGUISettings.charsToInclude = final; } } EditorGUI.EndDisabledGroup(); GUILayout.EndHorizontal(); } } NGUIEditorTools.EndContents(); if (mType == FontType.Dynamic) { EditorGUI.BeginDisabledGroup(ttf == null); GUILayout.BeginHorizontal(); GUILayout.Space(20f); if (GUILayout.Button("Create the Font")) create = Create.Dynamic; GUILayout.Space(20f); GUILayout.EndHorizontal(); EditorGUI.EndDisabledGroup(); #if UNITY_3_5 EditorGUILayout.HelpBox("Dynamic fonts require Unity 4.0 or higher.", MessageType.Error); #else // Helpful info if (ttf == null) { EditorGUILayout.HelpBox("You don't have to create a UIFont to use dynamic fonts. You can just reference the Unity Font directly on the label.", MessageType.Info); } EditorGUILayout.HelpBox("Please note that dynamic fonts can't be made a part of an atlas, and using dynamic fonts will result in at least one extra draw call.", MessageType.Warning); #endif } else { bool isBuiltIn = (ttf != null) && string.IsNullOrEmpty(UnityEditor.AssetDatabase.GetAssetPath(ttf)); // Draw the atlas selection only if we have the font data and texture specified, just to make it easier EditorGUI.BeginDisabledGroup(ttf == null || isBuiltIn || !FreeType.isPresent); { NGUIEditorTools.DrawHeader("Output", true); NGUIEditorTools.BeginContents(false); ComponentSelector.Draw<UIAtlas>(NGUISettings.atlas, OnSelectAtlas, false); NGUIEditorTools.EndContents(); if (ttf == null) { EditorGUILayout.HelpBox("You can create a bitmap font by specifying a dynamic font to use as the source.", MessageType.Info); } else if (isBuiltIn) { EditorGUILayout.HelpBox("You chose an embedded font. You can't create a bitmap font from an embedded resource.", MessageType.Warning); } else if (NGUISettings.atlas == null) { EditorGUILayout.HelpBox("You can create a font that doesn't use a texture atlas. This will mean that the text " + "labels using this font will generate an extra draw call.\n\nIf you do specify an atlas, the font's texture will be added to it automatically.", MessageType.Info); } GUILayout.BeginHorizontal(); GUILayout.Space(20f); if (GUILayout.Button("Create the Font")) create = Create.Bitmap; GUILayout.Space(20f); GUILayout.EndHorizontal(); } EditorGUI.EndDisabledGroup(); } } if (create == Create.None) return; // Open the "Save As" file dialog #if UNITY_3_5 string prefabPath = EditorUtility.SaveFilePanel("Save As", NGUISettings.currentPath, "New Font.prefab", "prefab"); #else string prefabPath = EditorUtility.SaveFilePanelInProject("Save As", "New Font.prefab", "prefab", "Save font as...", NGUISettings.currentPath); #endif if (string.IsNullOrEmpty(prefabPath)) return; NGUISettings.currentPath = System.IO.Path.GetDirectoryName(prefabPath); // Load the font's prefab GameObject go = AssetDatabase.LoadAssetAtPath(prefabPath, typeof(GameObject)) as GameObject; Object prefab = null; string fontName; // Font doesn't exist yet if (go == null || go.GetComponent<UIFont>() == null) { // Create a new prefab for the atlas prefab = PrefabUtility.CreateEmptyPrefab(prefabPath); fontName = prefabPath.Replace(".prefab", ""); fontName = fontName.Substring(prefabPath.LastIndexOfAny(new char[] { '/', '\\' }) + 1); // Create a new game object for the font go = new GameObject(fontName); uiFont = go.AddComponent<UIFont>(); } else { uiFont = go.GetComponent<UIFont>(); fontName = go.name; } if (create == Create.Dynamic) { uiFont.atlas = null; uiFont.dynamicFont = NGUISettings.FMFont; uiFont.dynamicFontStyle = NGUISettings.fontStyle; uiFont.defaultSize = NGUISettings.FMSize; } else if (create == Create.Import) { Material mat = null; if (NGUISettings.atlas != null) { // Add the font's texture to the atlas UIAtlasMaker.AddOrUpdate(NGUISettings.atlas, NGUISettings.fontTexture); } else { // Create a material for the font string matPath = prefabPath.Replace(".prefab", ".mat"); mat = AssetDatabase.LoadAssetAtPath(matPath, typeof(Material)) as Material; // If the material doesn't exist, create it if (mat == null) { Shader shader = Shader.Find("Unlit/Transparent Colored"); mat = new Material(shader); // Save the material AssetDatabase.CreateAsset(mat, matPath); AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport); // Load the material so it's usable mat = AssetDatabase.LoadAssetAtPath(matPath, typeof(Material)) as Material; } mat.mainTexture = NGUISettings.fontTexture; } uiFont.dynamicFont = null; BMFontReader.Load(uiFont.bmFont, NGUITools.GetHierarchy(uiFont.gameObject), NGUISettings.fontData.bytes); if (NGUISettings.atlas == null) { uiFont.atlas = null; uiFont.material = mat; } else { uiFont.spriteName = NGUISettings.fontTexture.name; uiFont.atlas = NGUISettings.atlas; } NGUISettings.FMSize = uiFont.defaultSize; } else if (create == Create.Bitmap) { // Create the bitmap font BMFont bmFont; Texture2D tex; if (FreeType.CreateFont( NGUISettings.FMFont, NGUISettings.FMSize, mFaceIndex, NGUISettings.fontKerning, NGUISettings.charsToInclude, 1, out bmFont, out tex)) { uiFont.bmFont = bmFont; tex.name = fontName; if (NGUISettings.atlas != null) { // Add this texture to the atlas and destroy it UIAtlasMaker.AddOrUpdate(NGUISettings.atlas, tex); NGUITools.DestroyImmediate(tex); NGUISettings.fontTexture = null; tex = null; uiFont.atlas = NGUISettings.atlas; uiFont.spriteName = fontName; } else { string texPath = prefabPath.Replace(".prefab", ".png"); string matPath = prefabPath.Replace(".prefab", ".mat"); byte[] png = tex.EncodeToPNG(); FileStream fs = File.OpenWrite(texPath); fs.Write(png, 0, png.Length); fs.Close(); // See if the material already exists Material mat = AssetDatabase.LoadAssetAtPath(matPath, typeof(Material)) as Material; // If the material doesn't exist, create it if (mat == null) { Shader shader = Shader.Find("Unlit/Transparent Colored"); mat = new Material(shader); // Save the material AssetDatabase.CreateAsset(mat, matPath); AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport); // Load the material so it's usable mat = AssetDatabase.LoadAssetAtPath(matPath, typeof(Material)) as Material; } else AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport); // Re-load the texture tex = AssetDatabase.LoadAssetAtPath(texPath, typeof(Texture2D)) as Texture2D; // Assign the texture mat.mainTexture = tex; NGUISettings.fontTexture = tex; uiFont.atlas = null; uiFont.material = mat; } } else return; } if (prefab != null) { // Update the prefab PrefabUtility.ReplacePrefab(go, prefab); DestroyImmediate(go); AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport); // Select the atlas go = AssetDatabase.LoadAssetAtPath(prefabPath, typeof(GameObject)) as GameObject; uiFont = go.GetComponent<UIFont>(); } if (uiFont != null) { NGUISettings.FMFont = null; NGUISettings.BMFont = uiFont; } MarkAsChanged(); Selection.activeGameObject = go; }
/// <summary> /// Computes the typography availabilities. /// It checks the presence of a set of required features in the font /// for ranges of unicode code points and set the corresponding bits /// in the TypographyAvailabilities enum. TypographyAvailabilities enum is /// used to determind whether fast path can be used to format the input. /// </summary> private void ComputeTypographyAvailabilities() { int glyphBitsLength = (GlyphCount + 31) >> 5; uint[] glyphBits = BufferCache.GetUInts(glyphBitsLength); Array.Clear(glyphBits, 0, glyphBitsLength); ushort minGlyphId = 65535; ushort maxGlyphId = 0; WritingSystem[] complexScripts; TypographyAvailabilities typography = TypographyAvailabilities.None; GsubGposTables GsubGpos = new GsubGposTables(this); // preparing the glyph bits. When the bit is set, it means the corresponding // glyph needs to be checked against. for (int i = 0; i < fastTextRanges.Length; i++) { uint[] codepoints = fastTextRanges[i].GetFullRange(); ushort[] glyphIndices = BufferCache.GetUShorts(codepoints.Length); unsafe { fixed(uint *pCodepoints = &codepoints[0]) { fixed(ushort *pGlyphIndices = &glyphIndices[0]) { CharacterMap.TryGetValues(pCodepoints, checked ((uint)codepoints.Length), pGlyphIndices); } } } for (int j = 0; j < codepoints.Length; j++) { ushort glyphId = glyphIndices[j]; if (glyphId != 0) { glyphBits[glyphId >> 5] |= (uint)(1 << (glyphId % 32)); if (glyphId > maxGlyphId) { maxGlyphId = glyphId; } if (glyphId < minGlyphId) { minGlyphId = glyphId; } } } BufferCache.ReleaseUShorts(glyphIndices); } // // Step 1: call OpenType layout engine to test presence of // 'locl' feature. Based on the returned writing systems, set // FastTextMajorLanguageLocalizedFormAvailable bit and // FastTextExtraLanguageLocalizedFormAvailable bit // OpenTypeLayoutResult result; unsafe { result = OpenTypeLayout.GetComplexLanguageList( GsubGpos, LoclFeature, glyphBits, minGlyphId, maxGlyphId, out complexScripts ); } if (result != OpenTypeLayoutResult.Success) { // The check failed. We abort and don't keep partial results that are not reliable _typographyAvailabilities = TypographyAvailabilities.None; return; } else if (complexScripts != null) { // This is the bits for localized form we would want to set // if both bits for localized form were set, we can end the loop earlier TypographyAvailabilities loclBitsTest = TypographyAvailabilities.FastTextMajorLanguageLocalizedFormAvailable | TypographyAvailabilities.FastTextExtraLanguageLocalizedFormAvailable; for (int i = 0; i < complexScripts.Length && typography != loclBitsTest; i++) { if (MajorLanguages.Contains((ScriptTags)complexScripts[i].scriptTag, (LanguageTags)complexScripts[i].langSysTag)) { typography |= TypographyAvailabilities.FastTextMajorLanguageLocalizedFormAvailable; } else { typography |= TypographyAvailabilities.FastTextExtraLanguageLocalizedFormAvailable; } } } // // step 2: continue to find out whether there is common features availabe // in the font for the unicode ranges and set the FastTextTypographyAvailable bit // unsafe { result = OpenTypeLayout.GetComplexLanguageList( GsubGpos, RequiredTypographyFeatures, glyphBits, minGlyphId, maxGlyphId, out complexScripts ); } if (result != OpenTypeLayoutResult.Success) { // The check failed. We abort and don't keep partial results that are not reliable _typographyAvailabilities = TypographyAvailabilities.None; return; } else if (complexScripts != null) { typography |= TypographyAvailabilities.FastTextTypographyAvailable; } // // Step 3: call OpenType layout engine to find out if there is any feature present for // ideographs. Because there are many ideographs to check for, an alternative is to // check for all scripts with the required features in the font by setting all // glyph bits to 1, then see whether CJKIdeograph is in the returned list. // for (int i = 0; i < glyphBitsLength; i++) { glyphBits[i] = 0xFFFFFFFF; } unsafe { result = OpenTypeLayout.GetComplexLanguageList( GsubGpos, RequiredFeatures, glyphBits, minGlyphId, maxGlyphId, out complexScripts ); } if (result != OpenTypeLayoutResult.Success) { // The check failed. We abort and don't keep partial results that are not reliable _typographyAvailabilities = TypographyAvailabilities.None; return; } else if (complexScripts != null) { for (int i = 0; i < complexScripts.Length; i++) { if (complexScripts[i].scriptTag == (uint)ScriptTags.CJKIdeographic) { typography |= TypographyAvailabilities.IdeoTypographyAvailable; } else { typography |= TypographyAvailabilities.Available; } } } if (typography != TypographyAvailabilities.None) { // if any of the bits were set, set TypographyAvailabilities.Avaialble bit // as well to indicate some lookup is available. typography |= TypographyAvailabilities.Available; } _typographyAvailabilities = typography; // Note: we don't worry about calling ReleaseUInts in case of early out for a failure // above. Releasing glyphBits is a performance optimization that is not necessary // for correctness, and not interesting in the rare failure case. BufferCache.ReleaseUInts(glyphBits); }
static CharacterMap ReadCharacterMap(CMapEntry entry, BinaryReader input) { ushort format = input.ReadUInt16(); ushort length = input.ReadUInt16(); switch (format) { default: { throw new Exception("Unknown cmap subtable: " + format); // TODO: Replace all application exceptions } case 0: { //Format 0: Byte encoding table //This is the Apple standard character to glyph index mapping table. //Type Name Description //USHORT format Format number is set to 0. //USHORT length This is the length in bytes of the subtable. //USHORT language Please see “Note on the language field in 'cmap' subtables“ in this document. //BYTE glyphIdArray[256] An array that maps character codes to glyph index values. //This is a simple 1 to 1 mapping of character codes to glyph indices. //The glyph set is limited to 256. Note that if this format is used to index into a larger glyph set, //only the first 256 glyphs will be accessible. ushort language = input.ReadUInt16(); byte[] only256Glyphs = input.ReadBytes(256); ushort[] only256UInt16Glyphs = new ushort[256]; for (int i = 255; i >= 0; --i) { //expand only256UInt16Glyphs[i] = only256Glyphs[i]; } //convert to format4 cmap table return(CharacterMap.BuildFromFormat4(1, new ushort[] { 0 }, new ushort[] { 255 }, null, null, only256UInt16Glyphs)); } case 4: { //This is the Microsoft standard character to glyph index mapping table for fonts that support Unicode ranges other than the range [U+D800 - U+DFFF] (defined as Surrogates Area, in Unicode v 3.0) //which is used for UCS-4 characters. //If a font supports this character range (i.e. in turn supports the UCS-4 characters) a subtable in this format with a platform specific encoding ID 1 is yet needed, //in addition to a subtable in format 12 with a platform specific encoding ID 10. Please see details on format 12 below, for fonts that support UCS-4 characters on Windows. // //This format is used when the character codes for the characters represented by a font fall into several contiguous ranges, //possibly with holes in some or all of the ranges (that is, some of the codes in a range may not have a representation in the font). //The format-dependent data is divided into three parts, which must occur in the following order: // A four-word header gives parameters for an optimized search of the segment list; // Four parallel arrays describe the segments (one segment for each contiguous range of codes); // A variable-length array of glyph IDs (unsigned words). long tableStartEndAt = input.BaseStream.Position + length; ushort language = input.ReadUInt16(); //Note on the language field in 'cmap' subtables: //Note on the language field in 'cmap' subtables: //The language field must be set to zero for all cmap subtables whose platform IDs are other than Macintosh (platform ID 1). //For cmap subtables whose platform IDs are Macintosh, set this field to the Macintosh language ID of the cmap subtable plus one, //or to zero if the cmap subtable is not language-specific. //For example, a Mac OS Turkish cmap subtable must set this field to 18, since the Macintosh language ID for Turkish is 17. //A Mac OS Roman cmap subtable must set this field to 0, since Mac OS Roman is not a language-specific encoding. ushort segCountX2 = input.ReadUInt16(); //2 * segCount ushort searchRange = input.ReadUInt16(); //2 * (2**FLOOR(log2(segCount))) ushort entrySelector = input.ReadUInt16(); //2 * (2**FLOOR(log2(segCount))) ushort rangeShift = input.ReadUInt16(); //2 * (2**FLOOR(log2(segCount))) int segCount = segCountX2 / 2; ushort[] endCode = Utils.ReadUInt16Array(input, segCount); //Ending character code for each segment, last = 0xFFFF. //>To ensure that the search will terminate, the final endCode value must be 0xFFFF. //>This segment need not contain any valid mappings. It can simply map the single character code 0xFFFF to the missing character glyph, glyph 0. input.ReadUInt16(); // Reserved = 0 ushort[] startCode = Utils.ReadUInt16Array(input, segCount); //Starting character code for each segment ushort[] idDelta = Utils.ReadUInt16Array(input, segCount); //Delta for all character codes in segment ushort[] idRangeOffset = Utils.ReadUInt16Array(input, segCount); //Offset in bytes to glyph indexArray, or 0 //------------------------------------------------------------------------------------ long remainingLen = tableStartEndAt - input.BaseStream.Position; int recordNum2 = (int)(remainingLen / 2); ushort[] glyphIdArray = Utils.ReadUInt16Array(input, recordNum2); //Glyph index array return(CharacterMap.BuildFromFormat4(segCount, startCode, endCode, idDelta, idRangeOffset, glyphIdArray)); } case 6: { //Format 6: Trimmed table mapping //Type Name Description //USHORT format Format number is set to 6. //USHORT length This is the length in bytes of the subtable. //USHORT language Please see “Note on the language field in 'cmap' subtables“ in this document. //USHORT firstCode First character code of subrange. //USHORT entryCount Number of character codes in subrange. //USHORT glyphIdArray[entryCount] Array of glyph index values for character codes in the range. //The firstCode and entryCount values specify a subrange(beginning at firstCode, length = entryCount) within the range of possible character codes. //Codes outside of this subrange are mapped to glyph index 0. //The offset of the code(from the first code) within this subrange is used as index to the glyphIdArray, //which provides the glyph index value. long tableStartEndAt = input.BaseStream.Position + length; ushort language = input.ReadUInt16(); ushort firstCode = input.ReadUInt16(); ushort entryCount = input.ReadUInt16(); ushort[] glyphIdArray = Utils.ReadUInt16Array(input, entryCount); return(CharacterMap.BuildFromFormat6(firstCode, glyphIdArray)); } } }
/// <summary> /// Draw the UI for this tool. /// </summary> void OnGUI() { Object fnt = (Object)NGUISettings.FMFont ?? (Object)NGUISettings.BMFont; UIFont uiFont = (fnt as UIFont); NGUIEditorTools.SetLabelWidth(80f); GUILayout.Space(3f); NGUIEditorTools.DrawHeader("Input", true); NGUIEditorTools.BeginContents(false); GUILayout.BeginHorizontal(); mType = (FontType)EditorGUILayout.EnumPopup("Type", mType, GUILayout.MinWidth(200f)); NGUIEditorTools.DrawPadding(); GUILayout.EndHorizontal(); Create create = Create.None; if (mType == FontType.ImportedBitmap) { NGUISettings.fontData = EditorGUILayout.ObjectField("Font Data", NGUISettings.fontData, typeof(TextAsset), false) as TextAsset; NGUISettings.fontTexture = EditorGUILayout.ObjectField("Texture", NGUISettings.fontTexture, typeof(Texture2D), false, GUILayout.Width(140f)) as Texture2D; NGUIEditorTools.EndContents(); // Draw the atlas selection only if we have the font data and texture specified, just to make it easier EditorGUI.BeginDisabledGroup(NGUISettings.fontData == null || NGUISettings.fontTexture == null); { NGUIEditorTools.DrawHeader("Output", true); NGUIEditorTools.BeginContents(false); ComponentSelector.Draw <UIAtlas>(NGUISettings.atlas, OnSelectAtlas, false); NGUIEditorTools.EndContents(); } EditorGUI.EndDisabledGroup(); if (NGUISettings.fontData == null) { EditorGUILayout.HelpBox("To create a font from a previously exported FNT file, you need to use BMFont on " + "Windows or your choice of Glyph Designer or the less expensive bmGlyph on the Mac.\n\n" + "Either of these tools will create a FNT file for you that you will drag & drop into the field above.", MessageType.Info); } else if (NGUISettings.fontTexture == null) { EditorGUILayout.HelpBox("When exporting your font, you should get two files: the FNT, and the texture. Only one texture can be used per font.", MessageType.Info); } else if (NGUISettings.atlas == null) { EditorGUILayout.HelpBox("You can create a font that doesn't use a texture atlas. This will mean that the text " + "labels using this font will generate an extra draw call.\n\nIf you do specify an atlas, the font's texture will be added to it automatically.", MessageType.Info); } EditorGUI.BeginDisabledGroup(NGUISettings.fontData == null || NGUISettings.fontTexture == null); { GUILayout.BeginHorizontal(); GUILayout.Space(20f); if (GUILayout.Button("Create the Font")) { create = Create.Import; } GUILayout.Space(20f); GUILayout.EndHorizontal(); } EditorGUI.EndDisabledGroup(); } else { GUILayout.BeginHorizontal(); if (NGUIEditorTools.DrawPrefixButton("Source")) { ComponentSelector.Show <Font>(OnUnityFont, new string[] { ".ttf", ".otf" }); } Font ttf = EditorGUILayout.ObjectField(NGUISettings.FMFont, typeof(Font), false) as Font; GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); { NGUISettings.FMSize = EditorGUILayout.IntField("Size", NGUISettings.FMSize, GUILayout.Width(120f)); if (mType == FontType.Dynamic) { NGUISettings.fontStyle = (FontStyle)EditorGUILayout.EnumPopup(NGUISettings.fontStyle); NGUIEditorTools.DrawPadding(); } } GUILayout.EndHorizontal(); // Choose the font style if there are multiple faces present if (mType == FontType.GeneratedBitmap) { if (!FreeType.isPresent) { string filename = (Application.platform == RuntimePlatform.WindowsEditor) ? "FreeType.dll" : "FreeType.dylib"; EditorGUILayout.HelpBox(filename + " is missing", MessageType.Error); GUILayout.BeginHorizontal(); GUILayout.Space(20f); if (GUILayout.Button("Find " + filename)) { string path = EditorUtility.OpenFilePanel("Find " + filename, NGUISettings.currentPath, (Application.platform == RuntimePlatform.WindowsEditor) ? "dll" : "dylib"); if (!string.IsNullOrEmpty(path)) { if (System.IO.Path.GetFileName(path) == filename) { NGUISettings.currentPath = System.IO.Path.GetDirectoryName(path); NGUISettings.pathToFreeType = path; } else { Debug.LogError("The library must be named '" + filename + "'"); } } } GUILayout.Space(20f); GUILayout.EndHorizontal(); } else if (ttf != null) { string[] faces = FreeType.GetFaces(ttf); if (faces != null) { if (mFaceIndex >= faces.Length) { mFaceIndex = 0; } if (faces.Length > 1) { GUILayout.Label("Style", EditorStyles.boldLabel); for (int i = 0; i < faces.Length; ++i) { GUILayout.BeginHorizontal(); GUILayout.Space(10f); if (DrawOption(i == mFaceIndex, " " + faces[i])) { mFaceIndex = i; } GUILayout.EndHorizontal(); } } } NGUISettings.fontKerning = EditorGUILayout.Toggle("Kerning", NGUISettings.fontKerning); GUILayout.Label("Characters", EditorStyles.boldLabel); CharacterMap cm = characterMap; GUILayout.BeginHorizontal(GUILayout.Width(100f)); GUILayout.BeginVertical(); GUI.changed = false; if (DrawOption(cm == CharacterMap.Numeric, " Numeric")) { cm = CharacterMap.Numeric; } if (DrawOption(cm == CharacterMap.Ascii, " ASCII")) { cm = CharacterMap.Ascii; } if (DrawOption(cm == CharacterMap.Latin, " Latin")) { cm = CharacterMap.Latin; } if (DrawOption(cm == CharacterMap.Custom, " Custom")) { cm = CharacterMap.Custom; } if (GUI.changed) { characterMap = cm; } GUILayout.EndVertical(); EditorGUI.BeginDisabledGroup(cm != CharacterMap.Custom); { if (cm != CharacterMap.Custom) { string chars = ""; if (cm == CharacterMap.Ascii) { for (int i = 33; i < 127; ++i) { chars += System.Convert.ToChar(i); } } else if (cm == CharacterMap.Numeric) { chars = "0123456789"; } else if (cm == CharacterMap.Latin) { for (int i = 33; i < 127; ++i) { chars += System.Convert.ToChar(i); } for (int i = 161; i < 256; ++i) { chars += System.Convert.ToChar(i); } } NGUISettings.charsToInclude = chars; } GUI.changed = false; string text = NGUISettings.charsToInclude; if (cm == CharacterMap.Custom) { text = EditorGUILayout.TextArea(text, GUI.skin.textArea, GUILayout.Height(80f), GUILayout.Width(Screen.width - 100f)); } else { GUILayout.Label(text, GUI.skin.textArea, GUILayout.Height(80f), GUILayout.Width(Screen.width - 100f)); } if (GUI.changed) { string final = ""; for (int i = 0; i < text.Length; ++i) { char c = text[i]; if (c < 33) { continue; } string s = c.ToString(); if (!final.Contains(s)) { final += s; } } if (final.Length > 0) { char[] chars = final.ToCharArray(); System.Array.Sort(chars); final = new string(chars); } else { final = ""; } NGUISettings.charsToInclude = final; } } EditorGUI.EndDisabledGroup(); GUILayout.EndHorizontal(); } } NGUIEditorTools.EndContents(); if (mType == FontType.Dynamic) { EditorGUI.BeginDisabledGroup(ttf == null); GUILayout.BeginHorizontal(); GUILayout.Space(20f); if (GUILayout.Button("Create the Font")) { create = Create.Dynamic; } GUILayout.Space(20f); GUILayout.EndHorizontal(); EditorGUI.EndDisabledGroup(); #if UNITY_3_5 EditorGUILayout.HelpBox("Dynamic fonts require Unity 4.0 or higher.", MessageType.Error); #else // Helpful info if (ttf == null) { EditorGUILayout.HelpBox("You don't have to create a UIFont to use dynamic fonts. You can just reference the Unity Font directly on the label.", MessageType.Info); } EditorGUILayout.HelpBox("Please note that dynamic fonts can't be made a part of an atlas, and using dynamic fonts will result in at least one extra draw call.", MessageType.Warning); #endif } else { bool isBuiltIn = (ttf != null) && string.IsNullOrEmpty(UnityEditor.AssetDatabase.GetAssetPath(ttf)); // Draw the atlas selection only if we have the font data and texture specified, just to make it easier EditorGUI.BeginDisabledGroup(ttf == null || isBuiltIn || !FreeType.isPresent); { NGUIEditorTools.DrawHeader("Output", true); NGUIEditorTools.BeginContents(false); ComponentSelector.Draw <UIAtlas>(NGUISettings.atlas, OnSelectAtlas, false); NGUIEditorTools.EndContents(); if (ttf == null) { EditorGUILayout.HelpBox("You can create a bitmap font by specifying a dynamic font to use as the source.", MessageType.Info); } else if (isBuiltIn) { EditorGUILayout.HelpBox("You chose an embedded font. You can't create a bitmap font from an embedded resource.", MessageType.Warning); } else if (NGUISettings.atlas == null) { EditorGUILayout.HelpBox("You can create a font that doesn't use a texture atlas. This will mean that the text " + "labels using this font will generate an extra draw call.\n\nIf you do specify an atlas, the font's texture will be added to it automatically.", MessageType.Info); } GUILayout.BeginHorizontal(); GUILayout.Space(20f); if (GUILayout.Button("Create the Font")) { create = Create.Bitmap; } GUILayout.Space(20f); GUILayout.EndHorizontal(); } EditorGUI.EndDisabledGroup(); } } if (create == Create.None) { return; } // Open the "Save As" file dialog #if UNITY_3_5 string prefabPath = EditorUtility.SaveFilePanel("Save As", NGUISettings.currentPath, "New Font.prefab", "prefab"); #else string prefabPath = EditorUtility.SaveFilePanelInProject("Save As", "New Font.prefab", "prefab", "Save font as...", NGUISettings.currentPath); #endif if (string.IsNullOrEmpty(prefabPath)) { return; } NGUISettings.currentPath = System.IO.Path.GetDirectoryName(prefabPath); // Load the font's prefab GameObject go = AssetDatabase.LoadAssetAtPath(prefabPath, typeof(GameObject)) as GameObject; Object prefab = null; string fontName; // Font doesn't exist yet if (go == null || go.GetComponent <UIFont>() == null) { // Create a new prefab for the atlas prefab = PrefabUtility.CreateEmptyPrefab(prefabPath); fontName = prefabPath.Replace(".prefab", ""); fontName = fontName.Substring(prefabPath.LastIndexOfAny(new char[] { '/', '\\' }) + 1); // Create a new game object for the font go = new GameObject(fontName); uiFont = go.AddComponent <UIFont>(); } else { uiFont = go.GetComponent <UIFont>(); fontName = go.name; } if (create == Create.Dynamic) { uiFont.atlas = null; uiFont.dynamicFont = NGUISettings.FMFont; uiFont.dynamicFontStyle = NGUISettings.fontStyle; uiFont.defaultSize = NGUISettings.FMSize; } else if (create == Create.Import) { Material mat = null; if (NGUISettings.atlas != null) { // Add the font's texture to the atlas UIAtlasMaker.AddOrUpdate(NGUISettings.atlas, NGUISettings.fontTexture); } else { // Create a material for the font string matPath = prefabPath.Replace(".prefab", ".mat"); mat = AssetDatabase.LoadAssetAtPath(matPath, typeof(Material)) as Material; // If the material doesn't exist, create it if (mat == null) { Shader shader = Shader.Find("Unlit/Transparent Colored"); mat = new Material(shader); // Save the material AssetDatabase.CreateAsset(mat, matPath); AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport); // Load the material so it's usable mat = AssetDatabase.LoadAssetAtPath(matPath, typeof(Material)) as Material; } mat.mainTexture = NGUISettings.fontTexture; } uiFont.dynamicFont = null; BMFontReader.Load(uiFont.bmFont, NGUITools.GetHierarchy(uiFont.gameObject), NGUISettings.fontData.bytes); if (NGUISettings.atlas == null) { uiFont.atlas = null; uiFont.material = mat; } else { uiFont.spriteName = NGUISettings.fontTexture.name; uiFont.atlas = NGUISettings.atlas; } NGUISettings.FMSize = uiFont.defaultSize; } else if (create == Create.Bitmap) { // Create the bitmap font BMFont bmFont; Texture2D tex; if (FreeType.CreateFont( NGUISettings.FMFont, NGUISettings.FMSize, mFaceIndex, NGUISettings.fontKerning, NGUISettings.charsToInclude, 1, out bmFont, out tex)) { uiFont.bmFont = bmFont; tex.name = fontName; if (NGUISettings.atlas != null) { // Add this texture to the atlas and destroy it UIAtlasMaker.AddOrUpdate(NGUISettings.atlas, tex); NGUITools.DestroyImmediate(tex); NGUISettings.fontTexture = null; tex = null; uiFont.atlas = NGUISettings.atlas; uiFont.spriteName = fontName; } else { string texPath = prefabPath.Replace(".prefab", ".png"); string matPath = prefabPath.Replace(".prefab", ".mat"); byte[] png = tex.EncodeToPNG(); FileStream fs = File.OpenWrite(texPath); fs.Write(png, 0, png.Length); fs.Close(); // See if the material already exists Material mat = AssetDatabase.LoadAssetAtPath(matPath, typeof(Material)) as Material; // If the material doesn't exist, create it if (mat == null) { Shader shader = Shader.Find("Unlit/Transparent Colored"); mat = new Material(shader); // Save the material AssetDatabase.CreateAsset(mat, matPath); AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport); // Load the material so it's usable mat = AssetDatabase.LoadAssetAtPath(matPath, typeof(Material)) as Material; } else { AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport); } // Re-load the texture tex = AssetDatabase.LoadAssetAtPath(texPath, typeof(Texture2D)) as Texture2D; // Assign the texture mat.mainTexture = tex; NGUISettings.fontTexture = tex; uiFont.atlas = null; uiFont.material = mat; } } else { return; } } if (prefab != null) { // Update the prefab PrefabUtility.ReplacePrefab(go, prefab); DestroyImmediate(go); AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport); // Select the atlas go = AssetDatabase.LoadAssetAtPath(prefabPath, typeof(GameObject)) as GameObject; uiFont = go.GetComponent <UIFont>(); } if (uiFont != null) { NGUISettings.FMFont = null; NGUISettings.BMFont = uiFont; } MarkAsChanged(); Selection.activeGameObject = go; }
/// <summary> /// Transliterate Unicode character to ASCII string. /// </summary> /// <param name="c">Character you want to transliterate into ASCII</param> /// <param name="level">Level of transliteration.</param> /// <returns> /// Transliterated string. /// </returns> public static string Unidecode(this char c, UnidecoderLevel level = UnidecoderLevel.Ascii) { if (level == UnidecoderLevel.Off) { return(new String(c, 1)); } if (c < 0x80)/*128*/ { return(new string(c, 1)); } else if (c < 161) { return(String.Empty); } else if (c < 256) { switch (level) { case UnidecoderLevel.Ansi: return(new string(c, 1)); case UnidecoderLevel.AnsiPlus: return(new string(c, 1)); } } else if (level == UnidecoderLevel.AnsiPlus && c == 306) { return("IJ"); } else if (level == UnidecoderLevel.AnsiPlus && c == 307) { return("ij"); } else if (level == UnidecoderLevel.AnsiPlus && c == 312) { return("k"); } else if (level == UnidecoderLevel.AnsiPlus && c == 319) { return("L"); } else if (level == UnidecoderLevel.AnsiPlus && c == 320) { return("l"); } else if (level == UnidecoderLevel.AnsiPlus && c == 329) { return("'n"); } else if (level == UnidecoderLevel.AnsiPlus && c == 330) { return("ng"); } else if (level == UnidecoderLevel.AnsiPlus && c == 331) { return("NG"); } else if (level == UnidecoderLevel.AnsiPlus && c == 383) { return("s"); } else if (level == UnidecoderLevel.AnsiPlus && c < 384) { return(new string(c, 1)); } else if (level == UnidecoderLevel.AnsiPlus && c == 393) { return(new string(c, 1)); } // By default: { int high = c >> 8; int low = c & 0xff; string[] transliterations; string result; if (CharacterMap.TryGetValue(high, out transliterations)) { result = transliterations[low]; } else { result = ""; } return(result); } }
/// <summary> /// Transliterate Unicode string to ASCII string. /// </summary> /// <param name="input">String you want to transliterate into ASCII</param> /// <param name="level">Level of transliteration.</param> /// <returns> /// Transliterated string. /// </returns> public static string Unidecode(this string input, UnidecoderLevel level = UnidecoderLevel.Ascii) { if (level == UnidecoderLevel.Off) { return(input); } else if (string.IsNullOrEmpty(input)) { return(input); } else if (input.All(x => x < 0x80)) { return(input); } // Unidecode result often can be at least two times longer than input string. var sb = new StringBuilder(input.Length * 2); if (level == UnidecoderLevel.Ascii) { foreach (char c in input) { if (c < 0x80)/*128*/ { sb.Append(c); } else if (c < 161) { sb.Append(""); } else { int high = c >> 8; int low = c & 0xff; string[] transliterations; string result; if (CharacterMap.TryGetValue(high, out transliterations)) { result = transliterations[low]; } else { result = ""; } sb.Append(result); } } } else if (level == UnidecoderLevel.Ansi) { foreach (char c in input) { if (c < 0x80)/*128*/ { sb.Append(c); } else if (c < 160) { sb.Append(unkn); } else if (c == 160) { sb.Append(" "); } else if (c < 256) { sb.Append(c); } else { int high = c >> 8; int low = c & 0xff; string[] transliterations; string result; if (CharacterMap.TryGetValue(high, out transliterations)) { result = transliterations[low]; } else { result = ""; } sb.Append(result); } } } else if (level == UnidecoderLevel.AnsiPlus) { foreach (char c in input) { if (c < 0x80)/*128*/ { sb.Append(c); } else if (c < 160) { sb.Append(unkn); } else if (c == 160) { sb.Append(" "); } else if (c < 256) { sb.Append(c); } else if (c == 306) { sb.Append("IJ"); } else if (c == 307) { sb.Append("ij"); } else if (c == 312) { sb.Append("k"); } else if (c == 319) { sb.Append("L"); } else if (c == 320) { sb.Append("l"); } else if (c == 329) { sb.Append("'n"); } else if (c == 330) { sb.Append("ng"); } else if (c == 331) { sb.Append("NG"); } else if (c == 383) { sb.Append("s"); } else if (c < 384) { sb.Append(c); } else if (c == 393) { sb.Append(c); } else { int high = c >> 8; int low = c & 0xff; string[] transliterations; string result; if (CharacterMap.TryGetValue(high, out transliterations)) { result = transliterations[low]; } else { result = ""; } sb.Append(result); } } } else { throw new ArgumentException("Unsupported level argument value.", "level"); } return(sb.ToString()); }
protected ParserList(IEnumerable <T> parsersArg) : base(parsersArg) { var charCounter = new Dictionary <char, int>(); int globalCounter = 0; for (int i = 0; i < Count; i++) { var parser = this[i]; if (parser == null) { throw new InvalidOperationException("Unexpected null parser found"); } parser.Initialize(); parser.Index = i; if (parser.OpeningCharacters != null && parser.OpeningCharacters.Length != 0) { foreach (var openingChar in parser.OpeningCharacters) { if (!charCounter.ContainsKey(openingChar)) { charCounter[openingChar] = 0; } charCounter[openingChar]++; } } else { globalCounter++; } } if (globalCounter > 0) { globalParsers = new T[globalCounter]; } var tempCharMap = new Dictionary <char, T[]>(); foreach (var parser in this) { if (parser.OpeningCharacters != null && parser.OpeningCharacters.Length != 0) { foreach (var openingChar in parser.OpeningCharacters) { T[] parsers; if (!tempCharMap.TryGetValue(openingChar, out parsers)) { parsers = new T[charCounter[openingChar]]; tempCharMap[openingChar] = parsers; } var index = parsers.Length - charCounter[openingChar]; parsers[index] = parser; charCounter[openingChar]--; } } else { globalParsers[globalParsers.Length - globalCounter] = parser; globalCounter--; } } charMap = new CharacterMap <T[]>(tempCharMap); }
internal virtual void AddCharacterMap(CharacterMap map) { this.characterMaps[charMapPointer++] = map; }
/// <summary> /// Convert specified text to equivalent persian text /// </summary> /// <param name="text">Text to convert</param> /// <returns>Converted text</returns> public string Convert(string text) { if (string.IsNullOrEmpty(text)) { LastConvertedText = string.Empty; } else if (LastConvertedText != null && LastConvertedText.Equals(text, StringComparison.Ordinal)) { return(LastConvertedText); } else { // make sure that we alocated enough space for text EnsureCharSize(text.Length); if (LastConvertedText == null) { LastConvertedText = string.Empty; } int startoldIndex = 0; int lastoldIndex = LastConvertedText.Length - 1; if (LastConvertedText.Length == text.Length + 1) // characters removed { int index = LastConvertedText.IndexOf(text); if (index >= 0) { if (index == 0) { PersianCharacter pc = CharacterMap.GetMappedCharacter(LastConvertedText[LastConvertedText.Length - 1]); if (pc != null && !pc.LeftToRight) { // we have to remove from first because text is reversed startoldIndex = LastConvertedText.Length - text.Length; } else { lastoldIndex = text.Length - 1; } } else { lastoldIndex = text.Length - 1; } text = LastConvertedText.Substring(startoldIndex, text.Length); } } bool findDiff = false; for (int i = 0; i < text.Length; i++) { CharInfo cf = _SourceChars[i]; cf.SourceChar = text[i]; cf.Character = CharacterMap.GetMappedCharacter(cf.SourceChar); cf.Form = PersianCharacterForm.Isolated; _RepositionedChars[i] = null; cf.IsReversed = false; // start from begin and search for missmatch if (!findDiff && startoldIndex < LastConvertedText.Length) { if (cf.SourceChar == LastConvertedText[startoldIndex]) { startoldIndex++; cf.IsReversed = true; } else { findDiff = true; } } } if (findDiff) // if we found missmatch start from last index to find last missmatch { for (int i = text.Length - 1; i >= 0; i--) { CharInfo cf = _SourceChars[i]; if (lastoldIndex >= startoldIndex) { if (cf.SourceChar == LastConvertedText[lastoldIndex]) { lastoldIndex--; cf.IsReversed = true; } else { break; } } } } // find none persian characters and place them in correct position for (int i = 0; i < text.Length; i++) { CharInfo cf = _SourceChars[i]; if (!cf.IsReversed) { _RepositionedChars[text.Length - i - 1] = cf; } } // place persian characters ( that we found them in previous text change) in correct position int j = 0; for (int i = 0; i < text.Length; i++) { if (_RepositionedChars[i] == null) { for (; j < text.Length; j++) { if (_SourceChars[j].IsReversed) { _RepositionedChars[i] = _SourceChars[j]; j++; break; } } } } // place Left To Right characters to correct place // find one left to right character for (int i = 0; i < text.Length; i++) { CharInfo cf = _RepositionedChars[i]; if (!cf.IsReversed && (cf.Character == null || cf.Character.LeftToRight)) { // find surrounding ltf chars int startIndex = i; int endIndex = i; //find most previous valid ltf index for (int k = i - 1; k >= 0; k--) { CharInfo cf2 = _RepositionedChars[k]; if (cf2.IsReversed && (cf2.Character == null || cf2.Character.LeftToRight)) { startIndex = k; } else { break; } } //find most next valid ltf index for (int k = i + 1; k < text.Length; k++) { CharInfo cf2 = _RepositionedChars[k]; if (cf2.IsReversed && (cf2.Character == null || cf2.Character.LeftToRight)) { endIndex = k; } else { break; } } int index = endIndex - (i - startIndex); int sign = Math.Sign(index - i); for (int k = i; k != index; k += sign) { _RepositionedChars[k] = _RepositionedChars[k + sign]; } _RepositionedChars[index] = cf; cf.IsReversed = true; } } // calc forms of each character for (int i = 0; i < text.Length; i++) { PersianCharacter currentPc = _RepositionedChars[i].Character; PersianCharacter prePc = null; PersianCharacter nextPc = null; PersianCharacterForm form = PersianCharacterForm.Isolated; if (currentPc != null) { if (i > 0) { nextPc = _RepositionedChars[i - 1].Character; } if (i < text.Length - 1) { prePc = _RepositionedChars[i + 1].Character; } if (prePc == null) { if (nextPc != null && nextPc.CanStickToPrevious && currentPc.CanStickToNext) { form = PersianCharacterForm.Initial; } } else if (nextPc == null) { if (prePc != null && prePc.CanStickToNext) { form = PersianCharacterForm.Final; } } else { if (nextPc.CanStickToPrevious && prePc.CanStickToNext) { if (currentPc.CanStickToNext) { form = PersianCharacterForm.Medial; } else { form = PersianCharacterForm.Final; } } else if (prePc.CanStickToNext) { form = PersianCharacterForm.Final; } else if (nextPc.CanStickToPrevious && currentPc.CanStickToNext) { form = PersianCharacterForm.Initial; } } } _RepositionedChars[i].Form = form; } // build text from end to start StringBuilder result = new StringBuilder(); for (int i = 0; i < text.Length; i++) { CharInfo cf = _RepositionedChars[i]; if (cf.Character != null) { result.Append(cf.Character[cf.Form]); } else { result.Append(cf.SourceChar); } } if (ConvertLigature) { result.Replace("\uFE8E\uFEDF", "\uFEFB"); result.Replace("\uFE8E\uFEE0", "\uFEFC"); result.Replace("\uFEEA\uFEE0\uFEDF\uFE8D", "\uFDF2"); } LastConvertedText = result.ToString(); } return(LastConvertedText); }