diff --git a/Sakura.Framework/Graphics/Drawables/SpriteText.cs b/Sakura.Framework/Graphics/Drawables/SpriteText.cs index aaca47f..c50e390 100644 --- a/Sakura.Framework/Graphics/Drawables/SpriteText.cs +++ b/Sakura.Framework/Graphics/Drawables/SpriteText.cs @@ -92,12 +92,14 @@ private void computeLayout() if (resolvedFont == null) return; + var fallbacks = fontStore.GetFallbacks(fontUsage); + window.GetPhysicalSize(out int physW, out int physH); float dpiScale = (float)physW / window.Width; if (dpiScale <= 0) dpiScale = 1.0f; - - shapedText = resolvedFont.ProcessText(Text, fontUsage.Size, dpiScale); + + shapedText = resolvedFont.ProcessText(Text, fontUsage.Size, dpiScale, fallbacks); ContentSize = new Vector2(shapedText.BoundingBox.X, shapedText.BoundingBox.Y); if (Math.Abs(Size.X - ContentSize.X) > 1.0f || Math.Abs(Size.Y - ContentSize.Y) > 1.0f) diff --git a/Sakura.Framework/Graphics/Text/Font.cs b/Sakura.Framework/Graphics/Text/Font.cs index ed000b7..5accc59 100644 --- a/Sakura.Framework/Graphics/Text/Font.cs +++ b/Sakura.Framework/Graphics/Text/Font.cs @@ -9,6 +9,7 @@ using HarfBuzzSharp; using Sakura.Framework.Graphics.Textures; using Sakura.Framework.Maths; +using Logger = Sakura.Framework.Logging.Logger; namespace Sakura.Framework.Graphics.Text; @@ -33,6 +34,7 @@ public class Font : IDisposable private readonly Dictionary<(uint CodePoint, float PhysicalSize), GlyphData> glyphCache = new(); private float currentPhysicalSize = 24; + private float currentGlyphScale = 1.0f; private readonly Lock stateLock = new Lock(); @@ -74,38 +76,126 @@ public Font(string name, byte[] fontData, TextureAtlas atlas) private GCHandle pinnedFontData; - public ShapedText ProcessText(string text, float fontSize, float dpiScale = 1.0f) + public ShapedText ProcessText(string text, float fontSize, float dpiScale = 1.0f, IEnumerable? fallbacks = null) { if (string.IsNullOrEmpty(text)) return ShapedText.Empty; + float renderFontSize = fontSize * dpiScale; + + // Determine base vertical metrics from the PRIMARY font so line height stays consistent + float ascenderPx = 0; + float lineHeightPx = Size; + lock (stateLock) { - float renderFontSize = fontSize * dpiScale; + updateFontSize(renderFontSize); + unsafe + { + var face = (FT_FaceRec_*)faceHandle; + ascenderPx = face->size->metrics.ascender / 64f; + lineHeightPx = face->size->metrics.height / 64f; + } + } + + // Segment the text into runs based on font support + var glyphs = new List(); + float cursorX = 0; + + int currentRunStart = 0; + Font? currentFont = null; + + int i = 0; + while (i < text.Length) + { + // Handle surrogate pairs properly (e.g., Emojis) + int charLen = char.IsSurrogatePair(text, i) ? 2 : 1; + uint codepoint = (uint)char.ConvertToUtf32(text, i); + + // Find which font supports this codepoint + Font assignedFont = this; + if (!HasGlyph(codepoint)) + { + if (fallbacks != null) + { + foreach (var fallback in fallbacks) + { + if (fallback.HasGlyph(codepoint)) + { + assignedFont = fallback; + break; + } + } + } + } - // 1. Update Font Size if needed - if (Math.Abs(currentPhysicalSize - renderFontSize) > 0.01f) + // If the font changed, process the previous run + if (currentFont != assignedFont) { - currentPhysicalSize = renderFontSize; - unsafe + if (currentFont != null) { - FT.FT_Set_Pixel_Sizes((FT_FaceRec_*)faceHandle, 0, (uint)currentPhysicalSize); + string runText = text.Substring(currentRunStart, i - currentRunStart); + var runGlyphs = currentFont.shapeRun(runText, renderFontSize, dpiScale, ascenderPx, ref cursorX); + glyphs.AddRange(runGlyphs); } - hbFont.SetScale((int)(currentPhysicalSize * 64), (int)(currentPhysicalSize * 64)); + currentFont = assignedFont; + currentRunStart = i; } - // 2. Get Vertical Metrics - float ascenderPx = 0; - float lineHeightPx = Size; + i += charLen; + } + + // 3. Process the final remaining run + if (currentFont != null && currentRunStart < text.Length) + { + string runText = text.Substring(currentRunStart); + var runGlyphs = currentFont.shapeRun(runText, renderFontSize, dpiScale, ascenderPx, ref cursorX); + glyphs.AddRange(runGlyphs); + } + + return new ShapedText(glyphs, new Vector2(cursorX / dpiScale, lineHeightPx / dpiScale)); + } + + private void updateFontSize(float renderFontSize) + { + if (Math.Abs(currentPhysicalSize - renderFontSize) > 0.01f) + { + currentPhysicalSize = renderFontSize; + currentGlyphScale = 1.0f; // Reset scale for normal fonts unsafe { var face = (FT_FaceRec_*)faceHandle; - ascenderPx = face->size->metrics.ascender / 64f; - lineHeightPx = face->size->metrics.height / 64f; + + // Try to set the exact scalable pixel size + var err = FT.FT_Set_Pixel_Sizes(face, 0, (uint)currentPhysicalSize); + + // If the font is a bitmap-only font (like Color Emojis), it will fail if the size isn't exact. + if (err != FT_Error.FT_Err_Ok && face->num_fixed_sizes > 0) + { + // Fallback to the first available fixed size strike + FT.FT_Select_Size(face, 0); + + // Pull the height directly from the fixed size strike array + float actualSize = face->available_sizes[0].height; + if (actualSize > 0) + { + currentGlyphScale = currentPhysicalSize / actualSize; + } + } } + hbFont.SetScale((int)(currentPhysicalSize * 64), (int)(currentPhysicalSize * 64)); + } + } + + private List shapeRun(string text, float renderFontSize, float dpiScale, float baselineY, ref float cursorX) + { + var glyphs = new List(); + + lock (stateLock) + { + updateFontSize(renderFontSize); - // 3. Shape Text sharedBuffer.ClearContents(); sharedBuffer.AddUtf16(text); sharedBuffer.GuessSegmentProperties(); @@ -115,25 +205,19 @@ public ShapedText ProcessText(string text, float fontSize, float dpiScale = 1.0f var info = sharedBuffer.GlyphInfos; var pos = sharedBuffer.GlyphPositions; - var glyphs = new List(length); - - float cursorX = 0; - float baselineY = ascenderPx; - for (int i = 0; i < length; i++) { - uint codepoint = info[i].Codepoint; + // HarfBuzz info[i].Codepoint is actually the specific glyph index for THIS font face. + uint glyphIndex = info[i].Codepoint; float xAdvance = pos[i].XAdvance / 64.0f; - float yAdvance = pos[i].YAdvance / 64.0f; - // Since change render to DPI scaling, change to cache by real render size - var cacheKey = (codepoint, renderFontSize); + // Cache by glyph index and size. + var cacheKey = (glyphIndex, renderFontSize); if (!glyphCache.TryGetValue(cacheKey, out GlyphData data)) { - // Rasterize will use the current 'Size' set above - var loaded = rasterizeGlyph(codepoint); + var loaded = rasterizeGlyph(glyphIndex); if (loaded.HasValue) { data = loaded.Value; @@ -141,6 +225,7 @@ public ShapedText ProcessText(string text, float fontSize, float dpiScale = 1.0f } else { + // Move the cursor even if it's an invisible character like a space cursorX += xAdvance; continue; } @@ -149,37 +234,54 @@ public ShapedText ProcessText(string text, float fontSize, float dpiScale = 1.0f float hbXOffset = pos[i].XOffset / 64.0f; float hbYOffset = pos[i].YOffset / 64.0f; - float finalX = cursorX + hbXOffset + data.BitmapLeft; - float finalY = baselineY - hbYOffset - data.BitmapTop; + float scaledLeft = data.BitmapLeft * currentGlyphScale; + float scaledTop = data.BitmapTop * currentGlyphScale; + float scaledWidth = data.Texture.Width * currentGlyphScale; + float scaledHeight = data.Texture.Height * currentGlyphScale; + + float finalX = cursorX + hbXOffset + scaledLeft; + float finalY = baselineY - hbYOffset - scaledTop; glyphs.Add(new TextGlyph { Texture = data.Texture, Position = new Vector2(finalX / dpiScale, finalY / dpiScale), - Size = new Vector2(data.Texture.Width / dpiScale, data.Texture.Height / dpiScale) + Size = new Vector2(scaledWidth / dpiScale, scaledHeight / dpiScale) }); cursorX += xAdvance; } - - return new ShapedText(glyphs, new Vector2(cursorX / dpiScale, lineHeightPx / dpiScale)); } + + return glyphs; } private unsafe GlyphData? rasterizeGlyph(uint glyphIndex) { var facePtr = (FT_FaceRec_*)faceHandle; - const int ft_load_default = 0; + + // FT_LOAD_COLOR is 1 << 20 in FreeType. This tells FreeType to load colored emoji bitmaps if they exist. + const int FT_LOAD_COLOR = 1 << 20; + int loadFlags = 0 | FT_LOAD_COLOR; // 0 is FT_LOAD_DEFAULT // Load glyph - var err = FT.FT_Load_Glyph(facePtr, glyphIndex, ft_load_default); - if (err != FT_Error.FT_Err_Ok) return null; + var err = FT.FT_Load_Glyph(facePtr, glyphIndex, (FreeTypeSharp.FT_LOAD)loadFlags); + if (err != FT_Error.FT_Err_Ok) + { + Logger.Error($"Failed to load glyph index {glyphIndex} with FT_LOAD_COLOR. Error: {err}. Retrying without color flag."); + err = FT.FT_Load_Glyph(facePtr, glyphIndex, FreeTypeSharp.FT_LOAD.FT_LOAD_DEFAULT); + if (err != FT_Error.FT_Err_Ok) return null; + } var glyphSlotPtr = facePtr->glyph; - // Render to bitmap - err = FT.FT_Render_Glyph(glyphSlotPtr, FT_Render_Mode_.FT_RENDER_MODE_NORMAL); - if (err != FT_Error.FT_Err_Ok) return null; + // Only render if the glyph is a vector outline + // If it's an emoji, it will already be FT_GLYPH_FORMAT_BITMAP + if (glyphSlotPtr->format != FreeTypeSharp.FT_Glyph_Format_.FT_GLYPH_FORMAT_BITMAP) + { + err = FT.FT_Render_Glyph(glyphSlotPtr, FreeTypeSharp.FT_Render_Mode_.FT_RENDER_MODE_NORMAL); + if (err != FT_Error.FT_Err_Ok) return null; + } // Get bitmap info FT_Bitmap_ bitmap = glyphSlotPtr->bitmap; @@ -199,13 +301,40 @@ public ShapedText ProcessText(string text, float fontSize, float dpiScale = 1.0f byte[] rgba = new byte[width * height * 4]; byte* buffer = bitmap.buffer; - for (int i = 0; i < width * height; i++) + int pitch = Math.Abs(bitmap.pitch); + + if (bitmap.pixel_mode == FT_Pixel_Mode_.FT_PIXEL_MODE_BGRA) { - byte val = buffer[i]; - rgba[i * 4 + 0] = 255; - rgba[i * 4 + 1] = 255; - rgba[i * 4 + 2] = 255; - rgba[i * 4 + 3] = val; + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + int src = y * pitch + x * 4; + int dst = (y * width + x) * 4; + // Convert BGRA to RGBA for OpenGL + rgba[dst + 0] = buffer[src + 2]; // R + rgba[dst + 1] = buffer[src + 1]; // G + rgba[dst + 2] = buffer[src + 0]; // B + rgba[dst + 3] = buffer[src + 3]; // A + } + } + } + else + { + // Standard grayscale anti-aliased font + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + int src = y * pitch + x; + int dst = (y * width + x) * 4; + byte val = buffer[src]; + rgba[dst + 0] = 255; + rgba[dst + 1] = 255; + rgba[dst + 2] = 255; + rgba[dst + 3] = val; + } + } } var texture = atlas.AddRegion(width, height, rgba); @@ -219,6 +348,15 @@ public ShapedText ProcessText(string text, float fontSize, float dpiScale = 1.0f }; } + public bool HasGlyph(uint codepoint) + { + unsafe + { + // FT_Get_Char_Index returns 0 if the glyph is missing + return FT.FT_Get_Char_Index((FT_FaceRec_*)faceHandle, codepoint) > 0; + } + } + public void Dispose() { sharedBuffer.Dispose(); diff --git a/Sakura.Framework/Graphics/Text/GLFontStore.cs b/Sakura.Framework/Graphics/Text/GLFontStore.cs index a1ebffc..fabcf4e 100644 --- a/Sakura.Framework/Graphics/Text/GLFontStore.cs +++ b/Sakura.Framework/Graphics/Text/GLFontStore.cs @@ -19,6 +19,7 @@ public class GLFontStore : IFontStore { private readonly TextureAtlas atlas; private readonly Dictionary fontCache = new Dictionary(); + private readonly List fallbackFamilies = new List(); private Font defaultFont; @@ -29,22 +30,8 @@ public GLFontStore(GL gl) private void loadFrameworkFonts(Storage resourceStorage) { - string[] weights = Enum.GetNames(typeof(DefaultFontWeights)); - - string family = "NotoSans"; - - foreach (string weight in weights) - { - string normalFileName = $"{family}-{weight}.ttf"; - AddFont(resourceStorage, normalFileName, alias: $"{family}-{weight}"); - - string italicFileName; - string italicKey = $"{family}-{weight}Italic"; - - italicFileName = weight == "Regular" ? $"{family}-Italic.ttf" : $"{family}-{weight}Italic.ttf"; - - AddFont(resourceStorage, italicFileName, alias: italicKey); - } + // primary base font (NotoSans with Italics) + loadFamily(resourceStorage, "NotoSans", hasItalics: true); if (fontCache.TryGetValue("NotoSans-Regular", out var reg)) { @@ -56,6 +43,52 @@ private void loadFrameworkFonts(Storage resourceStorage) { Logger.Warning("FontLoader : NotoSans-Regular.ttf was not found. Default font is missing."); } + + // fallback families for various languages + string[] fallbackFamilies = new[] + { + "NotoSansThai", + "NotoSansJP", + "NotoSansKR", + "NotoSansSC", + "NotoSansTC", + "NotoSansArabic", + "NotoSansDevanagari", + "NotoSansHebrew" + }; + + foreach (string family in fallbackFamilies) + { + // These families don't have italics + loadFamily(resourceStorage, family, hasItalics: false); + AddFallbackFamily(family); + } + + // Use NotoEmoji for fallback of emoji and other symbols + // Since to use NotoColorEmoji need to change the freetype to compile with libpng so just use monochrome NotoEmoji for now + // which is still better than missing glyphs. + // TODO: Add support for color emoji in the future + AddFallbackFamily("NotoEmoji"); + } + + private void loadFamily(Storage storage, string family, bool hasItalics) + { + string[] weights = Enum.GetNames(typeof(DefaultFontWeights)); + + foreach (string weight in weights) + { + string normalFileName = $"{family}-{weight}.ttf"; + + // AddFont already has a try-catch and checks if the stream is null, + // so it will safely skip weights that don't exist in the storage. + AddFont(storage, normalFileName, alias: $"{family}-{weight}"); + + if (hasItalics) + { + string italicFileName = weight == "Regular" ? $"{family}-Italic.ttf" : $"{family}-{weight}Italic.ttf"; + AddFont(storage, italicFileName, alias: $"{family}-{weight}Italic"); + } + } } public void LoadDefaultFont(Storage resourceStorage) @@ -79,7 +112,7 @@ public void AddFont(Storage storage, string filename, string alias = null!) fontCache[name] = font; GlobalStatistics.Get("Fonts", "Loaded Fonts").Value = fontCache.Count; - Logger.Verbose($"Loaded font {name} from {filename}"); + Logger.Debug($"Loaded font {name} from {filename}"); } catch (Exception ex) { @@ -126,6 +159,42 @@ public Font Get(string name) return defaultFont; } + public void AddFallbackFamily(string familyName) + { + if (!fallbackFamilies.Contains(familyName)) + fallbackFamilies.Add(familyName); + } + + public void InsertFallbackFamily(int index, string familyName) + { + if (!fallbackFamilies.Contains(familyName)) + fallbackFamilies.Insert(index, familyName); + } + + public void ClearFallbackFamilies() + { + fallbackFamilies.Clear(); + } + + public IEnumerable GetFallbacks(FontUsage usage) + { + var fallbacks = new List(); + + foreach (var family in fallbackFamilies) + { + var fallbackUsage = usage.With(family: family); + var fallbackFont = Get(fallbackUsage); + + // If the font exists and isn't just returning the default NotoSans-Regular fallback + if (fallbackFont != null && fallbackFont != defaultFont && !fallbacks.Contains(fallbackFont)) + { + fallbacks.Add(fallbackFont); + } + } + + return fallbacks; + } + public TextureAtlas Atlas => atlas; public void Dispose() diff --git a/Sakura.Framework/Graphics/Text/HeadlessFontStore.cs b/Sakura.Framework/Graphics/Text/HeadlessFontStore.cs index 77fd2a0..d3220d6 100644 --- a/Sakura.Framework/Graphics/Text/HeadlessFontStore.cs +++ b/Sakura.Framework/Graphics/Text/HeadlessFontStore.cs @@ -1,6 +1,8 @@ // This code is part of the Sakura framework project. Licensed under the MIT License. // See the LICENSE file for full license text. +using System; +using System.Collections.Generic; using Sakura.Framework.Graphics.Textures; using Sakura.Framework.Platform; @@ -26,6 +28,26 @@ public void AddFont(Storage storage, string filename, string alias = null) } + public void AddFallbackFamily(string familyName) + { + + } + + public void InsertFallbackFamily(int index, string familyName) + { + + } + + public void ClearFallbackFamilies() + { + + } + + public IEnumerable GetFallbacks(FontUsage usage) + { + return Array.Empty(); + } + public Font Get(FontUsage usage) { return null; diff --git a/Sakura.Framework/Graphics/Text/IFontStore.cs b/Sakura.Framework/Graphics/Text/IFontStore.cs index 85b6fb1..e4893c7 100644 --- a/Sakura.Framework/Graphics/Text/IFontStore.cs +++ b/Sakura.Framework/Graphics/Text/IFontStore.cs @@ -2,6 +2,7 @@ // See the LICENSE file for full license text. using System; +using System.Collections.Generic; using Sakura.Framework.Platform; namespace Sakura.Framework.Graphics.Text; @@ -24,6 +25,26 @@ public interface IFontStore : IDisposable /// Optional alias to refer to this font. If null, uses filename without extension. void AddFont(Storage storage, string filename, string alias = null); + /// + /// Adds a font family to be used as a fallback. + /// + void AddFallbackFamily(string familyName); + + /// + /// Inserts a fallback family at a specific priority level. + /// + void InsertFallbackFamily(int index, string familyName); + + /// + /// Clears all currently registered fallback families. + /// + void ClearFallbackFamilies(); + + /// + /// Retrieves all registered fallback fonts configured for the requested usage (Weight/Italics). + /// + IEnumerable GetFallbacks(FontUsage usage); + /// /// Retrieves a font matching the specified usage. /// diff --git a/Sakura.Framework/Resources/Fonts/NotoEmoji-Bold.ttf b/Sakura.Framework/Resources/Fonts/NotoEmoji-Bold.ttf new file mode 100755 index 0000000..d574c00 Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoEmoji-Bold.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoEmoji-Light.ttf b/Sakura.Framework/Resources/Fonts/NotoEmoji-Light.ttf new file mode 100755 index 0000000..86932b7 Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoEmoji-Light.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoEmoji-Medium.ttf b/Sakura.Framework/Resources/Fonts/NotoEmoji-Medium.ttf new file mode 100755 index 0000000..9f16d8e Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoEmoji-Medium.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoEmoji-Regular.ttf b/Sakura.Framework/Resources/Fonts/NotoEmoji-Regular.ttf new file mode 100755 index 0000000..b50618f Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoEmoji-Regular.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoEmoji-SemiBold.ttf b/Sakura.Framework/Resources/Fonts/NotoEmoji-SemiBold.ttf new file mode 100755 index 0000000..95c3e22 Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoEmoji-SemiBold.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansArabic-Black.ttf b/Sakura.Framework/Resources/Fonts/NotoSansArabic-Black.ttf new file mode 100755 index 0000000..bca658d Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansArabic-Black.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansArabic-Bold.ttf b/Sakura.Framework/Resources/Fonts/NotoSansArabic-Bold.ttf new file mode 100755 index 0000000..5e44da8 Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansArabic-Bold.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansArabic-ExtraBold.ttf b/Sakura.Framework/Resources/Fonts/NotoSansArabic-ExtraBold.ttf new file mode 100755 index 0000000..d680fe4 Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansArabic-ExtraBold.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansArabic-ExtraLight.ttf b/Sakura.Framework/Resources/Fonts/NotoSansArabic-ExtraLight.ttf new file mode 100755 index 0000000..c55e869 Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansArabic-ExtraLight.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansArabic-Light.ttf b/Sakura.Framework/Resources/Fonts/NotoSansArabic-Light.ttf new file mode 100755 index 0000000..351eb18 Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansArabic-Light.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansArabic-Medium.ttf b/Sakura.Framework/Resources/Fonts/NotoSansArabic-Medium.ttf new file mode 100755 index 0000000..f05d484 Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansArabic-Medium.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansArabic-Regular.ttf b/Sakura.Framework/Resources/Fonts/NotoSansArabic-Regular.ttf new file mode 100755 index 0000000..3279938 Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansArabic-Regular.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansArabic-SemiBold.ttf b/Sakura.Framework/Resources/Fonts/NotoSansArabic-SemiBold.ttf new file mode 100755 index 0000000..ea3d762 Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansArabic-SemiBold.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansArabic-Thin.ttf b/Sakura.Framework/Resources/Fonts/NotoSansArabic-Thin.ttf new file mode 100755 index 0000000..99cf26b Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansArabic-Thin.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansDevanagari-Black.ttf b/Sakura.Framework/Resources/Fonts/NotoSansDevanagari-Black.ttf new file mode 100755 index 0000000..53d6254 Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansDevanagari-Black.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansDevanagari-Bold.ttf b/Sakura.Framework/Resources/Fonts/NotoSansDevanagari-Bold.ttf new file mode 100755 index 0000000..7aa0d06 Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansDevanagari-Bold.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansDevanagari-ExtraBold.ttf b/Sakura.Framework/Resources/Fonts/NotoSansDevanagari-ExtraBold.ttf new file mode 100755 index 0000000..f6cb81e Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansDevanagari-ExtraBold.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansDevanagari-ExtraLight.ttf b/Sakura.Framework/Resources/Fonts/NotoSansDevanagari-ExtraLight.ttf new file mode 100755 index 0000000..460ffb0 Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansDevanagari-ExtraLight.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansDevanagari-Light.ttf b/Sakura.Framework/Resources/Fonts/NotoSansDevanagari-Light.ttf new file mode 100755 index 0000000..d26aac0 Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansDevanagari-Light.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansDevanagari-Medium.ttf b/Sakura.Framework/Resources/Fonts/NotoSansDevanagari-Medium.ttf new file mode 100755 index 0000000..8f3fafd Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansDevanagari-Medium.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansDevanagari-Regular.ttf b/Sakura.Framework/Resources/Fonts/NotoSansDevanagari-Regular.ttf new file mode 100755 index 0000000..ab3f433 Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansDevanagari-Regular.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansDevanagari-SemiBold.ttf b/Sakura.Framework/Resources/Fonts/NotoSansDevanagari-SemiBold.ttf new file mode 100755 index 0000000..f3aa99f Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansDevanagari-SemiBold.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansDevanagari-Thin.ttf b/Sakura.Framework/Resources/Fonts/NotoSansDevanagari-Thin.ttf new file mode 100755 index 0000000..2c2dbcf Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansDevanagari-Thin.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansHebrew-Black.ttf b/Sakura.Framework/Resources/Fonts/NotoSansHebrew-Black.ttf new file mode 100755 index 0000000..f3e8352 Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansHebrew-Black.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansHebrew-Bold.ttf b/Sakura.Framework/Resources/Fonts/NotoSansHebrew-Bold.ttf new file mode 100755 index 0000000..4275d78 Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansHebrew-Bold.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansHebrew-ExtraBold.ttf b/Sakura.Framework/Resources/Fonts/NotoSansHebrew-ExtraBold.ttf new file mode 100755 index 0000000..9386680 Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansHebrew-ExtraBold.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansHebrew-ExtraLight.ttf b/Sakura.Framework/Resources/Fonts/NotoSansHebrew-ExtraLight.ttf new file mode 100755 index 0000000..3be1942 Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansHebrew-ExtraLight.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansHebrew-Light.ttf b/Sakura.Framework/Resources/Fonts/NotoSansHebrew-Light.ttf new file mode 100755 index 0000000..3f447a3 Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansHebrew-Light.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansHebrew-Medium.ttf b/Sakura.Framework/Resources/Fonts/NotoSansHebrew-Medium.ttf new file mode 100755 index 0000000..91a1d53 Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansHebrew-Medium.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansHebrew-Regular.ttf b/Sakura.Framework/Resources/Fonts/NotoSansHebrew-Regular.ttf new file mode 100755 index 0000000..b44a0db Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansHebrew-Regular.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansHebrew-SemiBold.ttf b/Sakura.Framework/Resources/Fonts/NotoSansHebrew-SemiBold.ttf new file mode 100755 index 0000000..3d3c457 Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansHebrew-SemiBold.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansHebrew-Thin.ttf b/Sakura.Framework/Resources/Fonts/NotoSansHebrew-Thin.ttf new file mode 100755 index 0000000..d0c64b9 Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansHebrew-Thin.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansJP-Black.ttf b/Sakura.Framework/Resources/Fonts/NotoSansJP-Black.ttf new file mode 100755 index 0000000..0e33dde Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansJP-Black.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansJP-Bold.ttf b/Sakura.Framework/Resources/Fonts/NotoSansJP-Bold.ttf new file mode 100755 index 0000000..26a47bb Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansJP-Bold.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansJP-ExtraBold.ttf b/Sakura.Framework/Resources/Fonts/NotoSansJP-ExtraBold.ttf new file mode 100755 index 0000000..8435967 Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansJP-ExtraBold.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansJP-ExtraLight.ttf b/Sakura.Framework/Resources/Fonts/NotoSansJP-ExtraLight.ttf new file mode 100755 index 0000000..df421ba Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansJP-ExtraLight.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansJP-Light.ttf b/Sakura.Framework/Resources/Fonts/NotoSansJP-Light.ttf new file mode 100755 index 0000000..7e8c292 Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansJP-Light.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansJP-Medium.ttf b/Sakura.Framework/Resources/Fonts/NotoSansJP-Medium.ttf new file mode 100755 index 0000000..f107339 Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansJP-Medium.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansJP-Regular.ttf b/Sakura.Framework/Resources/Fonts/NotoSansJP-Regular.ttf new file mode 100755 index 0000000..d13df30 Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansJP-Regular.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansJP-SemiBold.ttf b/Sakura.Framework/Resources/Fonts/NotoSansJP-SemiBold.ttf new file mode 100755 index 0000000..2a24dc7 Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansJP-SemiBold.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansJP-Thin.ttf b/Sakura.Framework/Resources/Fonts/NotoSansJP-Thin.ttf new file mode 100755 index 0000000..b3d4cf4 Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansJP-Thin.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansKR-Black.ttf b/Sakura.Framework/Resources/Fonts/NotoSansKR-Black.ttf new file mode 100755 index 0000000..16ed6f3 Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansKR-Black.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansKR-Bold.ttf b/Sakura.Framework/Resources/Fonts/NotoSansKR-Bold.ttf new file mode 100755 index 0000000..c7aef6b Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansKR-Bold.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansKR-ExtraBold.ttf b/Sakura.Framework/Resources/Fonts/NotoSansKR-ExtraBold.ttf new file mode 100755 index 0000000..12f5f13 Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansKR-ExtraBold.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansKR-ExtraLight.ttf b/Sakura.Framework/Resources/Fonts/NotoSansKR-ExtraLight.ttf new file mode 100755 index 0000000..abb47f4 Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansKR-ExtraLight.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansKR-Light.ttf b/Sakura.Framework/Resources/Fonts/NotoSansKR-Light.ttf new file mode 100755 index 0000000..c53b3ae Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansKR-Light.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansKR-Medium.ttf b/Sakura.Framework/Resources/Fonts/NotoSansKR-Medium.ttf new file mode 100755 index 0000000..734a40a Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansKR-Medium.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansKR-Regular.ttf b/Sakura.Framework/Resources/Fonts/NotoSansKR-Regular.ttf new file mode 100755 index 0000000..984b17f Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansKR-Regular.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansKR-SemiBold.ttf b/Sakura.Framework/Resources/Fonts/NotoSansKR-SemiBold.ttf new file mode 100755 index 0000000..9db1869 Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansKR-SemiBold.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansKR-Thin.ttf b/Sakura.Framework/Resources/Fonts/NotoSansKR-Thin.ttf new file mode 100755 index 0000000..3be5536 Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansKR-Thin.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansSC-Black.ttf b/Sakura.Framework/Resources/Fonts/NotoSansSC-Black.ttf new file mode 100755 index 0000000..fad9d0b Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansSC-Black.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansSC-Bold.ttf b/Sakura.Framework/Resources/Fonts/NotoSansSC-Bold.ttf new file mode 100755 index 0000000..17380a9 Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansSC-Bold.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansSC-ExtraBold.ttf b/Sakura.Framework/Resources/Fonts/NotoSansSC-ExtraBold.ttf new file mode 100755 index 0000000..91c81b6 Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansSC-ExtraBold.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansSC-ExtraLight.ttf b/Sakura.Framework/Resources/Fonts/NotoSansSC-ExtraLight.ttf new file mode 100755 index 0000000..1c39e51 Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansSC-ExtraLight.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansSC-Light.ttf b/Sakura.Framework/Resources/Fonts/NotoSansSC-Light.ttf new file mode 100755 index 0000000..a0772d4 Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansSC-Light.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansSC-Medium.ttf b/Sakura.Framework/Resources/Fonts/NotoSansSC-Medium.ttf new file mode 100755 index 0000000..901afd4 Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansSC-Medium.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansSC-Regular.ttf b/Sakura.Framework/Resources/Fonts/NotoSansSC-Regular.ttf new file mode 100755 index 0000000..9c21fd3 Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansSC-Regular.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansSC-SemiBold.ttf b/Sakura.Framework/Resources/Fonts/NotoSansSC-SemiBold.ttf new file mode 100755 index 0000000..707bc34 Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansSC-SemiBold.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansSC-Thin.ttf b/Sakura.Framework/Resources/Fonts/NotoSansSC-Thin.ttf new file mode 100755 index 0000000..818ae7a Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansSC-Thin.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansTC-Black.ttf b/Sakura.Framework/Resources/Fonts/NotoSansTC-Black.ttf new file mode 100755 index 0000000..b0715da Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansTC-Black.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansTC-Bold.ttf b/Sakura.Framework/Resources/Fonts/NotoSansTC-Bold.ttf new file mode 100755 index 0000000..61a61ad Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansTC-Bold.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansTC-ExtraBold.ttf b/Sakura.Framework/Resources/Fonts/NotoSansTC-ExtraBold.ttf new file mode 100755 index 0000000..99db098 Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansTC-ExtraBold.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansTC-ExtraLight.ttf b/Sakura.Framework/Resources/Fonts/NotoSansTC-ExtraLight.ttf new file mode 100755 index 0000000..25f3c97 Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansTC-ExtraLight.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansTC-Light.ttf b/Sakura.Framework/Resources/Fonts/NotoSansTC-Light.ttf new file mode 100755 index 0000000..4530b2f Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansTC-Light.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansTC-Medium.ttf b/Sakura.Framework/Resources/Fonts/NotoSansTC-Medium.ttf new file mode 100755 index 0000000..27aa1f2 Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansTC-Medium.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansTC-Regular.ttf b/Sakura.Framework/Resources/Fonts/NotoSansTC-Regular.ttf new file mode 100755 index 0000000..807c2af Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansTC-Regular.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansTC-SemiBold.ttf b/Sakura.Framework/Resources/Fonts/NotoSansTC-SemiBold.ttf new file mode 100755 index 0000000..05e6f40 Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansTC-SemiBold.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansTC-Thin.ttf b/Sakura.Framework/Resources/Fonts/NotoSansTC-Thin.ttf new file mode 100755 index 0000000..5d36627 Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansTC-Thin.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansThai-Black.ttf b/Sakura.Framework/Resources/Fonts/NotoSansThai-Black.ttf new file mode 100755 index 0000000..aec52a6 Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansThai-Black.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansThai-Bold.ttf b/Sakura.Framework/Resources/Fonts/NotoSansThai-Bold.ttf new file mode 100755 index 0000000..9c6798c Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansThai-Bold.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansThai-ExtraBold.ttf b/Sakura.Framework/Resources/Fonts/NotoSansThai-ExtraBold.ttf new file mode 100755 index 0000000..0ed192b Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansThai-ExtraBold.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansThai-ExtraLight.ttf b/Sakura.Framework/Resources/Fonts/NotoSansThai-ExtraLight.ttf new file mode 100755 index 0000000..30f2a25 Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansThai-ExtraLight.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansThai-Light.ttf b/Sakura.Framework/Resources/Fonts/NotoSansThai-Light.ttf new file mode 100755 index 0000000..de2b2e0 Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansThai-Light.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansThai-Medium.ttf b/Sakura.Framework/Resources/Fonts/NotoSansThai-Medium.ttf new file mode 100755 index 0000000..aea0450 Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansThai-Medium.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansThai-Regular.ttf b/Sakura.Framework/Resources/Fonts/NotoSansThai-Regular.ttf new file mode 100755 index 0000000..54f3e29 Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansThai-Regular.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansThai-SemiBold.ttf b/Sakura.Framework/Resources/Fonts/NotoSansThai-SemiBold.ttf new file mode 100755 index 0000000..4a04f1a Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansThai-SemiBold.ttf differ diff --git a/Sakura.Framework/Resources/Fonts/NotoSansThai-Thin.ttf b/Sakura.Framework/Resources/Fonts/NotoSansThai-Thin.ttf new file mode 100755 index 0000000..12e3095 Binary files /dev/null and b/Sakura.Framework/Resources/Fonts/NotoSansThai-Thin.ttf differ