View Issue Details
ID | Project | Category | View Status | Date Submitted | Last Update |
---|---|---|---|---|---|
0002380 | NoesisGUI | C++ SDK | public | 2022-07-14 11:45 | 2024-02-26 11:12 |
Reporter | steveh | Assigned To | jsantos | ||
Priority | normal | Severity | feature | Reproducibility | always |
Status | feedback | Resolution | open | ||
Product Version | 3.2.0 | ||||
Target Version | 3.2 | ||||
Summary | 0002380: Feature Request: Composite Fonts | ||||
Description | Hi guys, I'm wondering if it's possible to get composite font support (https://docs.microsoft.com/en-us/dotnet/api/system.windows.media.fontfamily?view=windowsdesktop-6.0#composite-fonts) The issue I'm having at the moment is I have a tonne of individual license fonts for a variety of languages. If I include all fonts in the font fallback array I have no way of only selecting a range of unicode characters to use a given font. So it creates an issue as the following: - The Chinese font has glyphs for Traditional Chinese, Simplified Chinese and Korean. - The Korean font has glyphs for Korean If I make my font fallback map: {KoreanFont, ChineseFont} Then it works. If I reverse the order the Chinese font takes precedence. This is easy enough to solve in this case, but in my case I have 6 different fonts for different languages, and they share a huge overlap of supported uniocde ranges. I am unable to get into a position where the fallback map uses the correct font fallback for every language. I have managed to get around this issue by only loading the fonts for the current selected language, but we have some screens (e.g. language select and some editor sandboxes) where we show multiple languages on screen at the same time. I could change our fonts so they remove the glyphs, but that would require that I change some licensed font. Ideally we would be able to specify the unicode range that a font supports in the font fallback map, either via a bespoke solution or via the implementation of the composite fonts. | ||||
Tags | No tags attached. | ||||
Platform | Any | ||||
Hello, let me think more about this and I will come back again (by the way I am taking the next week off) At first, I would say that implementing support for *.CompositeFont files seems to be the best solution. But I am not sure how complex this can be |
|
Hi Steven, I am not sure if you found a solution for this or how critical this request is. I was investigating the implementation of CompositeFonts in Noesis and it is harder than expected and we want to do it after official 3.2 is released (we have plans to release the beta this month). Please, let me know your thoughts about this and if we can help you with a workaround. |
|
Hey Jesús, we have not come to a solution yet. We have 2 critical parts that we're dealing with. 1. Gamertags - Microsoft dictate that with modern gamertags we must support 14 different character sets. We support these across a variety of different separate fonts. 2. Language Select - We need to show multiple different fonts on this page. 2 is fairly trivial to solve as we could just have a bunch of fixed pre-rendered bitmap images instead of fonts as there is a finite set of languages we support so we can bake the text into an image. However, we don't have any solution for gamertags. |
|
As a temporary workaround, could you strip glyph ranges from needed fonts? Just to save time before we can properly implement this. Are you 100% sure this would break the copyright of the font? |
|
Hey Jesús, I thought from the initial look we were not allowed to make any modifications to the fonts at all. I'll ask our producers to run it through our lawyers though to double check. | |
Thank you. Meanwhile let me see if I find an easier way to quickly implement this. I am thinking about adding a range to each font (manually). | |
Hi Steve, I hope everything is great over there. Did you get an answer from the producers? We have been really busy closing 3.2 BETA 1 (https://www.noesisengine.com/docs/3.2/Gui.Core.Changelog.html) and we have imminent plans to release BETA 2. After that, I think I can find time to offer you solutions for this. Should we have a call? |
|
Hey Jesús, everything's going great thank you, hope the 3.2 release is going smoothly. I got the initial okay from our production staff but they were planning on checking with legal before we commit to that, I've not heard back. I've not solved this yet, so it's still going to be an issue. I'll give our production a prod now to chase up with legal again. If we need to specify a range for each font then that sounds fine to me and would be a find solution. I'm happy to join a call if you think that's easier. Just drop me an email with a desired time and I'm happy to jump on a call. Cheers, -Steven |
|
I am attaching the "hacky" solution for now. There are two important changes in the patch: 1) HarfBuzz.h and HarfBuzz.cpp are exposing now a new HB::Shape function with ranges. These changes will be part of Noesis 2) The rest of changes are experimental, we have added a vector of ranges to VGLFontFace and the layout is using that vector when calling HB::Shape. You need to manually detect the fonts by name and adjust the needed ranges If everything is working fine with these change we will keep working on a better solution by implementing the property FontFamily.FontFamilyMap. This will support scenarios like this: <Grid xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:System="clr-namespace:System;assembly=mscorlib"> <Grid.Resources> <FontFamily x:Key="TheFont"> <FontFamily.FamilyMaps> <FontFamilyMap Unicode="0048" Target="Times New Roman" Scale="1.0" /> <FontFamilyMap Unicode="0000-052F" Target="Comic Sans MS, Verdana" Scale="4.0" /> </FontFamily.FamilyMaps> </FontFamily> </Grid.Resources> <TextBlock Text="Hello World!" FontFamily="{StaticResource TheFont}" HorizontalAlignment="Center" VerticalAlignment="Center"/> </Grid> UnicodeRange.patch (6,408 bytes)
Index: Drawing/VGL/Inc/NsDrawing/VGLFontFace.h =================================================================== --- Drawing/VGL/Inc/NsDrawing/VGLFontFace.h (revision 12570) +++ Drawing/VGL/Inc/NsDrawing/VGLFontFace.h (working copy) @@ -58,6 +58,7 @@ uint32_t SpaceIndex() const { NS_ASSERT(mHandle); return mSpaceIndex; } ArrayRef<uint32_t> Palette() const { NS_ASSERT(mHandle); return mPalette; } + ArrayRef<UnicodeRange> Ranges() const { NS_ASSERT(mHandle); return mRanges; } private: void LoadInternal(); @@ -86,6 +87,7 @@ float mEllipsisWidth; Vector<uint32_t> mPalette; + Vector<UnicodeRange> mRanges; }; NS_WARNING_POP Index: Drawing/VGL/Src/HarfBuzz.cpp =================================================================== --- Drawing/VGL/Src/HarfBuzz.cpp (revision 12570) +++ Drawing/VGL/Src/HarfBuzz.cpp (working copy) @@ -224,12 +224,54 @@ } //////////////////////////////////////////////////////////////////////////////////////////////////// +static bool InRange(uint32_t ch, const UnicodeRange* ranges, uint32_t numRanges) +{ + for (uint32_t i = 0; i < numRanges; i++) + { + const UnicodeRange& r = ranges[i]; + + if (ch - r.fist <= r.size) + { + return true; + } + } + + return false; +} + +static thread_local const UnicodeRange* gUnicodeRanges; +static thread_local uint32_t gUnicodeRangesSize; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +static hb_bool_t GetGlyphInRange(hb_font_t* font, void* data, hb_codepoint_t unicode, + hb_codepoint_t* glyph, void *user) +{ + uint32_t numRanges = gUnicodeRangesSize; + + if (numRanges == 0 || InRange(unicode, gUnicodeRanges, numRanges)) + { + return hb_ot_get_nominal_glyph(font, data, unicode, glyph, user); + } + + return false; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// void* HB::FontCreate(void* blob, uint32_t index) { NS_ASSERT(blob != nullptr); hb_face_t* face = hb_face_create((hb_blob_t*)blob, index); - hb_font_t* font = hb_font_create(face); + hb_font_t* parent = hb_font_create(face); hb_face_destroy(face); + + hb_font_t* font = hb_font_create_sub_font(parent); + hb_font_destroy(parent); + + hb_font_funcs_t* funcs = hb_font_funcs_create(); + hb_font_funcs_set_nominal_glyph_func(funcs, GetGlyphInRange, nullptr, nullptr); + hb_font_set_funcs(font, funcs, &face->table, nullptr); + hb_font_funcs_destroy(funcs); + return font; } @@ -965,12 +1007,18 @@ } //////////////////////////////////////////////////////////////////////////////////////////////////// -void HB::Shape(void* font, void* buffer, const FontFeatures& features) +void HB::Shape(void* font, void* buffer, const FontFeatures& features, const UnicodeRange* ranges, + uint32_t numRanges) { Vector<hb_feature_t, 16> v; FillFeatures(features, v); + gUnicodeRanges = ranges; + gUnicodeRangesSize = numRanges; + hb_shape((hb_font_t*)font, (hb_buffer_t*)buffer, v.Data(), v.Size()); + + gUnicodeRangesSize = 0; } //////////////////////////////////////////////////////////////////////////////////////////////////// Index: Drawing/VGL/Src/HarfBuzz.h =================================================================== --- Drawing/VGL/Src/HarfBuzz.h (revision 12570) +++ Drawing/VGL/Src/HarfBuzz.h (working copy) @@ -18,6 +18,7 @@ class Stream; struct FontFeatures; +struct UnicodeRange; enum FontWeight : int32_t; enum FontStyle : int32_t; enum FontStretch : int32_t; @@ -76,7 +77,8 @@ uint32_t offset, uint32_t length, uint32_t script, bool rtl, void* language); static void BufferDestroy(void* buffer); - static void Shape(void* font, void* buffer, const FontFeatures& features); + static void Shape(void* font, void* buffer, const FontFeatures& features, + const UnicodeRange* ranges, uint32_t numRanges); static uint32_t BufferGetGlyphCount(void* buffer); static void BufferGetGlyph(void* buffer, uint32_t index, uint32_t& glyph, uint32_t& cluster, Index: Drawing/VGL/Src/ShapingFull.h =================================================================== --- Drawing/VGL/Src/ShapingFull.h (revision 12570) +++ Drawing/VGL/Src/ShapingFull.h (working copy) @@ -541,10 +541,12 @@ HB::BufferSetContent(buffer, chars, numChars, start, len, script, rtl, lang); - properties->faces[faceIndex]->Load(); - void* font = properties->faces[faceIndex]->Handle(); - HB::Shape(font, buffer, properties->features); + VGLFontFace* face = properties->faces[faceIndex]; + face->Load(); + ArrayRef<UnicodeRange> ranges = face->Ranges(); + HB::Shape(face->Handle(), buffer, properties->features, ranges.Data(), ranges.Size()); + uint32_t numGlyphs = HB::BufferGetGlyphCount(buffer); // New glyphs replace the [replacePos, replacePos + replaceSize] range Index: Drawing/VGL/Src/VGLFontFace.cpp =================================================================== --- Drawing/VGL/Src/VGLFontFace.cpp (revision 12570) +++ Drawing/VGL/Src/VGLFontFace.cpp (working copy) @@ -59,6 +59,17 @@ VGLFontFace::VGLFontFace(const char* uri, Stream* stream, uint32_t index): mUriHash(StrCaseHash(uri)), mStream(stream), mIndex(index), mHandle(0) { + //if (StrEquals(uri, "#Aero Matics")) + //{ + // { + // UnicodeRange range{ 'H', 1 }; + // mRanges.PushBack(range); + // } + // { + // UnicodeRange range{ 'e', 1 }; + // mRanges.PushBack(range); + // } + //} } //////////////////////////////////////////////////////////////////////////////////////////////////// Index: Gui/Providers/Include/NsGui/FontProperties.h =================================================================== --- Gui/Providers/Include/NsGui/FontProperties.h (revision 12570) +++ Gui/Providers/Include/NsGui/FontProperties.h (working copy) @@ -61,6 +61,12 @@ FontStretch_UltraExpanded = 9 }; +struct UnicodeRange +{ + uint32_t fist; + uint32_t size; +}; + } NS_DECLARE_REFLECTION_ENUM_EXPORT(NS_GUI_PROVIDERS_API, Noesis::FontWeight) |
|
Good afternoon It’s good that I came across this ticket before creating a post requesting this function)) I want to make a composite font in which “extra” glyphs will be removed and number glyphs from another font will be added. It would also be a good idea to experiment with glyphs for different languages. How soon will FontFamilyMap support be added? | |
After the first releases of the Studio we will find time to work on this feature. | |
Date Modified | Username | Field | Change |
---|---|---|---|
2022-07-14 11:45 | steveh | New Issue | |
2022-07-14 11:52 | steveh | Description Updated | |
2022-07-14 16:27 | jsantos | Assigned To | => jsantos |
2022-07-14 16:27 | jsantos | Status | new => assigned |
2022-07-14 16:27 | jsantos | Target Version | => 3.2.0 |
2022-07-14 16:30 | jsantos | Note Added: 0008018 | |
2022-09-21 14:01 | jsantos | Note Added: 0008077 | |
2022-09-21 14:01 | jsantos | Status | assigned => feedback |
2022-09-21 14:51 | steveh | Note Added: 0008078 | |
2022-09-21 14:51 | steveh | Status | feedback => assigned |
2022-09-23 10:13 | jsantos | Note Added: 0008079 | |
2022-09-23 10:14 | jsantos | Status | assigned => feedback |
2022-09-23 10:57 | steveh | Note Added: 0008080 | |
2022-09-23 10:57 | steveh | Status | feedback => assigned |
2022-09-23 11:18 | jsantos | Note Added: 0008081 | |
2023-01-19 14:22 | jsantos | Note Added: 0008227 | |
2023-01-19 14:22 | jsantos | Status | assigned => feedback |
2023-01-19 15:22 | steveh | Note Added: 0008229 | |
2023-01-19 15:22 | steveh | Status | feedback => assigned |
2023-03-27 12:14 | jsantos | Target Version | 3.2.0 => 3.2 |
2023-06-28 14:32 | jsantos | Note Added: 0008570 | |
2023-06-28 14:32 | jsantos | File Added: UnicodeRange.patch | |
2023-06-28 14:32 | jsantos | Status | assigned => feedback |
2023-06-28 14:33 | jsantos | Note Edited: 0008570 | |
2023-06-28 14:33 | jsantos | Note Edited: 0008570 | |
2024-02-25 06:52 | Demond | Note Added: 0009237 | |
2024-02-26 11:12 | jsantos | Note Added: 0009238 |