1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00

Typeface: Implement platform typefaces using Harfbuzz hb_font_t

This commit is contained in:
reuk 2024-02-26 16:42:32 +00:00
parent 080ac6e7e7
commit 0d2e34f34c
No known key found for this signature in database
GPG key ID: FCB43929F012EE5C
56 changed files with 8359 additions and 2985 deletions

View file

@ -4,6 +4,30 @@
## Change
CustomTypeface has been removed.
**Possible Issues**
Code that interacts with CustomTypeface will fail to compile.
**Workaround**
There is currently no workaround. If you were using CustomTypeface to
implement typeface fallback, there is a new API,
Font::findSuitableFontForText, that you can use to locate fonts capable
of rendering given strings.
**Rationale**
The CustomTypeface class is difficult/impossible to support with the new
HarfBuzz Typeface implementation. New support for automatic font fallback
will be introduced in JUCE 8, and this will obviate much of the need for
CustomTypeface.
>>>>>>> 94454123d6 (Typeface: Implement platform typefaces using Harfbuzz hb_font_t)
## Change
The Android implementations of Typeface::getStringWidth(), getGlyphPositions(),
and getEdgeTableForGlyph() have been updated to return correctly-normalised
results. The effect of this change is to change (in practice, slightly reduce)

View file

@ -1814,8 +1814,6 @@ add_library( ${BINARY_NAME}
"../../../../../modules/juce_graphics/fonts/harfbuzz/hb.hh"
"../../../../../modules/juce_graphics/fonts/juce_AttributedString.cpp"
"../../../../../modules/juce_graphics/fonts/juce_AttributedString.h"
"../../../../../modules/juce_graphics/fonts/juce_CustomTypeface.cpp"
"../../../../../modules/juce_graphics/fonts/juce_CustomTypeface.h"
"../../../../../modules/juce_graphics/fonts/juce_Font.cpp"
"../../../../../modules/juce_graphics/fonts/juce_Font.h"
"../../../../../modules/juce_graphics/fonts/juce_FunctionPointerDestructor.h"
@ -1826,6 +1824,7 @@ add_library( ${BINARY_NAME}
"../../../../../modules/juce_graphics/fonts/juce_TextLayout.h"
"../../../../../modules/juce_graphics/fonts/juce_Typeface.cpp"
"../../../../../modules/juce_graphics/fonts/juce_Typeface.h"
"../../../../../modules/juce_graphics/fonts/juce_TypefaceTestData.cpp"
"../../../../../modules/juce_graphics/geometry/juce_AffineTransform.cpp"
"../../../../../modules/juce_graphics/geometry/juce_AffineTransform.h"
"../../../../../modules/juce_graphics/geometry/juce_BorderSize.h"
@ -1948,7 +1947,6 @@ add_library( ${BINARY_NAME}
"../../../../../modules/juce_graphics/native/juce_Fonts_freetype.cpp"
"../../../../../modules/juce_graphics/native/juce_Fonts_linux.cpp"
"../../../../../modules/juce_graphics/native/juce_Fonts_mac.mm"
"../../../../../modules/juce_graphics/native/juce_Fonts_windows.cpp"
"../../../../../modules/juce_graphics/native/juce_GraphicsContext_android.cpp"
"../../../../../modules/juce_graphics/native/juce_IconHelpers_android.cpp"
"../../../../../modules/juce_graphics/native/juce_IconHelpers_linux.cpp"
@ -4278,8 +4276,6 @@ set_source_files_properties(
"../../../../../modules/juce_graphics/fonts/harfbuzz/hb.hh"
"../../../../../modules/juce_graphics/fonts/juce_AttributedString.cpp"
"../../../../../modules/juce_graphics/fonts/juce_AttributedString.h"
"../../../../../modules/juce_graphics/fonts/juce_CustomTypeface.cpp"
"../../../../../modules/juce_graphics/fonts/juce_CustomTypeface.h"
"../../../../../modules/juce_graphics/fonts/juce_Font.cpp"
"../../../../../modules/juce_graphics/fonts/juce_Font.h"
"../../../../../modules/juce_graphics/fonts/juce_FunctionPointerDestructor.h"
@ -4290,6 +4286,7 @@ set_source_files_properties(
"../../../../../modules/juce_graphics/fonts/juce_TextLayout.h"
"../../../../../modules/juce_graphics/fonts/juce_Typeface.cpp"
"../../../../../modules/juce_graphics/fonts/juce_Typeface.h"
"../../../../../modules/juce_graphics/fonts/juce_TypefaceTestData.cpp"
"../../../../../modules/juce_graphics/geometry/juce_AffineTransform.cpp"
"../../../../../modules/juce_graphics/geometry/juce_AffineTransform.h"
"../../../../../modules/juce_graphics/geometry/juce_BorderSize.h"
@ -4412,7 +4409,6 @@ set_source_files_properties(
"../../../../../modules/juce_graphics/native/juce_Fonts_freetype.cpp"
"../../../../../modules/juce_graphics/native/juce_Fonts_linux.cpp"
"../../../../../modules/juce_graphics/native/juce_Fonts_mac.mm"
"../../../../../modules/juce_graphics/native/juce_Fonts_windows.cpp"
"../../../../../modules/juce_graphics/native/juce_GraphicsContext_android.cpp"
"../../../../../modules/juce_graphics/native/juce_IconHelpers_android.cpp"
"../../../../../modules/juce_graphics/native/juce_IconHelpers_linux.cpp"

View file

@ -2096,9 +2096,6 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_AttributedString.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_CustomTypeface.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_Font.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
@ -2111,6 +2108,9 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_Typeface.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_TypefaceTestData.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\geometry\juce_AffineTransform.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
@ -2351,9 +2351,6 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_Fonts_linux.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_Fonts_windows.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_GraphicsContext_android.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
@ -4131,7 +4128,6 @@
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\harfbuzz\hb.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\harfbuzz\hb.hh"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_AttributedString.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_CustomTypeface.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_Font.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_FunctionPointerDestructor.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_GlyphArrangement.h"/>

View file

@ -2839,9 +2839,6 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_AttributedString.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_CustomTypeface.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_Font.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
@ -2854,6 +2851,9 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_Typeface.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_TypefaceTestData.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\geometry\juce_AffineTransform.cpp">
<Filter>JUCE Modules\juce_graphics\geometry</Filter>
</ClCompile>
@ -3100,9 +3100,6 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_Fonts_mac.mm">
<Filter>JUCE Modules\juce_graphics\native</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_Fonts_windows.cpp">
<Filter>JUCE Modules\juce_graphics\native</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_GraphicsContext_android.cpp">
<Filter>JUCE Modules\juce_graphics\native</Filter>
</ClCompile>
@ -7125,9 +7122,6 @@
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_AttributedString.h">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClInclude>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_CustomTypeface.h">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClInclude>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_Font.h">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClInclude>

View file

@ -2096,9 +2096,6 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_AttributedString.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_CustomTypeface.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_Font.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
@ -2111,6 +2108,9 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_Typeface.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_TypefaceTestData.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\geometry\juce_AffineTransform.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
@ -2351,9 +2351,6 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_Fonts_linux.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_Fonts_windows.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_GraphicsContext_android.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
@ -4131,7 +4128,6 @@
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\harfbuzz\hb.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\harfbuzz\hb.hh"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_AttributedString.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_CustomTypeface.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_Font.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_FunctionPointerDestructor.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_GlyphArrangement.h"/>

View file

@ -2839,9 +2839,6 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_AttributedString.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_CustomTypeface.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_Font.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
@ -2854,6 +2851,9 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_Typeface.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_TypefaceTestData.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\geometry\juce_AffineTransform.cpp">
<Filter>JUCE Modules\juce_graphics\geometry</Filter>
</ClCompile>
@ -3100,9 +3100,6 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_Fonts_mac.mm">
<Filter>JUCE Modules\juce_graphics\native</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_Fonts_windows.cpp">
<Filter>JUCE Modules\juce_graphics\native</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_GraphicsContext_android.cpp">
<Filter>JUCE Modules\juce_graphics\native</Filter>
</ClCompile>
@ -7125,9 +7122,6 @@
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_AttributedString.h">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClInclude>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_CustomTypeface.h">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClInclude>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_Font.h">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClInclude>

View file

@ -2096,9 +2096,6 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_AttributedString.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_CustomTypeface.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_Font.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
@ -2111,6 +2108,9 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_Typeface.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_TypefaceTestData.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\geometry\juce_AffineTransform.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
@ -2351,9 +2351,6 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_Fonts_linux.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_Fonts_windows.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_GraphicsContext_android.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
@ -4131,7 +4128,6 @@
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\harfbuzz\hb.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\harfbuzz\hb.hh"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_AttributedString.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_CustomTypeface.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_Font.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_FunctionPointerDestructor.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_GlyphArrangement.h"/>

View file

@ -2839,9 +2839,6 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_AttributedString.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_CustomTypeface.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_Font.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
@ -2854,6 +2851,9 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_Typeface.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_TypefaceTestData.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\geometry\juce_AffineTransform.cpp">
<Filter>JUCE Modules\juce_graphics\geometry</Filter>
</ClCompile>
@ -3100,9 +3100,6 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_Fonts_mac.mm">
<Filter>JUCE Modules\juce_graphics\native</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_Fonts_windows.cpp">
<Filter>JUCE Modules\juce_graphics\native</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_GraphicsContext_android.cpp">
<Filter>JUCE Modules\juce_graphics\native</Filter>
</ClCompile>
@ -7125,9 +7122,6 @@
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_AttributedString.h">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClInclude>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_CustomTypeface.h">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClInclude>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_Font.h">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClInclude>

View file

@ -1576,8 +1576,6 @@ add_library( ${BINARY_NAME}
"../../../../../modules/juce_graphics/fonts/harfbuzz/hb.hh"
"../../../../../modules/juce_graphics/fonts/juce_AttributedString.cpp"
"../../../../../modules/juce_graphics/fonts/juce_AttributedString.h"
"../../../../../modules/juce_graphics/fonts/juce_CustomTypeface.cpp"
"../../../../../modules/juce_graphics/fonts/juce_CustomTypeface.h"
"../../../../../modules/juce_graphics/fonts/juce_Font.cpp"
"../../../../../modules/juce_graphics/fonts/juce_Font.h"
"../../../../../modules/juce_graphics/fonts/juce_FunctionPointerDestructor.h"
@ -1588,6 +1586,7 @@ add_library( ${BINARY_NAME}
"../../../../../modules/juce_graphics/fonts/juce_TextLayout.h"
"../../../../../modules/juce_graphics/fonts/juce_Typeface.cpp"
"../../../../../modules/juce_graphics/fonts/juce_Typeface.h"
"../../../../../modules/juce_graphics/fonts/juce_TypefaceTestData.cpp"
"../../../../../modules/juce_graphics/geometry/juce_AffineTransform.cpp"
"../../../../../modules/juce_graphics/geometry/juce_AffineTransform.h"
"../../../../../modules/juce_graphics/geometry/juce_BorderSize.h"
@ -1710,7 +1709,6 @@ add_library( ${BINARY_NAME}
"../../../../../modules/juce_graphics/native/juce_Fonts_freetype.cpp"
"../../../../../modules/juce_graphics/native/juce_Fonts_linux.cpp"
"../../../../../modules/juce_graphics/native/juce_Fonts_mac.mm"
"../../../../../modules/juce_graphics/native/juce_Fonts_windows.cpp"
"../../../../../modules/juce_graphics/native/juce_GraphicsContext_android.cpp"
"../../../../../modules/juce_graphics/native/juce_IconHelpers_android.cpp"
"../../../../../modules/juce_graphics/native/juce_IconHelpers_linux.cpp"
@ -3722,8 +3720,6 @@ set_source_files_properties(
"../../../../../modules/juce_graphics/fonts/harfbuzz/hb.hh"
"../../../../../modules/juce_graphics/fonts/juce_AttributedString.cpp"
"../../../../../modules/juce_graphics/fonts/juce_AttributedString.h"
"../../../../../modules/juce_graphics/fonts/juce_CustomTypeface.cpp"
"../../../../../modules/juce_graphics/fonts/juce_CustomTypeface.h"
"../../../../../modules/juce_graphics/fonts/juce_Font.cpp"
"../../../../../modules/juce_graphics/fonts/juce_Font.h"
"../../../../../modules/juce_graphics/fonts/juce_FunctionPointerDestructor.h"
@ -3734,6 +3730,7 @@ set_source_files_properties(
"../../../../../modules/juce_graphics/fonts/juce_TextLayout.h"
"../../../../../modules/juce_graphics/fonts/juce_Typeface.cpp"
"../../../../../modules/juce_graphics/fonts/juce_Typeface.h"
"../../../../../modules/juce_graphics/fonts/juce_TypefaceTestData.cpp"
"../../../../../modules/juce_graphics/geometry/juce_AffineTransform.cpp"
"../../../../../modules/juce_graphics/geometry/juce_AffineTransform.h"
"../../../../../modules/juce_graphics/geometry/juce_BorderSize.h"
@ -3856,7 +3853,6 @@ set_source_files_properties(
"../../../../../modules/juce_graphics/native/juce_Fonts_freetype.cpp"
"../../../../../modules/juce_graphics/native/juce_Fonts_linux.cpp"
"../../../../../modules/juce_graphics/native/juce_Fonts_mac.mm"
"../../../../../modules/juce_graphics/native/juce_Fonts_windows.cpp"
"../../../../../modules/juce_graphics/native/juce_GraphicsContext_android.cpp"
"../../../../../modules/juce_graphics/native/juce_IconHelpers_android.cpp"
"../../../../../modules/juce_graphics/native/juce_IconHelpers_linux.cpp"

View file

@ -1789,9 +1789,6 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_AttributedString.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_CustomTypeface.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_Font.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
@ -1804,6 +1801,9 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_Typeface.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_TypefaceTestData.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\geometry\juce_AffineTransform.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
@ -2044,9 +2044,6 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_Fonts_linux.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_Fonts_windows.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_GraphicsContext_android.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
@ -3602,7 +3599,6 @@
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\harfbuzz\hb.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\harfbuzz\hb.hh"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_AttributedString.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_CustomTypeface.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_Font.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_FunctionPointerDestructor.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_GlyphArrangement.h"/>

View file

@ -2383,9 +2383,6 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_AttributedString.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_CustomTypeface.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_Font.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
@ -2398,6 +2395,9 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_Typeface.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_TypefaceTestData.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\geometry\juce_AffineTransform.cpp">
<Filter>JUCE Modules\juce_graphics\geometry</Filter>
</ClCompile>
@ -2644,9 +2644,6 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_Fonts_mac.mm">
<Filter>JUCE Modules\juce_graphics\native</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_Fonts_windows.cpp">
<Filter>JUCE Modules\juce_graphics\native</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_GraphicsContext_android.cpp">
<Filter>JUCE Modules\juce_graphics\native</Filter>
</ClCompile>
@ -6180,9 +6177,6 @@
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_AttributedString.h">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClInclude>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_CustomTypeface.h">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClInclude>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_Font.h">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClInclude>

View file

@ -1706,8 +1706,6 @@ add_library( ${BINARY_NAME}
"../../../../../modules/juce_graphics/fonts/harfbuzz/hb.hh"
"../../../../../modules/juce_graphics/fonts/juce_AttributedString.cpp"
"../../../../../modules/juce_graphics/fonts/juce_AttributedString.h"
"../../../../../modules/juce_graphics/fonts/juce_CustomTypeface.cpp"
"../../../../../modules/juce_graphics/fonts/juce_CustomTypeface.h"
"../../../../../modules/juce_graphics/fonts/juce_Font.cpp"
"../../../../../modules/juce_graphics/fonts/juce_Font.h"
"../../../../../modules/juce_graphics/fonts/juce_FunctionPointerDestructor.h"
@ -1718,6 +1716,7 @@ add_library( ${BINARY_NAME}
"../../../../../modules/juce_graphics/fonts/juce_TextLayout.h"
"../../../../../modules/juce_graphics/fonts/juce_Typeface.cpp"
"../../../../../modules/juce_graphics/fonts/juce_Typeface.h"
"../../../../../modules/juce_graphics/fonts/juce_TypefaceTestData.cpp"
"../../../../../modules/juce_graphics/geometry/juce_AffineTransform.cpp"
"../../../../../modules/juce_graphics/geometry/juce_AffineTransform.h"
"../../../../../modules/juce_graphics/geometry/juce_BorderSize.h"
@ -1840,7 +1839,6 @@ add_library( ${BINARY_NAME}
"../../../../../modules/juce_graphics/native/juce_Fonts_freetype.cpp"
"../../../../../modules/juce_graphics/native/juce_Fonts_linux.cpp"
"../../../../../modules/juce_graphics/native/juce_Fonts_mac.mm"
"../../../../../modules/juce_graphics/native/juce_Fonts_windows.cpp"
"../../../../../modules/juce_graphics/native/juce_GraphicsContext_android.cpp"
"../../../../../modules/juce_graphics/native/juce_IconHelpers_android.cpp"
"../../../../../modules/juce_graphics/native/juce_IconHelpers_linux.cpp"
@ -4005,8 +4003,6 @@ set_source_files_properties(
"../../../../../modules/juce_graphics/fonts/harfbuzz/hb.hh"
"../../../../../modules/juce_graphics/fonts/juce_AttributedString.cpp"
"../../../../../modules/juce_graphics/fonts/juce_AttributedString.h"
"../../../../../modules/juce_graphics/fonts/juce_CustomTypeface.cpp"
"../../../../../modules/juce_graphics/fonts/juce_CustomTypeface.h"
"../../../../../modules/juce_graphics/fonts/juce_Font.cpp"
"../../../../../modules/juce_graphics/fonts/juce_Font.h"
"../../../../../modules/juce_graphics/fonts/juce_FunctionPointerDestructor.h"
@ -4017,6 +4013,7 @@ set_source_files_properties(
"../../../../../modules/juce_graphics/fonts/juce_TextLayout.h"
"../../../../../modules/juce_graphics/fonts/juce_Typeface.cpp"
"../../../../../modules/juce_graphics/fonts/juce_Typeface.h"
"../../../../../modules/juce_graphics/fonts/juce_TypefaceTestData.cpp"
"../../../../../modules/juce_graphics/geometry/juce_AffineTransform.cpp"
"../../../../../modules/juce_graphics/geometry/juce_AffineTransform.h"
"../../../../../modules/juce_graphics/geometry/juce_BorderSize.h"
@ -4139,7 +4136,6 @@ set_source_files_properties(
"../../../../../modules/juce_graphics/native/juce_Fonts_freetype.cpp"
"../../../../../modules/juce_graphics/native/juce_Fonts_linux.cpp"
"../../../../../modules/juce_graphics/native/juce_Fonts_mac.mm"
"../../../../../modules/juce_graphics/native/juce_Fonts_windows.cpp"
"../../../../../modules/juce_graphics/native/juce_GraphicsContext_android.cpp"
"../../../../../modules/juce_graphics/native/juce_IconHelpers_android.cpp"
"../../../../../modules/juce_graphics/native/juce_IconHelpers_linux.cpp"

View file

@ -1923,9 +1923,6 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_AttributedString.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_CustomTypeface.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_Font.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
@ -1938,6 +1935,9 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_Typeface.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_TypefaceTestData.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\geometry\juce_AffineTransform.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
@ -2178,9 +2178,6 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_Fonts_linux.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_Fonts_windows.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_GraphicsContext_android.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
@ -3835,7 +3832,6 @@
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\harfbuzz\hb.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\harfbuzz\hb.hh"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_AttributedString.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_CustomTypeface.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_Font.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_FunctionPointerDestructor.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_GlyphArrangement.h"/>

View file

@ -2590,9 +2590,6 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_AttributedString.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_CustomTypeface.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_Font.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
@ -2605,6 +2602,9 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_Typeface.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_TypefaceTestData.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\geometry\juce_AffineTransform.cpp">
<Filter>JUCE Modules\juce_graphics\geometry</Filter>
</ClCompile>
@ -2851,9 +2851,6 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_Fonts_mac.mm">
<Filter>JUCE Modules\juce_graphics\native</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_Fonts_windows.cpp">
<Filter>JUCE Modules\juce_graphics\native</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_GraphicsContext_android.cpp">
<Filter>JUCE Modules\juce_graphics\native</Filter>
</ClCompile>
@ -6615,9 +6612,6 @@
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_AttributedString.h">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClInclude>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_CustomTypeface.h">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClInclude>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_Font.h">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClInclude>

View file

@ -1923,9 +1923,6 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_AttributedString.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_CustomTypeface.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_Font.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
@ -1938,6 +1935,9 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_Typeface.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_TypefaceTestData.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\geometry\juce_AffineTransform.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
@ -2178,9 +2178,6 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_Fonts_linux.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_Fonts_windows.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_GraphicsContext_android.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
@ -3835,7 +3832,6 @@
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\harfbuzz\hb.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\harfbuzz\hb.hh"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_AttributedString.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_CustomTypeface.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_Font.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_FunctionPointerDestructor.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_GlyphArrangement.h"/>

View file

@ -2590,9 +2590,6 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_AttributedString.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_CustomTypeface.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_Font.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
@ -2605,6 +2602,9 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_Typeface.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_TypefaceTestData.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\geometry\juce_AffineTransform.cpp">
<Filter>JUCE Modules\juce_graphics\geometry</Filter>
</ClCompile>
@ -2851,9 +2851,6 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_Fonts_mac.mm">
<Filter>JUCE Modules\juce_graphics\native</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_Fonts_windows.cpp">
<Filter>JUCE Modules\juce_graphics\native</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_GraphicsContext_android.cpp">
<Filter>JUCE Modules\juce_graphics\native</Filter>
</ClCompile>
@ -6615,9 +6612,6 @@
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_AttributedString.h">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClInclude>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_CustomTypeface.h">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClInclude>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_Font.h">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClInclude>

View file

@ -1923,9 +1923,6 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_AttributedString.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_CustomTypeface.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_Font.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
@ -1938,6 +1935,9 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_Typeface.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_TypefaceTestData.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\geometry\juce_AffineTransform.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
@ -2178,9 +2178,6 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_Fonts_linux.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_Fonts_windows.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_GraphicsContext_android.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
@ -3835,7 +3832,6 @@
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\harfbuzz\hb.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\harfbuzz\hb.hh"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_AttributedString.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_CustomTypeface.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_Font.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_FunctionPointerDestructor.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_GlyphArrangement.h"/>

View file

@ -2590,9 +2590,6 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_AttributedString.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_CustomTypeface.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_Font.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
@ -2605,6 +2602,9 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_Typeface.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_TypefaceTestData.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\geometry\juce_AffineTransform.cpp">
<Filter>JUCE Modules\juce_graphics\geometry</Filter>
</ClCompile>
@ -2851,9 +2851,6 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_Fonts_mac.mm">
<Filter>JUCE Modules\juce_graphics\native</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_Fonts_windows.cpp">
<Filter>JUCE Modules\juce_graphics\native</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_GraphicsContext_android.cpp">
<Filter>JUCE Modules\juce_graphics\native</Filter>
</ClCompile>
@ -6615,9 +6612,6 @@
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_AttributedString.h">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClInclude>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_CustomTypeface.h">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClInclude>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_Font.h">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClInclude>

View file

@ -1595,8 +1595,6 @@ add_library( ${BINARY_NAME}
"../../../../../modules/juce_graphics/fonts/harfbuzz/hb.hh"
"../../../../../modules/juce_graphics/fonts/juce_AttributedString.cpp"
"../../../../../modules/juce_graphics/fonts/juce_AttributedString.h"
"../../../../../modules/juce_graphics/fonts/juce_CustomTypeface.cpp"
"../../../../../modules/juce_graphics/fonts/juce_CustomTypeface.h"
"../../../../../modules/juce_graphics/fonts/juce_Font.cpp"
"../../../../../modules/juce_graphics/fonts/juce_Font.h"
"../../../../../modules/juce_graphics/fonts/juce_FunctionPointerDestructor.h"
@ -1607,6 +1605,7 @@ add_library( ${BINARY_NAME}
"../../../../../modules/juce_graphics/fonts/juce_TextLayout.h"
"../../../../../modules/juce_graphics/fonts/juce_Typeface.cpp"
"../../../../../modules/juce_graphics/fonts/juce_Typeface.h"
"../../../../../modules/juce_graphics/fonts/juce_TypefaceTestData.cpp"
"../../../../../modules/juce_graphics/geometry/juce_AffineTransform.cpp"
"../../../../../modules/juce_graphics/geometry/juce_AffineTransform.h"
"../../../../../modules/juce_graphics/geometry/juce_BorderSize.h"
@ -1729,7 +1728,6 @@ add_library( ${BINARY_NAME}
"../../../../../modules/juce_graphics/native/juce_Fonts_freetype.cpp"
"../../../../../modules/juce_graphics/native/juce_Fonts_linux.cpp"
"../../../../../modules/juce_graphics/native/juce_Fonts_mac.mm"
"../../../../../modules/juce_graphics/native/juce_Fonts_windows.cpp"
"../../../../../modules/juce_graphics/native/juce_GraphicsContext_android.cpp"
"../../../../../modules/juce_graphics/native/juce_IconHelpers_android.cpp"
"../../../../../modules/juce_graphics/native/juce_IconHelpers_linux.cpp"
@ -3821,8 +3819,6 @@ set_source_files_properties(
"../../../../../modules/juce_graphics/fonts/harfbuzz/hb.hh"
"../../../../../modules/juce_graphics/fonts/juce_AttributedString.cpp"
"../../../../../modules/juce_graphics/fonts/juce_AttributedString.h"
"../../../../../modules/juce_graphics/fonts/juce_CustomTypeface.cpp"
"../../../../../modules/juce_graphics/fonts/juce_CustomTypeface.h"
"../../../../../modules/juce_graphics/fonts/juce_Font.cpp"
"../../../../../modules/juce_graphics/fonts/juce_Font.h"
"../../../../../modules/juce_graphics/fonts/juce_FunctionPointerDestructor.h"
@ -3833,6 +3829,7 @@ set_source_files_properties(
"../../../../../modules/juce_graphics/fonts/juce_TextLayout.h"
"../../../../../modules/juce_graphics/fonts/juce_Typeface.cpp"
"../../../../../modules/juce_graphics/fonts/juce_Typeface.h"
"../../../../../modules/juce_graphics/fonts/juce_TypefaceTestData.cpp"
"../../../../../modules/juce_graphics/geometry/juce_AffineTransform.cpp"
"../../../../../modules/juce_graphics/geometry/juce_AffineTransform.h"
"../../../../../modules/juce_graphics/geometry/juce_BorderSize.h"
@ -3955,7 +3952,6 @@ set_source_files_properties(
"../../../../../modules/juce_graphics/native/juce_Fonts_freetype.cpp"
"../../../../../modules/juce_graphics/native/juce_Fonts_linux.cpp"
"../../../../../modules/juce_graphics/native/juce_Fonts_mac.mm"
"../../../../../modules/juce_graphics/native/juce_Fonts_windows.cpp"
"../../../../../modules/juce_graphics/native/juce_GraphicsContext_android.cpp"
"../../../../../modules/juce_graphics/native/juce_IconHelpers_android.cpp"
"../../../../../modules/juce_graphics/native/juce_IconHelpers_linux.cpp"

View file

@ -1810,9 +1810,6 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_AttributedString.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_CustomTypeface.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_Font.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
@ -1825,6 +1822,9 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_Typeface.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_TypefaceTestData.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\geometry\juce_AffineTransform.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
@ -2065,9 +2065,6 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_Fonts_linux.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_Fonts_windows.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_GraphicsContext_android.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
@ -3700,7 +3697,6 @@
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\harfbuzz\hb.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\harfbuzz\hb.hh"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_AttributedString.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_CustomTypeface.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_Font.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_FunctionPointerDestructor.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_GlyphArrangement.h"/>

View file

@ -2437,9 +2437,6 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_AttributedString.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_CustomTypeface.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_Font.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
@ -2452,6 +2449,9 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_Typeface.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_TypefaceTestData.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\geometry\juce_AffineTransform.cpp">
<Filter>JUCE Modules\juce_graphics\geometry</Filter>
</ClCompile>
@ -2698,9 +2698,6 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_Fonts_mac.mm">
<Filter>JUCE Modules\juce_graphics\native</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_Fonts_windows.cpp">
<Filter>JUCE Modules\juce_graphics\native</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_GraphicsContext_android.cpp">
<Filter>JUCE Modules\juce_graphics\native</Filter>
</ClCompile>
@ -6342,9 +6339,6 @@
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_AttributedString.h">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClInclude>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_CustomTypeface.h">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClInclude>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_Font.h">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClInclude>

View file

@ -992,9 +992,6 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_AttributedString.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_CustomTypeface.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_Font.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
@ -1007,6 +1004,9 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_Typeface.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_TypefaceTestData.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\geometry\juce_AffineTransform.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
@ -1247,9 +1247,6 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_Fonts_linux.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_Fonts_windows.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_GraphicsContext_android.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
@ -2517,7 +2514,6 @@
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\harfbuzz\hb.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\harfbuzz\hb.hh"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_AttributedString.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_CustomTypeface.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_Font.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_FunctionPointerDestructor.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_GlyphArrangement.h"/>

View file

@ -1381,9 +1381,6 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_AttributedString.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_CustomTypeface.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_Font.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
@ -1396,6 +1393,9 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_Typeface.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_TypefaceTestData.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\geometry\juce_AffineTransform.cpp">
<Filter>JUCE Modules\juce_graphics\geometry</Filter>
</ClCompile>
@ -1642,9 +1642,6 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_Fonts_mac.mm">
<Filter>JUCE Modules\juce_graphics\native</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_Fonts_windows.cpp">
<Filter>JUCE Modules\juce_graphics\native</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_GraphicsContext_android.cpp">
<Filter>JUCE Modules\juce_graphics\native</Filter>
</ClCompile>
@ -4320,9 +4317,6 @@
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_AttributedString.h">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClInclude>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_CustomTypeface.h">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClInclude>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_Font.h">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClInclude>

View file

@ -992,9 +992,6 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_AttributedString.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_CustomTypeface.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_Font.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
@ -1007,6 +1004,9 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_Typeface.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_TypefaceTestData.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\geometry\juce_AffineTransform.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
@ -1247,9 +1247,6 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_Fonts_linux.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_Fonts_windows.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_GraphicsContext_android.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
@ -2517,7 +2514,6 @@
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\harfbuzz\hb.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\harfbuzz\hb.hh"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_AttributedString.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_CustomTypeface.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_Font.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_FunctionPointerDestructor.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_GlyphArrangement.h"/>

View file

@ -1381,9 +1381,6 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_AttributedString.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_CustomTypeface.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_Font.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
@ -1396,6 +1393,9 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_Typeface.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_TypefaceTestData.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\geometry\juce_AffineTransform.cpp">
<Filter>JUCE Modules\juce_graphics\geometry</Filter>
</ClCompile>
@ -1642,9 +1642,6 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_Fonts_mac.mm">
<Filter>JUCE Modules\juce_graphics\native</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_Fonts_windows.cpp">
<Filter>JUCE Modules\juce_graphics\native</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_GraphicsContext_android.cpp">
<Filter>JUCE Modules\juce_graphics\native</Filter>
</ClCompile>
@ -4320,9 +4317,6 @@
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_AttributedString.h">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClInclude>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_CustomTypeface.h">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClInclude>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_Font.h">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClInclude>

View file

@ -992,9 +992,6 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_AttributedString.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_CustomTypeface.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_Font.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
@ -1007,6 +1004,9 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_Typeface.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_TypefaceTestData.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\geometry\juce_AffineTransform.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
@ -1247,9 +1247,6 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_Fonts_linux.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_Fonts_windows.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_GraphicsContext_android.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
@ -2517,7 +2514,6 @@
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\harfbuzz\hb.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\harfbuzz\hb.hh"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_AttributedString.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_CustomTypeface.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_Font.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_FunctionPointerDestructor.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_GlyphArrangement.h"/>

View file

@ -1381,9 +1381,6 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_AttributedString.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_CustomTypeface.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_Font.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
@ -1396,6 +1393,9 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_Typeface.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_TypefaceTestData.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\geometry\juce_AffineTransform.cpp">
<Filter>JUCE Modules\juce_graphics\geometry</Filter>
</ClCompile>
@ -1642,9 +1642,6 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_Fonts_mac.mm">
<Filter>JUCE Modules\juce_graphics\native</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_Fonts_windows.cpp">
<Filter>JUCE Modules\juce_graphics\native</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_GraphicsContext_android.cpp">
<Filter>JUCE Modules\juce_graphics\native</Filter>
</ClCompile>
@ -4320,9 +4317,6 @@
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_AttributedString.h">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClInclude>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_CustomTypeface.h">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClInclude>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_Font.h">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClInclude>

View file

@ -1931,9 +1931,6 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_AttributedString.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_CustomTypeface.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_Font.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
@ -1946,6 +1943,9 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_Typeface.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_TypefaceTestData.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\geometry\juce_AffineTransform.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
@ -2186,9 +2186,6 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_Fonts_linux.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_Fonts_windows.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_GraphicsContext_android.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
@ -3931,7 +3928,6 @@
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\harfbuzz\hb.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\harfbuzz\hb.hh"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_AttributedString.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_CustomTypeface.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_Font.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_FunctionPointerDestructor.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_GlyphArrangement.h"/>

View file

@ -2611,9 +2611,6 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_AttributedString.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_CustomTypeface.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_Font.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
@ -2626,6 +2623,9 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_Typeface.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_TypefaceTestData.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\geometry\juce_AffineTransform.cpp">
<Filter>JUCE Modules\juce_graphics\geometry</Filter>
</ClCompile>
@ -2872,9 +2872,6 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_Fonts_mac.mm">
<Filter>JUCE Modules\juce_graphics\native</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_Fonts_windows.cpp">
<Filter>JUCE Modules\juce_graphics\native</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_GraphicsContext_android.cpp">
<Filter>JUCE Modules\juce_graphics\native</Filter>
</ClCompile>
@ -6729,9 +6726,6 @@
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_AttributedString.h">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClInclude>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_CustomTypeface.h">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClInclude>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_Font.h">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClInclude>

View file

@ -1931,9 +1931,6 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_AttributedString.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_CustomTypeface.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_Font.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
@ -1946,6 +1943,9 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_Typeface.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_TypefaceTestData.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\geometry\juce_AffineTransform.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
@ -2186,9 +2186,6 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_Fonts_linux.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_Fonts_windows.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_GraphicsContext_android.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
@ -3931,7 +3928,6 @@
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\harfbuzz\hb.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\harfbuzz\hb.hh"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_AttributedString.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_CustomTypeface.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_Font.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_FunctionPointerDestructor.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_GlyphArrangement.h"/>

View file

@ -2611,9 +2611,6 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_AttributedString.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_CustomTypeface.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_Font.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
@ -2626,6 +2623,9 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_Typeface.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_TypefaceTestData.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\geometry\juce_AffineTransform.cpp">
<Filter>JUCE Modules\juce_graphics\geometry</Filter>
</ClCompile>
@ -2872,9 +2872,6 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_Fonts_mac.mm">
<Filter>JUCE Modules\juce_graphics\native</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_Fonts_windows.cpp">
<Filter>JUCE Modules\juce_graphics\native</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_GraphicsContext_android.cpp">
<Filter>JUCE Modules\juce_graphics\native</Filter>
</ClCompile>
@ -6729,9 +6726,6 @@
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_AttributedString.h">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClInclude>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_CustomTypeface.h">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClInclude>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_Font.h">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClInclude>

View file

@ -1931,9 +1931,6 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_AttributedString.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_CustomTypeface.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_Font.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
@ -1946,6 +1943,9 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_Typeface.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_TypefaceTestData.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\geometry\juce_AffineTransform.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
@ -2186,9 +2186,6 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_Fonts_linux.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_Fonts_windows.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_GraphicsContext_android.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
@ -3931,7 +3928,6 @@
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\harfbuzz\hb.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\harfbuzz\hb.hh"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_AttributedString.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_CustomTypeface.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_Font.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_FunctionPointerDestructor.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_GlyphArrangement.h"/>

View file

@ -2611,9 +2611,6 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_AttributedString.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_CustomTypeface.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_Font.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
@ -2626,6 +2623,9 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_Typeface.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_TypefaceTestData.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\geometry\juce_AffineTransform.cpp">
<Filter>JUCE Modules\juce_graphics\geometry</Filter>
</ClCompile>
@ -2872,9 +2872,6 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_Fonts_mac.mm">
<Filter>JUCE Modules\juce_graphics\native</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_Fonts_windows.cpp">
<Filter>JUCE Modules\juce_graphics\native</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_GraphicsContext_android.cpp">
<Filter>JUCE Modules\juce_graphics\native</Filter>
</ClCompile>
@ -6729,9 +6726,6 @@
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_AttributedString.h">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClInclude>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_CustomTypeface.h">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClInclude>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_Font.h">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClInclude>

View file

@ -1809,9 +1809,6 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_AttributedString.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_CustomTypeface.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_Font.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
@ -1824,6 +1821,9 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_Typeface.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_TypefaceTestData.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\geometry\juce_AffineTransform.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
@ -2064,9 +2064,6 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_Fonts_linux.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_Fonts_windows.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_GraphicsContext_android.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
@ -3676,7 +3673,6 @@
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\harfbuzz\hb.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\harfbuzz\hb.hh"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_AttributedString.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_CustomTypeface.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_Font.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_FunctionPointerDestructor.h"/>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_GlyphArrangement.h"/>

View file

@ -2434,9 +2434,6 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_AttributedString.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_CustomTypeface.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_Font.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
@ -2449,6 +2446,9 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_Typeface.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\fonts\juce_TypefaceTestData.cpp">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\geometry\juce_AffineTransform.cpp">
<Filter>JUCE Modules\juce_graphics\geometry</Filter>
</ClCompile>
@ -2695,9 +2695,6 @@
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_Fonts_mac.mm">
<Filter>JUCE Modules\juce_graphics\native</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_Fonts_windows.cpp">
<Filter>JUCE Modules\juce_graphics\native</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\modules\juce_graphics\native\juce_GraphicsContext_android.cpp">
<Filter>JUCE Modules\juce_graphics\native</Filter>
</ClCompile>
@ -6309,9 +6306,6 @@
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_AttributedString.h">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClInclude>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_CustomTypeface.h">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClInclude>
<ClInclude Include="..\..\..\..\modules\juce_graphics\fonts\juce_Font.h">
<Filter>JUCE Modules\juce_graphics\fonts</Filter>
</ClInclude>

View file

@ -430,8 +430,10 @@ struct AndroidDocument::Utils
std::unique_ptr<InputStream> createInputStream() const override
{
auto result = std::make_unique<AndroidContentUriInputStream> (uri);
return result->openedSuccessfully() ? std::move (result) : nullptr;
if (auto opened = AndroidContentUriInputStream::fromUri (uri))
return std::make_unique<AndroidContentUriInputStream> (std::move (*opened));
return {};
}
std::unique_ptr<OutputStream> createOutputStream() const override

View file

@ -584,14 +584,33 @@ struct AndroidStreamHelpers
};
//==============================================================================
struct AndroidContentUriInputStream final : public InputStream
class AndroidInputStreamWrapper final : public InputStream
{
explicit AndroidContentUriInputStream (const GlobalRef& uriIn)
: uri (uriIn),
stream (AndroidStreamHelpers::createStream (uri, AndroidStreamHelpers::StreamKind::input))
{}
public:
explicit AndroidInputStreamWrapper (jobject streamIn)
: stream (LocalRef<jobject> { streamIn })
{
}
~AndroidContentUriInputStream() override
AndroidInputStreamWrapper (AndroidInputStreamWrapper&& other) noexcept
: byteArray (std::exchange (other.byteArray, {})),
stream (std::exchange (other.stream, {})),
pos (std::exchange (other.pos, {})),
exhausted (std::exchange (other.exhausted, {}))
{
}
AndroidInputStreamWrapper (const AndroidInputStreamWrapper&) = delete;
AndroidInputStreamWrapper& operator= (AndroidInputStreamWrapper&& other) noexcept
{
std::swap (*this, other);
return *this;
}
AndroidInputStreamWrapper& operator= (const AndroidInputStreamWrapper&) = delete;
~AndroidInputStreamWrapper() override
{
getEnv()->CallVoidMethod (stream.get(), AndroidInputStream.close);
jniCheckHasExceptionOccurredAndClear();
@ -625,16 +644,9 @@ struct AndroidContentUriInputStream final : public InputStream
return result;
}
bool setPosition (int64 newPos) override
bool setPosition (int64) override
{
if (newPos == pos)
return true;
if (pos < newPos)
return skipImpl (newPos - pos);
AndroidContentUriInputStream (uri).swap (*this);
return skipImpl (newPos);
return false;
}
int64 getPosition() override
@ -642,8 +654,6 @@ struct AndroidContentUriInputStream final : public InputStream
return pos;
}
bool openedSuccessfully() const { return stream != nullptr; }
void skipNextBytes (int64 num) override
{
skipImpl (num);
@ -664,21 +674,107 @@ private:
return skipped == num;
}
auto tie() { return std::tie (uri, byteArray, stream, pos, exhausted); }
void swap (AndroidContentUriInputStream& other) noexcept
{
auto toSwap = other.tie();
tie().swap (toSwap);
}
GlobalRef uri;
CachedByteArray byteArray;
GlobalRef stream;
int64 pos = 0;
bool exhausted = false;
};
std::unique_ptr<InputStream> makeAndroidInputStreamWrapper (jobject stream);
std::unique_ptr<InputStream> makeAndroidInputStreamWrapper (jobject stream)
{
return std::make_unique<AndroidInputStreamWrapper> (stream);
}
//==============================================================================
struct AndroidContentUriInputStream final : public InputStream
{
AndroidContentUriInputStream (AndroidContentUriInputStream&& other) noexcept
: stream (std::move (other.stream)),
uri (std::exchange (other.uri, {}))
{
}
AndroidContentUriInputStream (const AndroidContentUriInputStream&) = delete;
AndroidContentUriInputStream& operator= (AndroidContentUriInputStream&& other) noexcept
{
std::swap (*this, other);
return *this;
}
AndroidContentUriInputStream& operator= (const AndroidContentUriInputStream&) = delete;
int64 getTotalLength() override
{
return stream.getTotalLength();
}
bool isExhausted() override
{
return stream.isExhausted();
}
int read (void* destBuffer, int maxBytesToRead) override
{
return stream.read (destBuffer, maxBytesToRead);
}
bool setPosition (int64 newPos) override
{
if (newPos == getPosition())
return true;
if (getPosition() < newPos)
return skipImpl (newPos - getPosition());
auto newStream = fromUri (uri);
if (! newStream.has_value())
return false;
*this = std::move (*newStream);
return skipImpl (newPos);
}
int64 getPosition() override
{
return stream.getPosition();
}
void skipNextBytes (int64 num) override
{
stream.skipNextBytes (num);
}
static std::optional<AndroidContentUriInputStream> fromUri (const GlobalRef& uriIn)
{
const auto nativeStream = AndroidStreamHelpers::createStream (uriIn, AndroidStreamHelpers::StreamKind::input);
if (nativeStream == nullptr)
return {};
return AndroidContentUriInputStream { AndroidInputStreamWrapper { nativeStream }, uriIn };
}
private:
AndroidContentUriInputStream (AndroidInputStreamWrapper streamIn, const GlobalRef& uriIn)
: stream (std::move (streamIn)),
uri (uriIn)
{
}
bool skipImpl (int64 num)
{
const auto oldPosition = getPosition();
skipNextBytes (num);
return getPosition() == oldPosition + num;
}
AndroidInputStreamWrapper stream;
GlobalRef uri;
};
//==============================================================================
class MediaScannerConnectionClient : public AndroidInterfaceImplementer
{

View file

@ -1,407 +0,0 @@
/*
==============================================================================
This file is part of the JUCE framework.
Copyright (c) Raw Material Software Limited
JUCE is an open source framework subject to commercial or open source
licensing.
By downloading, installing, or using the JUCE framework, or combining the
JUCE framework with any other source code, object code, content or any other
copyrightable work, you agree to the terms of the JUCE End User Licence
Agreement, and all incorporated terms including the JUCE Privacy Policy and
the JUCE Website Terms of Service, as applicable, which will bind you. If you
do not agree to the terms of these agreements, we will not license the JUCE
framework to you, and you must discontinue the installation or download
process and cease use of the JUCE framework.
JUCE End User Licence Agreement: https://juce.com/legal/juce-8-licence/
JUCE Privacy Policy: https://juce.com/juce-privacy-policy
JUCE Website Terms of Service: https://juce.com/juce-website-terms-of-service/
Or:
You may also use this code under the terms of the AGPLv3:
https://www.gnu.org/licenses/agpl-3.0.en.html
THE JUCE FRAMEWORK IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL
WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING WARRANTY OF
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
namespace juce
{
class CustomTypeface::GlyphInfo
{
public:
GlyphInfo (juce_wchar c, const Path& p, float w) noexcept
: character (c), path (p), width (w)
{
}
struct KerningPair
{
juce_wchar character2;
float kerningAmount;
};
void addKerningPair (juce_wchar subsequentCharacter, float extraKerningAmount) noexcept
{
kerningPairs.add ({ subsequentCharacter, extraKerningAmount });
}
float getHorizontalSpacing (juce_wchar subsequentCharacter) const noexcept
{
if (subsequentCharacter != 0)
for (auto& kp : kerningPairs)
if (kp.character2 == subsequentCharacter)
return width + kp.kerningAmount;
return width;
}
const juce_wchar character;
const Path path;
float width;
Array<KerningPair> kerningPairs;
private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GlyphInfo)
};
//==============================================================================
namespace CustomTypefaceHelpers
{
static juce_wchar readChar (InputStream& in)
{
auto n = (uint32) (uint16) in.readShort();
if (n >= 0xd800 && n <= 0xdfff)
{
auto nextWord = (uint32) (uint16) in.readShort();
jassert (nextWord >= 0xdc00); // illegal unicode character!
n = 0x10000 + (((n - 0xd800) << 10) | (nextWord - 0xdc00));
}
return (juce_wchar) n;
}
static void writeChar (OutputStream& out, juce_wchar charToWrite)
{
if (charToWrite >= 0x10000)
{
charToWrite -= 0x10000;
out.writeShort ((short) (uint16) (0xd800 + (charToWrite >> 10)));
out.writeShort ((short) (uint16) (0xdc00 + (charToWrite & 0x3ff)));
}
else
{
out.writeShort ((short) (uint16) charToWrite);
}
}
}
//==============================================================================
CustomTypeface::CustomTypeface()
: Typeface (String(), String())
{
clear();
}
CustomTypeface::CustomTypeface (InputStream& serialisedTypefaceStream)
: Typeface (String(), String())
{
clear();
GZIPDecompressorInputStream gzin (serialisedTypefaceStream);
BufferedInputStream in (gzin, 32768);
name = in.readString();
const bool isBold = in.readBool();
const bool isItalic = in.readBool();
style = FontStyleHelpers::getStyleName (isBold, isItalic);
ascent = in.readFloat();
defaultCharacter = CustomTypefaceHelpers::readChar (in);
auto numChars = in.readInt();
for (int i = 0; i < numChars; ++i)
{
auto c = CustomTypefaceHelpers::readChar (in);
auto width = in.readFloat();
Path p;
p.loadPathFromStream (in);
addGlyph (c, p, width);
}
auto numKerningPairs = in.readInt();
for (int i = 0; i < numKerningPairs; ++i)
{
auto char1 = CustomTypefaceHelpers::readChar (in);
auto char2 = CustomTypefaceHelpers::readChar (in);
addKerningPair (char1, char2, in.readFloat());
}
}
CustomTypeface::~CustomTypeface()
{
}
//==============================================================================
void CustomTypeface::clear()
{
defaultCharacter = 0;
ascent = 1.0f;
style = "Regular";
zeromem (lookupTable, sizeof (lookupTable));
glyphs.clear();
}
void CustomTypeface::setCharacteristics (const String& newName, float newAscent, bool isBold,
bool isItalic, juce_wchar newDefaultCharacter) noexcept
{
name = newName;
defaultCharacter = newDefaultCharacter;
ascent = newAscent;
style = FontStyleHelpers::getStyleName (isBold, isItalic);
}
void CustomTypeface::setCharacteristics (const String& newName, const String& newStyle,
float newAscent, juce_wchar newDefaultCharacter) noexcept
{
name = newName;
style = newStyle;
defaultCharacter = newDefaultCharacter;
ascent = newAscent;
}
void CustomTypeface::addGlyph (juce_wchar character, const Path& path, float width) noexcept
{
// Check that you're not trying to add the same character twice..
jassert (findGlyph (character, false) == nullptr);
if (isPositiveAndBelow ((int) character, numElementsInArray (lookupTable)))
lookupTable [character] = (short) glyphs.size();
glyphs.add (new GlyphInfo (character, path, width));
}
void CustomTypeface::addKerningPair (juce_wchar char1, juce_wchar char2, float extraAmount) noexcept
{
if (! approximatelyEqual (extraAmount, 0.0f))
{
if (auto* g = findGlyph (char1, true))
g->addKerningPair (char2, extraAmount);
else
jassertfalse; // can only add kerning pairs for characters that exist!
}
}
CustomTypeface::GlyphInfo* CustomTypeface::findGlyph (juce_wchar character, bool loadIfNeeded) noexcept
{
if (isPositiveAndBelow ((int) character, numElementsInArray (lookupTable)) && lookupTable [character] > 0)
return glyphs [(int) lookupTable [(int) character]];
for (auto* g : glyphs)
if (g->character == character)
return g;
if (loadIfNeeded && loadGlyphIfPossible (character))
return findGlyph (character, false);
return nullptr;
}
bool CustomTypeface::loadGlyphIfPossible (juce_wchar)
{
return false;
}
void CustomTypeface::addGlyphsFromOtherTypeface (Typeface& typefaceToCopy, juce_wchar characterStartIndex, int numCharacters) noexcept
{
setCharacteristics (name, style, typefaceToCopy.getAscent(), defaultCharacter);
for (int i = 0; i < numCharacters; ++i)
{
auto c = (juce_wchar) (characterStartIndex + static_cast<juce_wchar> (i));
Array<int> glyphIndexes;
Array<float> offsets;
typefaceToCopy.getGlyphPositions (String::charToString (c), glyphIndexes, offsets);
const int glyphIndex = glyphIndexes.getFirst();
if (glyphIndex >= 0 && glyphIndexes.size() > 0)
{
auto glyphWidth = offsets[1];
Path p;
typefaceToCopy.getOutlineForGlyph (glyphIndex, p);
addGlyph (c, p, glyphWidth);
for (int j = glyphs.size() - 1; --j >= 0;)
{
auto char2 = glyphs.getUnchecked (j)->character;
glyphIndexes.clearQuick();
offsets.clearQuick();
typefaceToCopy.getGlyphPositions (String::charToString (c) + String::charToString (char2), glyphIndexes, offsets);
if (offsets.size() > 1)
addKerningPair (c, char2, offsets[1] - glyphWidth);
}
}
}
}
bool CustomTypeface::writeToStream (OutputStream& outputStream)
{
GZIPCompressorOutputStream out (outputStream);
out.writeString (name);
out.writeBool (FontStyleHelpers::isBold (style));
out.writeBool (FontStyleHelpers::isItalic (style));
out.writeFloat (ascent);
CustomTypefaceHelpers::writeChar (out, defaultCharacter);
out.writeInt (glyphs.size());
int numKerningPairs = 0;
for (auto* g : glyphs)
{
CustomTypefaceHelpers::writeChar (out, g->character);
out.writeFloat (g->width);
g->path.writePathToStream (out);
numKerningPairs += g->kerningPairs.size();
}
out.writeInt (numKerningPairs);
for (auto* g : glyphs)
{
for (auto& p : g->kerningPairs)
{
CustomTypefaceHelpers::writeChar (out, g->character);
CustomTypefaceHelpers::writeChar (out, p.character2);
out.writeFloat (p.kerningAmount);
}
}
return true;
}
//==============================================================================
float CustomTypeface::getAscent() const { return ascent; }
float CustomTypeface::getDescent() const { return 1.0f - ascent; }
float CustomTypeface::getHeightToPointsFactor() const { return ascent; }
float CustomTypeface::getStringWidth (const String& text)
{
float x = 0;
for (auto t = text.getCharPointer(); ! t.isEmpty();)
{
auto c = t.getAndAdvance();
if (auto* glyph = findGlyph (c, true))
{
x += glyph->getHorizontalSpacing (*t);
}
else
{
if (auto fallbackTypeface = Typeface::getFallbackTypeface())
if (fallbackTypeface.get() != this)
x += fallbackTypeface->getStringWidth (String::charToString (c));
}
}
return x;
}
void CustomTypeface::getGlyphPositions (const String& text, Array<int>& resultGlyphs, Array<float>& xOffsets)
{
xOffsets.add (0);
float x = 0;
for (auto t = text.getCharPointer(); ! t.isEmpty();)
{
float width = 0.0f;
int glyphChar = 0;
auto c = t.getAndAdvance();
if (auto* glyph = findGlyph (c, true))
{
width = glyph->getHorizontalSpacing (*t);
glyphChar = (int) glyph->character;
}
else
{
auto fallbackTypeface = getFallbackTypeface();
if (fallbackTypeface != nullptr && fallbackTypeface.get() != this)
{
Array<int> subGlyphs;
Array<float> subOffsets;
fallbackTypeface->getGlyphPositions (String::charToString (c), subGlyphs, subOffsets);
if (subGlyphs.size() > 0)
{
glyphChar = subGlyphs.getFirst();
width = subOffsets[1];
}
}
}
x += width;
resultGlyphs.add (glyphChar);
xOffsets.add (x);
}
}
bool CustomTypeface::getOutlineForGlyph (int glyphNumber, Path& path)
{
if (auto* glyph = findGlyph ((juce_wchar) glyphNumber, true))
{
path = glyph->path;
return true;
}
if (auto fallbackTypeface = getFallbackTypeface())
if (fallbackTypeface.get() != this)
return fallbackTypeface->getOutlineForGlyph (glyphNumber, path);
return false;
}
EdgeTable* CustomTypeface::getEdgeTableForGlyph (int glyphNumber, const AffineTransform& transform, float fontHeight)
{
if (auto* glyph = findGlyph ((juce_wchar) glyphNumber, true))
{
if (! glyph->path.isEmpty())
return new EdgeTable (glyph->path.getBoundsTransformed (transform)
.getSmallestIntegerContainer().expanded (1, 0),
glyph->path, transform);
}
else
{
if (auto fallbackTypeface = getFallbackTypeface())
if (fallbackTypeface.get() != this)
return fallbackTypeface->getEdgeTableForGlyph (glyphNumber, transform, fontHeight);
}
return nullptr;
}
} // namespace juce

View file

@ -1,174 +0,0 @@
/*
==============================================================================
This file is part of the JUCE framework.
Copyright (c) Raw Material Software Limited
JUCE is an open source framework subject to commercial or open source
licensing.
By downloading, installing, or using the JUCE framework, or combining the
JUCE framework with any other source code, object code, content or any other
copyrightable work, you agree to the terms of the JUCE End User Licence
Agreement, and all incorporated terms including the JUCE Privacy Policy and
the JUCE Website Terms of Service, as applicable, which will bind you. If you
do not agree to the terms of these agreements, we will not license the JUCE
framework to you, and you must discontinue the installation or download
process and cease use of the JUCE framework.
JUCE End User Licence Agreement: https://juce.com/legal/juce-8-licence/
JUCE Privacy Policy: https://juce.com/juce-privacy-policy
JUCE Website Terms of Service: https://juce.com/juce-website-terms-of-service/
Or:
You may also use this code under the terms of the AGPLv3:
https://www.gnu.org/licenses/agpl-3.0.en.html
THE JUCE FRAMEWORK IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL
WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING WARRANTY OF
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
A typeface that can be populated with custom glyphs.
You can create a CustomTypeface if you need one that contains your own glyphs,
or if you need to load a typeface from a Juce-formatted binary stream.
If you want to create a copy of a native face, you can use addGlyphsFromOtherTypeface()
to copy glyphs into this face.
NOTE! For most people this class is almost certainly NOT the right tool to use!
If what you want to do is to embed a font into your exe, then your best plan is
probably to embed your TTF/OTF font file into your binary using the Projucer,
and then call Typeface::createSystemTypefaceFor() to load it from memory.
@see Typeface, Font
@tags{Graphics}
*/
class JUCE_API CustomTypeface : public Typeface
{
public:
//==============================================================================
/** Creates a new, empty typeface. */
CustomTypeface();
/** Loads a typeface from a previously saved stream.
The stream must have been created by writeToStream().
NOTE! Since this class was written, support was added for loading real font files from
memory, so for most people, using Typeface::createSystemTypefaceFor() to load a real font
is more appropriate than using this class to store it in a proprietary format.
@see writeToStream
*/
explicit CustomTypeface (InputStream& serialisedTypefaceStream);
/** Destructor. */
~CustomTypeface() override;
//==============================================================================
/** Resets this typeface, deleting all its glyphs and settings. */
void clear();
/** Sets the vital statistics for the typeface.
@param fontFamily the typeface's font family
@param ascent the ascent - this is normalised to a height of 1.0 and this is
the value that will be returned by Typeface::getAscent(). The
descent is assumed to be (1.0 - ascent)
@param isBold should be true if the typeface is bold
@param isItalic should be true if the typeface is italic
@param defaultCharacter the character to be used as a replacement if there's
no glyph available for the character that's being drawn
*/
void setCharacteristics (const String& fontFamily, float ascent,
bool isBold, bool isItalic,
juce_wchar defaultCharacter) noexcept;
/** Sets the vital statistics for the typeface.
@param fontFamily the typeface's font family
@param fontStyle the typeface's font style
@param ascent the ascent - this is normalised to a height of 1.0 and this is
the value that will be returned by Typeface::getAscent(). The
descent is assumed to be (1.0 - ascent)
@param defaultCharacter the character to be used as a replacement if there's
no glyph available for the character that's being drawn
*/
void setCharacteristics (const String& fontFamily, const String& fontStyle,
float ascent, juce_wchar defaultCharacter) noexcept;
/** Adds a glyph to the typeface.
The path that is passed in is normalised so that the font height is 1.0, and its
origin is the anchor point of the character on its baseline.
The width is the nominal width of the character, and any extra kerning values that
are specified will be added to this width.
*/
void addGlyph (juce_wchar character, const Path& path, float width) noexcept;
/** Specifies an extra kerning amount to be used between a pair of characters.
The amount will be added to the nominal width of the first character when laying out a string.
*/
void addKerningPair (juce_wchar char1, juce_wchar char2, float extraAmount) noexcept;
/** Adds a range of glyphs from another typeface.
This will attempt to pull in the paths and kerning information from another typeface and
add it to this one.
*/
void addGlyphsFromOtherTypeface (Typeface& typefaceToCopy, juce_wchar characterStartIndex, int numCharacters) noexcept;
/** Saves this typeface as a Juce-formatted font file.
A CustomTypeface can be created to reload the data that is written - see the CustomTypeface
constructor.
NOTE! Since this class was written, support was added for loading real font files from
memory, so for most people, using Typeface::createSystemTypefaceFor() to load a real font
is more appropriate than using this class to store it in a proprietary format.
*/
bool writeToStream (OutputStream& outputStream);
//==============================================================================
// The following methods implement the basic Typeface behaviour.
float getAscent() const override;
float getDescent() const override;
float getHeightToPointsFactor() const override;
float getStringWidth (const String&) override;
void getGlyphPositions (const String&, Array<int>& glyphs, Array<float>& xOffsets) override;
bool getOutlineForGlyph (int glyphNumber, Path&) override;
EdgeTable* getEdgeTableForGlyph (int glyphNumber, const AffineTransform&, float fontHeight) override;
protected:
//==============================================================================
juce_wchar defaultCharacter;
float ascent;
//==============================================================================
/** If a subclass overrides this, it can load glyphs into the font on-demand.
When methods such as getGlyphPositions() or getOutlineForGlyph() are asked for a
particular character and there's no corresponding glyph, they'll call this
method so that a subclass can try to add that glyph, returning true if it
manages to do so.
*/
virtual bool loadGlyphIfPossible (juce_wchar characterNeeded);
private:
//==============================================================================
class GlyphInfo;
OwnedArray<GlyphInfo> glyphs;
short lookupTable[128];
GlyphInfo* findGlyph (juce_wchar character, bool loadIfNeeded) noexcept;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CustomTypeface)
};
} // namespace juce

View file

@ -35,6 +35,12 @@
namespace juce
{
class Font::Native
{
public:
HbFont font{};
};
namespace FontValues
{
static float limitFontHeight (const float height) noexcept
@ -48,12 +54,130 @@ namespace FontValues
String fallbackFontStyle;
}
class HbScale
{
static constexpr float factor = 1 << 16;
public:
HbScale() = delete;
static constexpr hb_position_t juceToHb (float pos)
{
return (hb_position_t) (pos * factor);
}
static constexpr float hbToJuce (hb_position_t pos)
{
return (float) pos / (float) factor;
}
};
using GetTypefaceForFont = Typeface::Ptr (*)(const Font&);
GetTypefaceForFont juce_getTypefaceForFont = nullptr;
float Font::getDefaultMinimumHorizontalScaleFactor() noexcept { return FontValues::minimumHorizontalScale; }
void Font::setDefaultMinimumHorizontalScaleFactor (float newValue) noexcept { FontValues::minimumHorizontalScale = newValue; }
//==============================================================================
#if JUCE_MAC || JUCE_IOS
template <CTFontOrientation orientation>
void getAdvancesForGlyphs (hb_font_t* hbFont, CTFontRef ctFont, Span<const CGGlyph> glyphs, Span<CGSize> advances)
{
jassert (glyphs.size() == advances.size());
int x, y;
hb_font_get_scale (hbFont, &x, &y);
const auto scaleAdjustment = HbScale::hbToJuce (orientation == kCTFontOrientationHorizontal ? x : y) / CTFontGetSize (ctFont);
CTFontGetAdvancesForGlyphs (ctFont, orientation, std::data (glyphs), std::data (advances), (CFIndex) std::size (glyphs));
for (auto& advance : advances)
(orientation == kCTFontOrientationHorizontal ? advance.width : advance.height) *= scaleAdjustment;
}
template <CTFontOrientation orientation>
static auto getAdvanceFn()
{
return [] (hb_font_t* f, void*, hb_codepoint_t glyph, void* voidFontRef) -> hb_position_t
{
auto* fontRef = static_cast<CTFontRef> (voidFontRef);
const CGGlyph glyphs[] { (CGGlyph) glyph };
CGSize advances[std::size (glyphs)]{};
getAdvancesForGlyphs<orientation> (f, fontRef, glyphs, advances);
return HbScale::juceToHb ((float) (orientation == kCTFontOrientationHorizontal ? advances->width : advances->height));
};
}
template <CTFontOrientation orientation>
static auto getAdvancesFn()
{
return [] (hb_font_t* f,
void*,
unsigned int count,
const hb_codepoint_t* firstGlyph,
unsigned int glyphStride,
hb_position_t* firstAdvance,
unsigned int advanceStride,
void* voidFontRef)
{
auto* fontRef = static_cast<CTFontRef> (voidFontRef);
std::vector<CGGlyph> glyphs (count);
for (auto [index, glyph] : enumerate (glyphs))
glyph = (CGGlyph) *addBytesToPointer (firstGlyph, glyphStride * index);
std::vector<CGSize> advances (count);
getAdvancesForGlyphs<orientation> (f, fontRef, glyphs, advances);
for (auto [index, advance] : enumerate (advances))
*addBytesToPointer (firstAdvance, advanceStride * index) = HbScale::juceToHb ((float) (orientation == kCTFontOrientationHorizontal ? advance.width : advance.height));
};
}
/* This function overrides the callbacks that fetch glyph advances for fonts on macOS.
The built-in OpenType glyph metric callbacks that HarfBuzz uses by default for fonts such as
"Apple Color Emoji" don't always return correct advances, resulting in emoji that may overlap
with subsequent characters. This may be to do with ignoring the 'trak' table, but I'm not an
expert, so I'm not sure!
In any case, using CTFontGetAdvancesForGlyphs produces much nicer advances for emoji on Apple
platforms, as long as the CTFont is set to the size that will eventually be rendered.
This might need a bit of testing to make sure that it correctly handles advances for
custom (non-Apple?) fonts.
@param hb a hb_font_t to update with Apple-specific advances
@param fontRef the CTFontRef (normally with a custom point size) that will be queried when computing advances
*/
static void overrideCTFontAdvances (hb_font_t* hb, CTFontRef fontRef)
{
using HbFontFuncs = std::unique_ptr<hb_font_funcs_t, FunctionPointerDestructor<hb_font_funcs_destroy>>;
const HbFontFuncs funcs { hb_font_funcs_create() };
// We pass the CTFontRef as user data to each of these functions.
// We don't pass a custom destructor for the user data, as that will be handled by the custom
// destructor for the hb_font_funcs_t.
hb_font_funcs_set_glyph_h_advance_func (funcs.get(), getAdvanceFn <kCTFontOrientationHorizontal>(), (void*) fontRef, nullptr);
hb_font_funcs_set_glyph_v_advance_func (funcs.get(), getAdvanceFn <kCTFontOrientationVertical>(), (void*) fontRef, nullptr);
hb_font_funcs_set_glyph_h_advances_func (funcs.get(), getAdvancesFn<kCTFontOrientationHorizontal>(), (void*) fontRef, nullptr);
hb_font_funcs_set_glyph_v_advances_func (funcs.get(), getAdvancesFn<kCTFontOrientationVertical>(), (void*) fontRef, nullptr);
// We want to keep a copy of the font around so that all of our custom callbacks can query it,
// so retain it here and release it once the custom functions are no longer in use.
jassert (fontRef != nullptr);
CFRetain (fontRef);
hb_font_set_funcs (hb, funcs.get(), (void*) fontRef, [] (void* ptr)
{
CFRelease ((CTFontRef) ptr);
});
}
#endif
//==============================================================================
class TypefaceCache final : private DeletedAtShutdown
{
@ -102,8 +226,7 @@ public:
if (face.typefaceName == faceName
&& face.typefaceStyle == faceStyle
&& face.typeface != nullptr
&& face.typeface->isSuitableForFont (font))
&& face.typeface != nullptr)
{
face.lastUsageCount = ++counter;
return face.typeface;
@ -287,12 +410,34 @@ public:
return typeface;
}
void checkTypefaceSuitability (const Font& f)
HbFont getFontPtr (const Font& f)
{
const ScopedLock lock (mutex);
if (typeface != nullptr && ! typeface->isSuitableForFont (f))
typeface = nullptr;
if (auto ptr = getTypefacePtr (f))
{
if (HbFont subFont { hb_font_create_sub_font (ptr->getNativeDetails().getFont()) })
{
const auto points = legacyHeightToPoints (ptr, height);
hb_font_set_ptem (subFont.get(), points);
hb_font_set_scale (subFont.get(), HbScale::juceToHb (points * horizontalScale), HbScale::juceToHb (points));
#if JUCE_MAC || JUCE_IOS
overrideCTFontAdvances (subFont.get(), hb_coretext_font_get_ct_font (subFont.get()));
#endif
return subFont;
}
}
return {};
}
void resetTypeface()
{
const ScopedLock lock (mutex);
typeface = nullptr;
}
float getAscent (const Font& f)
@ -373,6 +518,11 @@ public:
}
private:
static float legacyHeightToPoints (Typeface::Ptr p, float h)
{
return h * p->getNativeDetails().getLegacyMetrics().getHeightToPointsFactor();
}
Typeface::Ptr typeface;
String typefaceName, typefaceStyle;
float height = 0.0f, horizontalScale = 1.0f, kerning = 0.0f, ascent = 0.0f;
@ -442,11 +592,6 @@ void Font::dupeInternalIfShared()
font = *new SharedFontInternal (*font);
}
void Font::checkTypefaceSuitability()
{
font->checkTypefaceSuitability (*this);
}
//==============================================================================
struct FontPlaceholderNames
{
@ -581,7 +726,7 @@ void Font::setHeight (float newHeight)
{
dupeInternalIfShared();
font->setHeight (newHeight);
checkTypefaceSuitability();
font->resetTypeface();
}
}
@ -594,7 +739,7 @@ void Font::setHeightWithoutChangingWidth (float newHeight)
dupeInternalIfShared();
font->setHorizontalScale (font->getHorizontalScale() * (font->getHeight() / newHeight));
font->setHeight (newHeight);
checkTypefaceSuitability();
font->resetTypeface();
}
}
@ -642,7 +787,7 @@ void Font::setSizeAndStyle (float newHeight,
font->setHeight (newHeight);
font->setHorizontalScale (newHorizontalScale);
font->setKerning (newKerningAmount);
checkTypefaceSuitability();
font->resetTypeface();
}
setStyleFlags (newStyleFlags);
@ -663,7 +808,7 @@ void Font::setSizeAndStyle (float newHeight,
font->setHeight (newHeight);
font->setHorizontalScale (newHorizontalScale);
font->setKerning (newKerningAmount);
checkTypefaceSuitability();
font->resetTypeface();
}
setTypefaceStyle (newStyle);
@ -680,7 +825,7 @@ void Font::setHorizontalScale (const float scaleFactor)
{
dupeInternalIfShared();
font->setHorizontalScale (scaleFactor);
checkTypefaceSuitability();
font->resetTypeface();
}
float Font::getHorizontalScale() const noexcept
@ -704,7 +849,7 @@ void Font::setExtraKerningFactor (const float extraKerning)
{
dupeInternalIfShared();
font->setKerning (extraKerning);
checkTypefaceSuitability();
font->resetTypeface();
}
Font Font::boldened() const { return withStyle (getStyleFlags() | bold); }
@ -732,7 +877,7 @@ void Font::setUnderline (const bool shouldBeUnderlined)
{
dupeInternalIfShared();
font->setUnderline (shouldBeUnderlined);
checkTypefaceSuitability();
font->resetTypeface();
}
float Font::getAscent() const
@ -837,4 +982,9 @@ Font Font::fromString (const String& fontDescription)
return Font (name, style, height);
}
Font::Native Font::getNativeDetails() const
{
return { font->getFontPtr (*this) };
}
} // namespace juce

View file

@ -478,12 +478,23 @@ public:
*/
static Font fromString (const String& fontDescription);
/** @internal */
class Native;
/** @internal
At the moment, this is a way to get at the hb_font_t that backs this font.
The typeface's hb_font_t is sized appropriately for this font instance.
The font may also have synthetic slant and bold applied.
This is only for internal use!
*/
Native getNativeDetails() const;
private:
//==============================================================================
static bool compare (const Font&, const Font&) noexcept;
void dupeInternalIfShared();
void checkTypefaceSuitability();
float getHeightToPointsFactor() const;
friend struct GraphicsFontHelpers;

View file

@ -35,8 +35,76 @@
namespace juce
{
struct TypefaceLegacyMetrics
{
float ascent{}; // in em units
float descent{}; // in em units
float getScaledAscent() const { return ascent * getHeightToPointsFactor(); }
float getScaledDescent() const { return descent * getHeightToPointsFactor(); }
float getPointsToHeightFactor() const { return ascent + descent; }
float getHeightToPointsFactor() const { return 1.0f / getPointsToHeightFactor(); }
};
using HbFont = std::unique_ptr<hb_font_t, FunctionPointerDestructor<hb_font_destroy>>;
using HbFace = std::unique_ptr<hb_face_t, FunctionPointerDestructor<hb_face_destroy>>;
using HbBuffer = std::unique_ptr<hb_buffer_t, FunctionPointerDestructor<hb_buffer_destroy>>;
class Typeface::Native
{
public:
explicit Native (hb_font_t* fontRef)
: Native (fontRef, findLegacyMetrics (fontRef)) {}
Native (hb_font_t* fontRef, TypefaceLegacyMetrics metrics)
: font (fontRef), legacyMetrics (metrics) {}
auto* getFont() const { return font; }
auto getLegacyMetrics() const { return legacyMetrics; }
private:
static TypefaceLegacyMetrics findLegacyMetrics (hb_font_t* f)
{
hb_font_extents_t extents{};
if (! hb_font_get_h_extents (f, &extents))
{
// jassertfalse;
return { 0.5f, 0.5f };
}
const auto ascent = std::abs ((float) extents.ascender);
const auto descent = std::abs ((float) extents.descender);
const auto upem = (float) hb_face_get_upem (hb_font_get_face (f));
TypefaceLegacyMetrics result;
result.ascent = ascent / upem;
result.descent = descent / upem;
return result;
}
hb_font_t* font = nullptr;
TypefaceLegacyMetrics legacyMetrics;
};
struct FontStyleHelpers
{
static void initSynthetics (hb_font_t* hb, const Font& font)
{
const auto styles = Font::findAllTypefaceStyles (font.getTypefaceName());
if (styles.contains (font.getTypefaceStyle()))
return;
if (font.isItalic())
hb_font_set_synthetic_slant (hb, 0.1f);
if (font.isBold())
hb_font_set_synthetic_bold (hb, 0.04f, 0.04f, true);
}
static const char* getStyleName (const bool bold,
const bool italic) noexcept
{
@ -60,7 +128,7 @@ struct FontStyleHelpers
static bool isItalic (const String& style) noexcept
{
return style.containsWholeWordIgnoreCase ("Italic")
|| style.containsWholeWordIgnoreCase ("Oblique");
|| style.containsWholeWordIgnoreCase ("Oblique");
}
static bool isPlaceholderFamilyName (const String& family)
@ -91,7 +159,7 @@ struct FontStyleHelpers
private:
static String findName (const String& placeholder)
{
const Font f (placeholder, Font::getDefaultStyle(), 15.0f);
const Font f (placeholder, 15.0f, Font::plain);
return Font::getDefaultTypefaceForFont (f)->getName();
}
@ -111,162 +179,223 @@ struct FontStyleHelpers
return isPlaceholderFamilyName (family) ? getConcreteFamilyNameFromPlaceholder (family)
: family;
}
static HbFace getFaceForBlob (Span<const char> bytes, unsigned int index)
{
auto* blob = hb_blob_create_or_fail (bytes.data(),
(unsigned int) bytes.size(),
HB_MEMORY_MODE_DUPLICATE,
nullptr,
nullptr);
const ScopeGuard scope { [&] { hb_blob_destroy (blob); } };
const auto count = hb_face_count (blob);
if (count < 1)
{
// Attempted to create a font from invalid data. Perhaps the font format was unrecognised.
jassertfalse;
return {};
}
return HbFace { hb_face_create (blob, index) };
}
};
//==============================================================================
Typeface::Typeface (const String& faceName, const String& styleName) noexcept
: name (faceName), style (styleName)
Typeface::Typeface (const String& faceName, const String& faceStyle) noexcept
: name (faceName),
style (faceStyle)
{
}
Typeface::~Typeface() = default;
Typeface::Ptr Typeface::getFallbackTypeface()
using HbDrawFuncs = std::unique_ptr<hb_draw_funcs_t, FunctionPointerDestructor<hb_draw_funcs_destroy>>;
static HbDrawFuncs getPathDrawFuncs()
{
const Font fallbackFont (Font::getFallbackFontName(), Font::getFallbackFontStyle(), 10.0f);
return fallbackFont.getTypefacePtr();
HbDrawFuncs funcs { hb_draw_funcs_create() };
hb_draw_funcs_set_move_to_func (funcs.get(), [] (auto*, void* data, auto*, float x, float y, auto*)
{
auto& path = *static_cast<Path*> (data);
path.startNewSubPath ({ x, y });
}, nullptr, nullptr);
hb_draw_funcs_set_line_to_func (funcs.get(), [] (auto*, void* data, auto*, float x, float y, auto*)
{
auto& path = *static_cast<Path*> (data);
path.lineTo ({ x, y });
}, nullptr, nullptr);
hb_draw_funcs_set_quadratic_to_func (funcs.get(), [] (auto*, void* data, auto*, float ctlX, float ctlY, float toX, float toY, auto*)
{
auto& path = *static_cast<Path*> (data);
path.quadraticTo ({ ctlX, ctlY }, { toX, toY });
}, nullptr, nullptr);
hb_draw_funcs_set_cubic_to_func (funcs.get(), [] (auto*, void* data, auto*, float ctlX1, float ctlY1, float ctlX2, float ctlY2, float toX, float toY, auto*)
{
auto& path = *static_cast<Path*> (data);
path.cubicTo ({ ctlX1, ctlY1 }, { ctlX2, ctlY2 }, { toX, toY });
}, nullptr, nullptr);
hb_draw_funcs_set_close_path_func (funcs.get(), [] (auto*, void* data, auto*, auto*)
{
auto& path = *static_cast<Path*> (data);
path.closeSubPath();
}, nullptr, nullptr);
return funcs;
}
EdgeTable* Typeface::getEdgeTableForGlyph (int glyphNumber, const AffineTransform& transform, float fontHeight)
static Path getTypefaceGlyph (const Typeface& typeface, int glyphNumber)
{
static const auto funcs = getPathDrawFuncs();
auto* font = typeface.getNativeDetails().getFont();
Path result;
hb_font_draw_glyph (font, (hb_codepoint_t) glyphNumber, funcs.get(), &result);
// Convert to em units
result.applyTransform (AffineTransform::scale (1.0f / (float) hb_face_get_upem (hb_font_get_face (font))).scaled (1.0f, -1.0f));
return result;
}
bool Typeface::getOutlineForGlyph (int glyphNumber, Path& path)
{
const auto metrics = getNativeDetails().getLegacyMetrics();
// getTypefaceGlyph returns glyphs in em space, getOutlineForGlyph returns glyphs in "special JUCE units" space
path = getTypefaceGlyph (*this, glyphNumber);
path.applyTransform (AffineTransform::scale (metrics.getHeightToPointsFactor()));
return true;
}
void Typeface::applyVerticalHintingTransform (float, Path&)
{
jassertfalse;
}
EdgeTable* Typeface::getEdgeTableForGlyph (int glyphNumber, const AffineTransform& transform, float)
{
Path path;
if (getOutlineForGlyph (glyphNumber, path) && ! path.isEmpty())
{
applyVerticalHintingTransform (fontHeight, path);
if (! getOutlineForGlyph (glyphNumber, path) || path.isEmpty())
return nullptr;
return new EdgeTable (path.getBoundsTransformed (transform).getSmallestIntegerContainer().expanded (1, 0),
path, transform);
}
return new EdgeTable (path.getBoundsTransformed (transform).getSmallestIntegerContainer().expanded (1, 0),
path, transform);
}
return nullptr;
float Typeface::getAscent() const { return getNativeDetails().getLegacyMetrics().getScaledAscent(); }
float Typeface::getDescent() const { return getNativeDetails().getLegacyMetrics().getScaledDescent(); }
float Typeface::getHeightToPointsFactor() const { return getNativeDetails().getLegacyMetrics().getHeightToPointsFactor(); }
Typeface::Ptr Typeface::createSystemTypefaceFor (const void* fontFileData, size_t fontFileDataSize)
{
return createSystemTypefaceFor (Span (static_cast<const std::byte*> (fontFileData), fontFileDataSize));
}
//==============================================================================
struct Typeface::HintingParams
std::optional<uint32_t> Typeface::getNominalGlyphForCodepoint (juce_wchar cp) const
{
HintingParams (Typeface& t)
{
Font font (t);
font = font.withHeight ((float) standardHeight);
auto* font = getNativeDetails().getFont();
top = getAverageY (font, "BDEFPRTZOQ", true);
middle = getAverageY (font, "acegmnopqrsuvwxy", true);
bottom = getAverageY (font, "BDELZOC", false);
}
if (font == nullptr)
return {};
void applyVerticalHintingTransform (float fontSize, Path& path)
{
if (! approximatelyEqual (cachedSize, fontSize))
{
cachedSize = fontSize;
cachedScale = Scaling (top, middle, bottom, fontSize);
}
hb_codepoint_t result{};
if (bottom < top + 3.0f / fontSize)
return;
if (! hb_font_get_nominal_glyph (font, static_cast<hb_codepoint_t> (cp), &result))
return {};
Path result;
for (Path::Iterator i (path); i.next();)
{
switch (i.elementType)
{
case Path::Iterator::startNewSubPath: result.startNewSubPath (i.x1, cachedScale.apply (i.y1)); break;
case Path::Iterator::lineTo: result.lineTo (i.x1, cachedScale.apply (i.y1)); break;
case Path::Iterator::quadraticTo: result.quadraticTo (i.x1, cachedScale.apply (i.y1),
i.x2, cachedScale.apply (i.y2)); break;
case Path::Iterator::cubicTo: result.cubicTo (i.x1, cachedScale.apply (i.y1),
i.x2, cachedScale.apply (i.y2),
i.x3, cachedScale.apply (i.y3)); break;
case Path::Iterator::closePath: result.closeSubPath(); break;
default: jassertfalse; break;
}
}
result.swapWithPath (path);
}
private:
struct Scaling
{
Scaling() noexcept : middle(), upperScale(), upperOffset(), lowerScale(), lowerOffset() {}
Scaling (float t, float m, float b, float fontSize) noexcept : middle (m)
{
const float newT = std::floor (fontSize * t + 0.5f) / fontSize;
const float newB = std::floor (fontSize * b + 0.5f) / fontSize;
const float newM = std::floor (fontSize * m + 0.3f) / fontSize; // this is slightly biased so that lower-case letters
// are more likely to become taller than shorter.
upperScale = jlimit (0.9f, 1.1f, (newM - newT) / (m - t));
lowerScale = jlimit (0.9f, 1.1f, (newB - newM) / (b - m));
upperOffset = newM - m * upperScale;
lowerOffset = newB - b * lowerScale;
}
float apply (float y) const noexcept
{
return y < middle ? (y * upperScale + upperOffset)
: (y * lowerScale + lowerOffset);
}
float middle, upperScale, upperOffset, lowerScale, lowerOffset;
};
float cachedSize = 0;
Scaling cachedScale;
static float getAverageY (const Font& font, const char* chars, bool getTop)
{
GlyphArrangement ga;
ga.addLineOfText (font, chars, 0, 0);
Array<float> yValues;
for (auto& glyph : ga)
{
Path p;
glyph.createPath (p);
auto bounds = p.getBounds();
if (! p.isEmpty())
yValues.add (getTop ? bounds.getY() : bounds.getBottom());
}
std::sort (yValues.begin(), yValues.end());
auto median = yValues[yValues.size() / 2];
float total = 0;
int num = 0;
for (auto y : yValues)
{
if (std::abs (median - y) < 0.05f * (float) standardHeight)
{
total += y;
++num;
}
}
return num < 4 ? 0.0f : total / ((float) num * (float) standardHeight);
}
enum { standardHeight = 100 };
float top = 0, middle = 0, bottom = 0;
};
void Typeface::applyVerticalHintingTransform (float fontSize, Path& path)
{
if (fontSize > 3.0f && fontSize < 25.0f)
{
ScopedLock sl (hintingLock);
if (hintingParams == nullptr)
hintingParams.reset (new HintingParams (*this));
return hintingParams->applyVerticalHintingTransform (fontSize, path);
}
return result;
}
//==============================================================================
//==============================================================================
#if JUCE_UNIT_TESTS
class TypefaceTests : public UnitTest
{
public:
TypefaceTests() : UnitTest ("Typeface", UnitTestCategories::graphics) {}
void runTest() override
{
// If we're running these tests standalone, we want singletons to be cleared before the app
// exists, so as not to alarm th leak detector.
const ScopedJuceInitialiser_GUI scope;
const auto systemNames = getFontFamilyNamesAsSet();
auto ptr = loadTypeface (FontBinaryData::Karla_Regular_Typo_On_Offsets_Off);
const auto ptrName = ptr->getName();
// These tests assume that you don't have a font named "karla" installed.
beginTest ("Setup");
{
expect (systemNames.count (ptr->getName()) == 0);
}
beginTest ("Creating a font from memory allows it to be discovered by Font::findAllTypefaceNames()");
{
const auto newNames = getFontFamilyNamesAsSet();
expect (newNames.size() == systemNames.size() + 1);
expect (newNames.count (ptr->getName()) == 1);
}
beginTest ("The available styles of memory fonts can be found");
{
const auto styles = Font::findAllTypefaceStyles (ptr->getName());
expect (styles == StringArray { ptr->getStyle() });
}
beginTest ("Typefaces loaded from memory are found when creating font instances by name");
{
Font font (ptr->getName(), ptr->getStyle(), 12.0f);
expect (font.getTypefacePtr() != nullptr);
expect (font.getTypefacePtr()->getName() == ptr->getName());
expect (font.getTypefacePtr()->getStyle() == ptr->getStyle());
}
// Unload font
ptr = nullptr;
beginTest ("After a memory font is no longer referenced, it is not returned from Font::findAllTypefaceNames()");
{
const auto newNames = getFontFamilyNamesAsSet();
expect (newNames == systemNames);
}
beginTest ("After a memory font is no longer referenced, it has no styles");
{
const auto styles = Font::findAllTypefaceStyles (ptrName);
expect (styles.isEmpty());
}
}
static std::set<String> getFontFamilyNamesAsSet()
{
std::set<String> result;
for (const auto& name : Font::findAllTypefaceNames())
result.insert (name);
return result;
}
static Typeface::Ptr loadTypeface (Span<const unsigned char> data)
{
return Typeface::createSystemTypefaceFor (data.data(), data.size());
}
};
static TypefaceTests typefaceTests;
#endif
} // namespace juce

View file

@ -42,19 +42,18 @@ namespace juce
This base class is abstract, but calling createSystemTypefaceFor() will return
a platform-specific subclass that can be used.
The CustomTypeface subclass allow you to build your own typeface, and to
load and save it in the JUCE typeface format.
Normally you should never need to deal directly with Typeface objects - the Font
class does everything you typically need for rendering text.
@see CustomTypeface, Font
@see Font
@tags{Graphics}
*/
class JUCE_API Typeface : public ReferenceCountedObject
{
public:
Typeface (const String& name, const String& newStyle) noexcept;
//==============================================================================
/** A handy typedef for a pointer to a typeface. */
using Ptr = ReferenceCountedObjectPtr<Typeface>;
@ -63,13 +62,13 @@ public:
/** Returns the font family of the typeface.
@see Font::getTypefaceName
*/
const String& getName() const noexcept { return name; }
const String& getName() const noexcept { return name; }
//==============================================================================
/** Returns the font style of the typeface.
@see Font::getTypefaceStyle
*/
const String& getStyle() const noexcept { return style; }
const String& getStyle() const noexcept { return style; }
//==============================================================================
/** Creates a new system typeface. */
@ -78,60 +77,97 @@ public:
/** Attempts to create a font from some raw font file data (e.g. a TTF or OTF file image).
The system will take its own internal copy of the data, so you can free the block once
this method has returned.
The typeface will remain registered with the system for as long as there is at least one
owner of the returned Ptr. This allows typefaces registered with createSystemTypefaceFor to
be created using just a typeface family name, e.g. in font fallback lists.
*/
static Ptr createSystemTypefaceFor (const void* fontFileData, size_t fontFileDataSize);
/** Attempts to create a font from some raw font file data (e.g. a TTF or OTF file image).
The system will take its own internal copy of the data.
The typeface will remain registered with the system for as long as there is at least one
owner of the returned Ptr. This allows typefaces registered with createSystemTypefaceFor to
be created using just a typeface family name, e.g. in font fallback lists.
*/
static Ptr createSystemTypefaceFor (Span<const std::byte>);
//==============================================================================
/** Destructor. */
~Typeface() override;
/** Returns true if this typeface can be used to render the specified font.
When called, the font will already have been checked to make sure that its name and
style flags match the typeface.
*/
virtual bool isSuitableForFont (const Font&) const { return true; }
/** Returns the ascent of the font, as a proportion of its height.
The height is considered to always be normalised as 1.0, so this will be a
value less that 1.0, indicating the proportion of the font that lies above
its baseline.
*/
virtual float getAscent() const = 0;
float getAscent() const;
/** Returns the descent of the font, as a proportion of its height.
The height is considered to always be normalised as 1.0, so this will be a
value less that 1.0, indicating the proportion of the font that lies below
its baseline.
*/
virtual float getDescent() const = 0;
float getDescent() const;
/** Returns the value by which you should multiply a JUCE font-height value to
convert it to the equivalent point-size.
*/
virtual float getHeightToPointsFactor() const = 0;
float getHeightToPointsFactor() const;
/** Measures the width of a line of text.
/** @deprecated
This function has several shortcomings:
- The returned value is based on a font with a normalised JUCE height of 1.0,
which will normally differ from the value that would be expected for a font
with a height of 1 pt.
- This function is unsuitable for measuring text with spacing that doesn't
scale linearly with point size, which might be the case for fonts that
implement optical sizing.
- The result is computed assuming that ligatures and other font features will
not be used when rendering the string. There's also no way of specifying a
language used for the string, which may affect the widths of CJK text.
- If the typeface doesn't contain suitable glyphs for all characters in the
string, missing characters will be substituted with the notdef/tofu glyph
instead of attempting to use a different font that contains suitable
glyphs.
Measures the width of a line of text.
The distance returned is based on the font having an normalised height of 1.0.
You should never need to call this directly! Use Font::getStringWidth() instead!
You should never need to call this!
*/
virtual float getStringWidth (const String& text) = 0;
/** Converts a line of text into its glyph numbers and their positions.
/** @deprecated
This function has several shortcomings:
- The returned values are based on a font with a normalised JUCE height of 1.0,
which will normally differ from the value that would be expected for a font
with a height of 1 pt.
- This function is unsuitable for measuring text with spacing that doesn't
scale linearly with point size, which might be the case for fonts that
implement optical sizing.
- Ligatures are deliberately ignored, which will lead to ugly results if the
positions are used to paint text using latin scripts, and potentially
illegible results for other scripts. There's also no way of specifying a
language used for the string, which may affect the rendering of CJK text.
- If the typeface doesn't contain suitable glyphs for all characters in the
string, missing characters will be substituted with the notdef/tofu glyph
instead of attempting to use a different font that contains suitable
glyphs.
Converts a line of text into its glyph numbers and their positions.
The distances returned are based on the font having an normalised height of 1.0.
You should never need to call this directly! Use Font::getGlyphPositions() instead!
You should never need to call this!
*/
virtual void getGlyphPositions (const String& text, Array<int>& glyphs, Array<float>& xOffsets) = 0;
/** Returns the outline for a glyph.
The path returned will be normalised to a font height of 1.0.
*/
virtual bool getOutlineForGlyph (int glyphNumber, Path& path) = 0;
bool getOutlineForGlyph (int glyphNumber, Path& path);
/** Returns a new EdgeTable that contains the path for the given glyph, with the specified transform applied. */
virtual EdgeTable* getEdgeTableForGlyph (int glyphNumber, const AffineTransform& transform, float fontHeight);
/** Returns true if the typeface uses hinting. */
virtual bool isHinted() const { return false; }
EdgeTable* getEdgeTableForGlyph (int glyphNumber, const AffineTransform& transform, float fontHeight);
//==============================================================================
/** Changes the number of fonts that are cached in memory. */
@ -149,20 +185,29 @@ public:
the given size to align vertically with the pixel grid. The path should be an unscaled
(i.e. normalised to height of 1.0) path for a glyph.
*/
[[deprecated]]
void applyVerticalHintingTransform (float fontHeight, Path& path);
protected:
//==============================================================================
String name, style;
/** Returns the glyph index corresponding to the provided codepoint, or nullopt if no
such glyph is found.
*/
std::optional<uint32_t> getNominalGlyphForCodepoint (juce_wchar) const;
Typeface (const String& name, const String& style) noexcept;
/** @internal */
class Native;
static Ptr getFallbackTypeface();
/** @internal
At the moment, this is a way to get at the hb_font_t that backs this typeface.
The typeface's hb_font_t has a size of 1 pt (i.e. 1 pt per em).
This is only for internal use!
*/
virtual Native getNativeDetails() const = 0;
private:
struct HintingParams;
std::unique_ptr<HintingParams> hintingParams;
CriticalSection hintingLock;
//==============================================================================
String name;
String style;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Typeface)
};

File diff suppressed because it is too large Load diff

View file

@ -81,6 +81,7 @@
#if JUCE_USE_FREETYPE
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_ADVANCES_H
#endif
#undef SIZEOF
@ -103,6 +104,10 @@
#include <juce_graphics/fonts/harfbuzz/hb-ot.h>
#if JUCE_UNIT_TESTS
#include "fonts/juce_TypefaceTestData.cpp"
#endif
//==============================================================================
#include "fonts/juce_FunctionPointerDestructor.h"
#include "fonts/juce_LruCache.h"
@ -151,8 +156,6 @@
#elif JUCE_WINDOWS
#include "native/juce_DirectWriteTypeface_windows.cpp"
#include "native/juce_DirectWriteTypeLayout_windows.cpp"
#include "native/juce_Fonts_windows.cpp"
#include "native/juce_IconHelpers_windows.cpp"
#if JUCE_DIRECT2D
#include "native/juce_Direct2DGraphicsContext_windows.cpp"

View file

@ -121,8 +121,7 @@ public:
void drawLine (const Line<float>&) override;
void setFont (const Font&) override;
const Font& getFont() override;
void drawGlyph (int glyphNumber, const AffineTransform&) override;
bool drawTextLayout (const AttributedString&, const Rectangle<float>&) override;
void drawGlyph (int glyphNumber, const AffineTransform& transform) override;
private:
//==============================================================================
@ -131,22 +130,7 @@ private:
detail::ColorSpacePtr rgbColourSpace, greyColourSpace;
mutable std::optional<Rectangle<int>> lastClipRect;
struct SavedState
{
SavedState();
SavedState (const SavedState&);
~SavedState();
void setFill (const FillType&);
FillType fillType;
Font font;
CGFontRef fontRef = {};
CGAffineTransform textMatrix = CGAffineTransformIdentity,
inverseTextMatrix = CGAffineTransformIdentity;
detail::GradientPtr gradient = {};
};
struct SavedState;
std::unique_ptr<SavedState> state;
OwnedArray<SavedState> stateStack;

View file

@ -192,6 +192,33 @@ struct ScopedCGContextState
CGContextRef context;
};
struct CoreGraphicsContext::SavedState
{
SavedState() = default;
SavedState (const SavedState& other)
: fillType (other.fillType), font (other.font),
textMatrix (other.textMatrix), inverseTextMatrix (other.inverseTextMatrix),
gradient (other.gradient.get() != nullptr ? CGGradientRetain (other.gradient.get()) : nullptr)
{
}
~SavedState() = default;
void setFill (const FillType& newFill)
{
fillType = newFill;
gradient = nullptr;
}
FillType fillType;
Font font { 1.0f };
CFUniquePtr<CTFontRef> fontRef{};
CGAffineTransform textMatrix = CGAffineTransformIdentity,
inverseTextMatrix = CGAffineTransformIdentity;
detail::GradientPtr gradient = {};
};
//==============================================================================
CoreGraphicsContext::CoreGraphicsContext (CGContextRef c, float h)
: context (c),
@ -630,22 +657,19 @@ void CoreGraphicsContext::setFont (const Font& newFont)
{
if (state->font != newFont)
{
state->fontRef = nullptr;
state->font = newFont;
state->fontRef = nullptr;
auto typeface = state->font.getTypefacePtr();
const auto hbFont = state->font.getNativeDetails().font;
if (auto osxTypeface = dynamic_cast<OSXTypeface*> (typeface.get()))
{
state->fontRef = osxTypeface->fontRef;
CGContextSetFont (context.get(), state->fontRef);
CGContextSetFontSize (context.get(), state->font.getHeight() * osxTypeface->fontHeightToPointsFactor);
state->fontRef.reset (hb_coretext_font_get_ct_font (hbFont.get()));
CFRetain (state->fontRef.get());
state->textMatrix = osxTypeface->renderingTransform;
state->textMatrix.a *= state->font.getHorizontalScale();
CGContextSetTextMatrix (context.get(), state->textMatrix);
state->inverseTextMatrix = CGAffineTransformInvert (state->textMatrix);
}
const auto slant = hb_font_get_synthetic_slant (hbFont.get());
state->textMatrix = CGAffineTransformMake (state->font.getHorizontalScale(), 0, slant * state->font.getHorizontalScale(), 1.0f, 0, 0);
CGContextSetTextMatrix (context.get(), state->textMatrix);
state->inverseTextMatrix = CGAffineTransformInvert (state->textMatrix);
}
}
@ -658,72 +682,27 @@ void CoreGraphicsContext::drawGlyph (int glyphNumber, const AffineTransform& tra
{
if (state->fontRef != nullptr && state->fillType.isColour())
{
auto cgTransformIsOnlyTranslation = [] (CGAffineTransform t)
{
return t.a == 1.0f && t.d == 1.0f && t.b == 0.0f && t.c == 0.0f;
};
const CGGlyph glyphs[] { (CGGlyph) glyphNumber };
if (transform.isOnlyTranslation() && cgTransformIsOnlyTranslation (state->inverseTextMatrix))
{
auto x = transform.mat02 + state->inverseTextMatrix.tx;
auto y = transform.mat12 + state->inverseTextMatrix.ty;
ScopedCGContextState scopedState (context.get());
CGGlyph glyphs[1] = { (CGGlyph) glyphNumber };
CGPoint positions[1] = { { x, flipHeight - roundToInt (y) } };
CGContextShowGlyphsAtPositions (context.get(), glyphs, positions, 1);
}
else
{
ScopedCGContextState scopedState (context.get());
flip();
applyTransform (AffineTransform::scale (1.0f, -1.0f).followedBy (transform));
flip();
applyTransform (transform);
CGContextConcatCTM (context.get(), state->inverseTextMatrix);
auto cgTransform = state->textMatrix;
cgTransform.d = -cgTransform.d;
CGContextConcatCTM (context.get(), cgTransform);
CGGlyph glyphs[1] = { (CGGlyph) glyphNumber };
CGPoint positions[1] = { { 0.0f, 0.0f } };
CGContextShowGlyphsAtPositions (context.get(), glyphs, positions, 1);
}
const CGPoint positions[] { { 0.0f, 0.0f } };
CTFontDrawGlyphs (state->fontRef.get(), glyphs, positions, std::size (glyphs), context.get());
}
else
{
Path p;
auto& f = state->font;
f.getTypefacePtr()->getOutlineForGlyph (glyphNumber, p);
const auto scale = f.getHeight();
fillPath (p, AffineTransform::scale (f.getHeight() * f.getHorizontalScale(), f.getHeight())
.followedBy (transform));
fillPath (p, AffineTransform::scale (scale * f.getHorizontalScale(), scale).followedBy (transform));
}
}
bool CoreGraphicsContext::drawTextLayout (const AttributedString& text, const Rectangle<float>& area)
{
return CoreTextTypeLayout::drawToCGContext (text, area, context.get(), (float) flipHeight);
}
CoreGraphicsContext::SavedState::SavedState()
: font (1.0f)
{
}
CoreGraphicsContext::SavedState::SavedState (const SavedState& other)
: fillType (other.fillType), font (other.font), fontRef (other.fontRef),
textMatrix (other.textMatrix), inverseTextMatrix (other.inverseTextMatrix),
gradient (other.gradient.get() != nullptr ? CGGradientRetain (other.gradient.get()) : nullptr)
{
}
CoreGraphicsContext::SavedState::~SavedState() = default;
void CoreGraphicsContext::SavedState::setFill (const FillType& newFill)
{
fillType = newFill;
gradient = nullptr;
}
static CGGradientRef createGradient (const ColourGradient& g, CGColorSpaceRef colourSpace)
{
auto numColours = g.getNumColours();

View file

@ -773,17 +773,4 @@ void Direct2DLowLevelGraphicsContext::drawGlyph (int glyphNumber, const AffineTr
pimpl->renderingTarget->SetTransform (D2D1::IdentityMatrix());
}
bool Direct2DLowLevelGraphicsContext::drawTextLayout (const AttributedString& text, const Rectangle<float>& area)
{
pimpl->renderingTarget->SetTransform (transformToMatrix (currentState->transform));
DirectWriteTypeLayout::drawToD2DContext (text, area,
*(pimpl->renderingTarget),
*(pimpl->factories->directWriteFactory),
*(pimpl->factories->systemFonts));
pimpl->renderingTarget->SetTransform (D2D1::IdentityMatrix());
return true;
}
} // namespace juce

View file

@ -84,7 +84,6 @@ public:
void setFont (const Font&) override;
const Font& getFont() override;
void drawGlyph (int glyphNumber, const AffineTransform&) override;
bool drawTextLayout (const AttributedString&, const Rectangle<float>&) override;
void resized();
void clear();

View file

@ -35,50 +35,331 @@
namespace juce
{
#if JUCE_USE_DIRECTWRITE
namespace
static String getLocalisedName (IDWriteLocalizedStrings* names)
{
static String getLocalisedName (IDWriteLocalizedStrings* names)
{
jassert (names != nullptr);
jassert (names != nullptr);
uint32 index = 0;
BOOL exists = false;
[[maybe_unused]] auto hr = names->FindLocaleName (L"en-us", &index, &exists);
uint32 index = 0;
BOOL exists = false;
[[maybe_unused]] auto hr = names->FindLocaleName (L"en-us", &index, &exists);
if (! exists)
index = 0;
if (! exists)
index = 0;
uint32 length = 0;
hr = names->GetStringLength (index, &length);
uint32 length = 0;
hr = names->GetStringLength (index, &length);
HeapBlock<wchar_t> name (length + 1);
hr = names->GetString (index, name, length + 1);
HeapBlock<wchar_t> name (length + 1);
hr = names->GetString (index, name, length + 1);
return static_cast<const wchar_t*> (name);
}
static String getFontFamilyName (IDWriteFontFamily* family)
{
jassert (family != nullptr);
ComSmartPtr<IDWriteLocalizedStrings> familyNames;
[[maybe_unused]] auto hr = family->GetFamilyNames (familyNames.resetAndGetPointerAddress());
jassert (SUCCEEDED (hr));
return getLocalisedName (familyNames);
}
static String getFontFaceName (IDWriteFont* font)
{
jassert (font != nullptr);
ComSmartPtr<IDWriteLocalizedStrings> faceNames;
[[maybe_unused]] auto hr = font->GetFaceNames (faceNames.resetAndGetPointerAddress());
jassert (SUCCEEDED (hr));
return getLocalisedName (faceNames);
}
inline Point<float> convertPoint (D2D1_POINT_2F p) noexcept { return Point<float> ((float) p.x, (float) p.y); }
return static_cast<const wchar_t*> (name);
}
static String getFontFamilyName (IDWriteFontFamily* family)
{
jassert (family != nullptr);
ComSmartPtr<IDWriteLocalizedStrings> familyNames;
auto hr = family->GetFamilyNames (familyNames.resetAndGetPointerAddress());
jassertquiet (SUCCEEDED (hr));
return getLocalisedName (familyNames);
}
static String getFontFaceName (IDWriteFont* font)
{
jassert (font != nullptr);
ComSmartPtr<IDWriteLocalizedStrings> faceNames;
auto hr = font->GetFaceNames (faceNames.resetAndGetPointerAddress());
jassertquiet (SUCCEEDED (hr));
return getLocalisedName (faceNames);
}
template <typename Range>
static StringArray stringArrayFromRange (Range&& range)
{
StringArray result;
for (const auto& item : range)
result.add (item);
return result;
}
class AggregateFontCollection
{
public:
explicit AggregateFontCollection (ComSmartPtr<IDWriteFontCollection> baseCollection)
: collections { std::move (baseCollection) } {}
StringArray findAllTypefaceNames()
{
const std::scoped_lock lock { mutex };
std::set<String> strings;
for (const auto& collection : collections)
{
const auto count = collection->GetFontFamilyCount();
for (auto i = decltype (count){}; i < count; ++i)
{
ComSmartPtr<IDWriteFontFamily> family;
if (FAILED (collection->GetFontFamily (i, family.resetAndGetPointerAddress())) || family == nullptr)
continue;
strings.insert (getFontFamilyName (family));
}
}
return stringArrayFromRange (strings);
}
StringArray findAllTypefaceStyles (const String& family)
{
const std::scoped_lock lock { mutex };
for (const auto& collection : collections)
{
BOOL fontFound = false;
uint32 fontIndex = 0;
if (FAILED (collection->FindFamilyName (family.toWideCharPointer(), &fontIndex, &fontFound)) || ! fontFound)
continue;
ComSmartPtr<IDWriteFontFamily> fontFamily;
if (FAILED (collection->GetFontFamily (fontIndex, fontFamily.resetAndGetPointerAddress())) || fontFamily == nullptr)
continue;
// Get the font faces
const auto fontFacesCount = fontFamily->GetFontCount();
std::set<String> results;
for (uint32 i = 0; i < fontFacesCount; ++i)
{
ComSmartPtr<IDWriteFont> dwFont;
if (FAILED (fontFamily->GetFont (i, dwFont.resetAndGetPointerAddress())) || dwFont->GetSimulations() != DWRITE_FONT_SIMULATIONS_NONE)
continue;
results.insert (getFontFaceName (dwFont));
}
return stringArrayFromRange (results);
}
return {};
}
ComSmartPtr<IDWriteFontFamily> getFamilyByName (const wchar_t* name)
{
for (const auto& collection : collections)
{
const auto fontIndex = [&]
{
BOOL found = false;
UINT32 index = 0;
return (SUCCEEDED (collection->FindFamilyName (name, &index, &found)) && found)
? index
: (UINT32) -1;
}();
if (fontIndex == (UINT32) -1)
continue;
ComSmartPtr<IDWriteFontFamily> family;
if (FAILED (collection->GetFontFamily (fontIndex, family.resetAndGetPointerAddress())) || family == nullptr)
continue;
return family;
}
return {};
}
void addCollection (ComSmartPtr<IDWriteFontCollection> collection)
{
const std::scoped_lock lock { mutex };
collections.push_back (std::move (collection));
}
void removeCollection (ComSmartPtr<IDWriteFontCollection> collection)
{
const std::scoped_lock lock { mutex };
const auto iter = std::find (collections.begin(), collections.end(), collection);
if (iter != collections.end())
collections.erase (iter);
}
struct MapResult
{
ComSmartPtr<IDWriteFont> font;
UINT32 length{};
float scale{};
};
/* Tries matching against each collection in turn.
If any collection is able to match the entire string, then uses the appropriate font
from that collection.
Otherwise, returns the font that is able to match the longest sequence of characters,
preferring user-provided fonts.
*/
MapResult mapCharacters (IDWriteFontFallback* fallback,
IDWriteTextAnalysisSource* analysisSource,
UINT32 textPosition,
UINT32 textLength,
wchar_t const* baseFamilyName,
DWRITE_FONT_WEIGHT baseWeight,
DWRITE_FONT_STYLE baseStyle,
DWRITE_FONT_STRETCH baseStretch) noexcept
{
const std::scoped_lock lock { mutex };
// For reasons I don't understand, the system may pick better substitutions when passing
// nullptr, instead of the system collection, as the "default collection to use".
auto collectionsToCheck = collections;
collectionsToCheck.insert (collectionsToCheck.begin(), nullptr);
MapResult bestMatch;
for (const auto& collection : collectionsToCheck)
{
MapResult result;
const auto status = fallback->MapCharacters (analysisSource,
textPosition,
textLength,
collection,
baseFamilyName,
baseWeight,
baseStyle,
baseStretch,
&result.length,
result.font.resetAndGetPointerAddress(),
&result.scale);
if (FAILED (status) || result.font == nullptr)
continue;
if (result.length == textLength)
return result;
if (result.length >= bestMatch.length)
bestMatch = result;
}
return bestMatch;
}
private:
std::vector<ComSmartPtr<IDWriteFontCollection>> collections;
std::mutex mutex;
};
class MemoryFontFileStream final : public ComBaseClassHelper<IDWriteFontFileStream>
{
public:
explicit MemoryFontFileStream (MemoryBlock d) : rawData (std::move (d)) {}
JUCE_COMRESULT GetFileSize (UINT64* fileSize) noexcept override
{
*fileSize = rawData.getSize();
return S_OK;
}
JUCE_COMRESULT GetLastWriteTime (UINT64* lastWriteTime) noexcept override
{
*lastWriteTime = 0;
return S_OK;
}
JUCE_COMRESULT ReadFileFragment (const void** fragmentStart,
UINT64 fileOffset,
UINT64 fragmentSize,
void** fragmentContext) noexcept override
{
if (fileOffset + fragmentSize > rawData.getSize())
{
*fragmentStart = nullptr;
*fragmentContext = nullptr;
return E_INVALIDARG;
}
*fragmentStart = addBytesToPointer (rawData.getData(), fileOffset);
*fragmentContext = this;
return S_OK;
}
void WINAPI ReleaseFileFragment (void*) noexcept override {}
private:
MemoryBlock rawData;
};
class MemoryFontFileLoader final : public ComBaseClassHelper<IDWriteFontFileLoader>
{
public:
HRESULT WINAPI CreateStreamFromKey (const void* fontFileReferenceKey,
UINT32 keySize,
IDWriteFontFileStream** fontFileStream) noexcept override
{
*fontFileStream = new MemoryFontFileStream { MemoryBlock { fontFileReferenceKey, keySize } };
return S_OK;
}
};
class FontFileEnumerator final : public ComBaseClassHelper<IDWriteFontFileEnumerator>
{
public:
FontFileEnumerator (IDWriteFactory& factoryIn, IDWriteFontFileLoader& loaderIn, MemoryBlock keyIn)
: factory (factoryIn), loader (loaderIn), key (keyIn) {}
HRESULT WINAPI GetCurrentFontFile (IDWriteFontFile** fontFile) noexcept override
{
*fontFile = nullptr;
if (! isPositiveAndBelow (rawDataIndex, 1))
return E_FAIL;
return factory.CreateCustomFontFileReference (key.getData(),
(UINT32) key.getSize(),
&loader,
fontFile);
}
HRESULT WINAPI MoveNext (BOOL* hasCurrentFile) noexcept override
{
++rawDataIndex;
*hasCurrentFile = rawDataIndex < 1 ? TRUE : FALSE;
return S_OK;
}
IDWriteFactory& factory;
IDWriteFontFileLoader& loader;
MemoryBlock key;
size_t rawDataIndex = std::numeric_limits<size_t>::max();
};
class DirectWriteCustomFontCollectionLoader final : public ComBaseClassHelper<IDWriteFontCollectionLoader>
{
public:
explicit DirectWriteCustomFontCollectionLoader (IDWriteFontFileLoader& loaderIn)
: loader (loaderIn) {}
HRESULT WINAPI CreateEnumeratorFromKey (IDWriteFactory* factory,
const void* collectionKey,
UINT32 collectionKeySize,
IDWriteFontFileEnumerator** fontFileEnumerator) noexcept override
{
*fontFileEnumerator = new FontFileEnumerator { *factory, loader, MemoryBlock { collectionKey, collectionKeySize } };
return S_OK;
}
private:
IDWriteFontFileLoader& loader;
};
class Direct2DFactories
{
public:
@ -106,13 +387,29 @@ public:
JUCE_LOAD_WINAPI_FUNCTION (directWriteDll, DWriteCreateFactory, dWriteCreateFactory,
HRESULT, (DWRITE_FACTORY_TYPE, REFIID, IUnknown**))
if (dWriteCreateFactory != nullptr)
if (dWriteCreateFactory == nullptr)
return;
for (const auto uuid : { __uuidof (IDWriteFactory3), __uuidof (IDWriteFactory2), __uuidof (IDWriteFactory) })
{
dWriteCreateFactory (DWRITE_FACTORY_TYPE_SHARED, __uuidof (IDWriteFactory),
dWriteCreateFactory (DWRITE_FACTORY_TYPE_SHARED, uuid,
(IUnknown**) directWriteFactory.resetAndGetPointerAddress());
if (directWriteFactory != nullptr)
directWriteFactory->GetSystemFontCollection (systemFonts.resetAndGetPointerAddress());
break;
}
if (directWriteFactory != nullptr)
{
directWriteFactory->RegisterFontFileLoader (fileLoader);
directWriteFactory->RegisterFontCollectionLoader (collectionLoader);
ComSmartPtr<IDWriteFontCollection> collection;
if (SUCCEEDED (directWriteFactory->GetSystemFontCollection (collection.resetAndGetPointerAddress(), FALSE)) && collection != nullptr)
fonts.emplace (collection);
else
jassertfalse;
}
if (d2dFactory != nullptr)
@ -133,212 +430,379 @@ public:
~Direct2DFactories()
{
d2dFactory = nullptr; // (need to make sure these are released before deleting the DynamicLibrary objects)
directWriteFactory = nullptr;
systemFonts = nullptr;
directWriteRenderTarget = nullptr;
if (directWriteFactory != nullptr)
{
directWriteFactory->UnregisterFontCollectionLoader (collectionLoader);
directWriteFactory->UnregisterFontFileLoader (fileLoader);
}
}
ComSmartPtr<ID2D1Factory> d2dFactory;
ComSmartPtr<IDWriteFactory> directWriteFactory;
ComSmartPtr<IDWriteFontCollection> systemFonts;
ComSmartPtr<ID2D1DCRenderTarget> directWriteRenderTarget;
[[nodiscard]] ComSmartPtr<ID2D1Factory> getD2D1Factory() const { return d2dFactory; }
[[nodiscard]] ComSmartPtr<IDWriteFactory> getDWriteFactory() const { return directWriteFactory; }
[[nodiscard]] ComSmartPtr<ID2D1DCRenderTarget> getD2D1DCRenderTarget() const { return directWriteRenderTarget; }
[[nodiscard]] AggregateFontCollection& getFonts() { jassert (fonts.has_value()); return *fonts; }
[[nodiscard]] ComSmartPtr<IDWriteFontCollectionLoader> getCollectionLoader() const { return collectionLoader; }
private:
DynamicLibrary direct2dDll, directWriteDll;
ComSmartPtr<ID2D1Factory> d2dFactory;
ComSmartPtr<IDWriteFactory> directWriteFactory;
ComSmartPtr<ID2D1DCRenderTarget> directWriteRenderTarget;
std::optional<AggregateFontCollection> fonts;
ComSmartPtr<IDWriteFontFileLoader> fileLoader = becomeComSmartPtrOwner (new MemoryFontFileLoader);
ComSmartPtr<IDWriteFontCollectionLoader> collectionLoader = becomeComSmartPtrOwner (new DirectWriteCustomFontCollectionLoader (*fileLoader));
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Direct2DFactories)
};
//==============================================================================
StringArray Font::findAllTypefaceNames()
{
SharedResourcePointer<Direct2DFactories> factories;
return factories->getFonts().findAllTypefaceNames();
}
StringArray Font::findAllTypefaceStyles (const String& family)
{
if (FontStyleHelpers::isPlaceholderFamilyName (family))
return findAllTypefaceStyles (FontStyleHelpers::getConcreteFamilyNameFromPlaceholder (family));
SharedResourcePointer<Direct2DFactories> factories;
return factories->getFonts().findAllTypefaceStyles (family);
}
extern bool juce_isRunningInWine();
class WindowsDirectWriteTypeface final : public Typeface
{
public:
WindowsDirectWriteTypeface (const Font& font, IDWriteFontCollection* fontCollection)
: Typeface (font.getTypefaceName(), font.getTypefaceStyle())
~WindowsDirectWriteTypeface() override
{
jassert (fontCollection != nullptr);
uint32 fontIndex = 0;
[[maybe_unused]] auto hr = fontCollection->FindFamilyName (font.getTypefaceName().toWideCharPointer(), &fontIndex, &fontFound);
if (! fontFound)
fontIndex = 0;
// Get the font family using the search results
// Fonts like: Times New Roman, Times New Roman Bold, Times New Roman Italic are all in the same font family
ComSmartPtr<IDWriteFontFamily> dwFontFamily;
hr = fontCollection->GetFontFamily (fontIndex, dwFontFamily.resetAndGetPointerAddress());
// Get a specific font in the font family using typeface style
{
ComSmartPtr<IDWriteFont> dwFont;
for (int i = (int) dwFontFamily->GetFontCount(); --i >= 0;)
{
hr = dwFontFamily->GetFont ((UINT32) i, dwFont.resetAndGetPointerAddress());
if (i == 0)
break;
ComSmartPtr<IDWriteLocalizedStrings> faceNames;
hr = dwFont->GetFaceNames (faceNames.resetAndGetPointerAddress());
if (font.getTypefaceStyle() == getLocalisedName (faceNames))
break;
}
jassert (dwFont != nullptr);
hr = dwFont->CreateFontFace (dwFontFace.resetAndGetPointerAddress());
}
if (dwFontFace != nullptr)
{
DWRITE_FONT_METRICS dwFontMetrics;
dwFontFace->GetMetrics (&dwFontMetrics);
// All Font Metrics are in design units so we need to get designUnitsPerEm value
// to get the metrics into Em/Design Independent Pixels
designUnitsPerEm = dwFontMetrics.designUnitsPerEm;
ascent = std::abs ((float) dwFontMetrics.ascent);
auto totalSize = ascent + std::abs ((float) dwFontMetrics.descent);
ascent /= totalSize;
unitsToHeightScaleFactor = (float) designUnitsPerEm / totalSize;
auto tempDC = GetDC (nullptr);
auto dpi = (float) (GetDeviceCaps (tempDC, LOGPIXELSX) + GetDeviceCaps (tempDC, LOGPIXELSY)) / 2.0f;
heightToPointsFactor = (dpi / (float) GetDeviceCaps (tempDC, LOGPIXELSY)) * unitsToHeightScaleFactor;
ReleaseDC (nullptr, tempDC);
auto pathAscent = (1024.0f * dwFontMetrics.ascent) / (float) designUnitsPerEm;
auto pathDescent = (1024.0f * dwFontMetrics.descent) / (float) designUnitsPerEm;
auto pathScale = 1.0f / (std::abs (pathAscent) + std::abs (pathDescent));
pathTransform = AffineTransform::scale (pathScale);
}
if (collection != nullptr)
factories->getFonts().removeCollection (collection);
}
bool loadedOk() const noexcept { return dwFontFace != nullptr; }
BOOL isFontFound() const noexcept { return fontFound; }
float getAscent() const override { return ascent; }
float getDescent() const override { return 1.0f - ascent; }
float getHeightToPointsFactor() const override { return heightToPointsFactor; }
float getStringWidth (const String& text) override
{
auto textUTF32 = text.toUTF32();
auto len = textUTF32.length();
auto utf32 = text.toUTF32();
auto numChars = utf32.length();
std::vector<UINT16> results (numChars);
HeapBlock<UINT16> glyphIndices (len);
dwFontFace->GetGlyphIndices (textUTF32, (UINT32) len, glyphIndices);
HeapBlock<DWRITE_GLYPH_METRICS> dwGlyphMetrics (len);
dwFontFace->GetDesignGlyphMetrics (glyphIndices, (UINT32) len, dwGlyphMetrics, false);
if (FAILED (dwFontFace->GetGlyphIndices (utf32, (UINT32) numChars, results.data())))
return {};
float x = 0;
for (size_t i = 0; i < len; ++i)
x += (float) dwGlyphMetrics[i].advanceWidth / (float) designUnitsPerEm;
for (size_t i = 0; i < numChars; ++i)
x += getKerning (results[i], (i + 1) < numChars ? results[i + 1] : -1);
return x * unitsToHeightScaleFactor;
const auto heightToPoints = getNativeDetails().getLegacyMetrics().getHeightToPointsFactor();
return x * heightToPoints;
}
void getGlyphPositions (const String& text, Array<int>& resultGlyphs, Array<float>& xOffsets) override
{
xOffsets.add (0);
auto textUTF32 = text.toUTF32();
auto len = textUTF32.length();
HeapBlock<UINT16> glyphIndices (len);
dwFontFace->GetGlyphIndices (textUTF32, (UINT32) len, glyphIndices);
HeapBlock<DWRITE_GLYPH_METRICS> dwGlyphMetrics (len);
dwFontFace->GetDesignGlyphMetrics (glyphIndices, (UINT32) len, dwGlyphMetrics, false);
auto utf32 = text.toUTF32();
auto numChars = utf32.length();
std::vector<UINT16> results (numChars);
float x = 0;
for (size_t i = 0; i < len; ++i)
const auto heightToPoints = getNativeDetails().getLegacyMetrics().getHeightToPointsFactor();
if (SUCCEEDED (dwFontFace->GetGlyphIndices (utf32, (UINT32) numChars, results.data())))
{
x += (float) dwGlyphMetrics[i].advanceWidth / (float) designUnitsPerEm;
xOffsets.add (x * unitsToHeightScaleFactor);
resultGlyphs.add (glyphIndices[i]);
resultGlyphs.ensureStorageAllocated ((int) numChars);
xOffsets.ensureStorageAllocated ((int) numChars + 1);
for (size_t i = 0; i < numChars; ++i)
{
resultGlyphs.add (results[i]);
xOffsets.add (x * heightToPoints);
x += getKerning (results[i], (i + 1) < numChars ? results[i + 1] : -1);
}
}
xOffsets.add (x * heightToPoints);
}
bool getOutlineForGlyph (int glyphNumber, Path& path) override
static Typeface::Ptr from (const Font& f)
{
jassert (path.isEmpty()); // we might need to apply a transform to the path, so this must be empty
auto glyphIndex = (UINT16) glyphNumber;
auto pathGeometrySink = becomeComSmartPtrOwner (new PathGeometrySink());
const auto name = f.getTypefaceName();
const auto style = f.getTypefaceStyle();
dwFontFace->GetGlyphRunOutline (1024.0f, &glyphIndex, nullptr, nullptr,
1, false, false, pathGeometrySink);
path = pathGeometrySink->path;
SharedResourcePointer<Direct2DFactories> factories;
const auto family = factories->getFonts().getFamilyByName (name.toWideCharPointer());
if (! pathTransform.isIdentity())
path.applyTransform (pathTransform);
if (family == nullptr)
return {};
return true;
const auto weight = f.isBold() ? DWRITE_FONT_WEIGHT_BOLD : DWRITE_FONT_WEIGHT_NORMAL;
const auto italic = f.isItalic() ? DWRITE_FONT_STYLE_ITALIC : DWRITE_FONT_STYLE_NORMAL;
ComSmartPtr<IDWriteFont> dwFont;
if (FAILED (family->GetFirstMatchingFont (weight, DWRITE_FONT_STRETCH_NORMAL, italic, dwFont.resetAndGetPointerAddress())) || dwFont == nullptr)
return {};
ComSmartPtr<IDWriteFontFace> dwFontFace;
if (FAILED (dwFont->CreateFontFace (dwFontFace.resetAndGetPointerAddress())) || dwFontFace == nullptr)
return {};
const HbFace hbFace { hb_directwrite_face_create (dwFontFace) };
HbFont hbFont { hb_font_create (hbFace.get()) };
FontStyleHelpers::initSynthetics (hbFont.get(), f);
return new WindowsDirectWriteTypeface (name, style, dwFont, dwFontFace, std::move (hbFont));
}
IDWriteFontFace* getIDWriteFontFace() const noexcept { return dwFontFace; }
static Typeface::Ptr from (Span<const std::byte> blob)
{
return from (MemoryBlock { blob.data(), blob.size() });
}
float getUnitsToHeightScaleFactor() const noexcept { return unitsToHeightScaleFactor; }
Native getNativeDetails() const override
{
return Native { hbFont.get() };
}
IDWriteFontFace* getIDWriteFontFace() const { return dwFontFace; }
private:
SharedResourcePointer<Direct2DFactories> factories;
ComSmartPtr<IDWriteFontFace> dwFontFace;
float unitsToHeightScaleFactor = 1.0f, heightToPointsFactor = 1.0f, ascent = 0;
int designUnitsPerEm = 0;
AffineTransform pathTransform;
BOOL fontFound = false;
struct PathGeometrySink final : public ComBaseClassHelper<IDWriteGeometrySink>
float getKerning (int glyph1, int glyph2) const
{
PathGeometrySink() = default;
const auto face = dwFontFace.getInterface<IDWriteFontFace1>();
void STDMETHODCALLTYPE AddBeziers (const D2D1_BEZIER_SEGMENT* beziers, UINT beziersCount) noexcept override
const UINT16 glyphs[] { (UINT16) glyph1, (UINT16) glyph2 };
INT32 advances [std::size (glyphs)]{};
if (FAILED (face->GetDesignGlyphAdvances ((UINT32) std::size (glyphs), std::data (glyphs), std::data (advances))))
return {};
DWRITE_FONT_METRICS metrics{};
face->GetMetrics (&metrics);
// TODO(reuk) incorrect
return (float) advances[0] / (float) metrics.designUnitsPerEm;
}
static UINT32 numUtf16Words (const CharPointer_UTF16& str)
{
return (UINT32) (str.findTerminatingNull().getAddress() - str.getAddress());
}
class AnalysisSource final : public ComBaseClassHelper<IDWriteTextAnalysisSource>
{
public:
AnalysisSource (String cIn, String langIn)
: character (cIn), language (langIn) {}
~AnalysisSource() override = default;
JUCE_COMCALL GetLocaleName (UINT32, UINT32*, const WCHAR** localeName) noexcept override
{
for (UINT i = 0; i < beziersCount; ++i)
path.cubicTo (convertPoint (beziers[i].point1),
convertPoint (beziers[i].point2),
convertPoint (beziers[i].point3));
*localeName = language.isNotEmpty() ? language.toWideCharPointer() : nullptr;
return S_OK;
}
void STDMETHODCALLTYPE AddLines (const D2D1_POINT_2F* points, UINT pointsCount) noexcept override
JUCE_COMCALL GetNumberSubstitution (UINT32, UINT32*, IDWriteNumberSubstitution** substitution) noexcept override
{
for (UINT i = 0; i < pointsCount; ++i)
path.lineTo (convertPoint (points[i]));
*substitution = nullptr;
return S_OK;
}
void STDMETHODCALLTYPE BeginFigure (D2D1_POINT_2F startPoint, D2D1_FIGURE_BEGIN) noexcept override
DWRITE_READING_DIRECTION STDMETHODCALLTYPE GetParagraphReadingDirection() noexcept override
{
path.startNewSubPath (convertPoint (startPoint));
return DWRITE_READING_DIRECTION_LEFT_TO_RIGHT;
}
void STDMETHODCALLTYPE EndFigure (D2D1_FIGURE_END figureEnd) noexcept override
JUCE_COMCALL GetTextAtPosition (UINT32 textPosition, const WCHAR** textString, UINT32* textLength) noexcept override
{
if (figureEnd == D2D1_FIGURE_END_CLOSED)
path.closeSubPath();
if (textPosition == 0)
{
const auto utf16 = character.toUTF16();
*textString = utf16.getAddress();
*textLength = numUtf16Words (utf16);
}
else
{
// We don't expect this to be hit. If you see this, alert the JUCE team!
jassertfalse;
*textString = nullptr;
*textLength = 0;
}
return S_OK;
}
void STDMETHODCALLTYPE SetFillMode (D2D1_FILL_MODE fillMode) noexcept override
JUCE_COMCALL GetTextBeforePosition (UINT32, const WCHAR** textString, UINT32* textLength) noexcept override
{
path.setUsingNonZeroWinding (fillMode == D2D1_FILL_MODE_WINDING);
// We don't expect this to be hit. If you see this, alert the JUCE team!
jassertfalse;
*textString = nullptr;
*textLength = 0;
return S_OK;
}
void STDMETHODCALLTYPE SetSegmentFlags (D2D1_PATH_SEGMENT) noexcept override {}
JUCE_COMRESULT Close() noexcept override { return S_OK; }
Path path;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PathGeometrySink)
private:
String character;
String language;
};
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WindowsDirectWriteTypeface)
static Typeface::Ptr from (MemoryBlock blob)
{
SharedResourcePointer<Direct2DFactories> factories;
const auto dwFactory = factories->getDWriteFactory();
if (dwFactory == nullptr)
return {};
ComSmartPtr<IDWriteFontCollection> customFontCollection;
if (FAILED (dwFactory->CreateCustomFontCollection (factories->getCollectionLoader(),
blob.getData(),
(UINT32) blob.getSize(),
customFontCollection.resetAndGetPointerAddress())))
return {};
if (customFontCollection == nullptr)
return {};
ComSmartPtr<IDWriteFontFamily> fontFamily;
if (FAILED (customFontCollection->GetFontFamily (0, fontFamily.resetAndGetPointerAddress())) || fontFamily == nullptr)
return {};
ComSmartPtr<IDWriteFont> dwFont;
if (FAILED (fontFamily->GetFont (0, dwFont.resetAndGetPointerAddress())) || dwFont == nullptr)
return {};
ComSmartPtr<IDWriteFontFace> dwFontFace;
if (FAILED (dwFont->CreateFontFace (dwFontFace.resetAndGetPointerAddress())) || dwFontFace == nullptr)
return {};
const auto name = getLocalisedFamilyName (*fontFamily);
const auto style = getLocalisedStyle (*dwFont);
const HbFace hbFace { hb_directwrite_face_create (dwFontFace) };
return new WindowsDirectWriteTypeface (name, style, dwFont, dwFontFace, HbFont { hb_font_create (hbFace.get()) }, std::move (customFontCollection));
}
static String getLocalisedFamilyName (IDWriteFont& font)
{
ComSmartPtr<IDWriteFontFamily> family;
if (FAILED (font.GetFontFamily (family.resetAndGetPointerAddress())) || family == nullptr)
return {};
return getLocalisedFamilyName (*family);
}
static String getLocalisedFamilyName (IDWriteFontFamily& fontFamily)
{
ComSmartPtr<IDWriteLocalizedStrings> familyNames;
if (FAILED (fontFamily.GetFamilyNames (familyNames.resetAndGetPointerAddress())) || familyNames == nullptr)
return {};
return getLocalisedName (familyNames);
}
static String getLocalisedStyle (IDWriteFont& font)
{
ComSmartPtr<IDWriteLocalizedStrings> faceNames;
if (FAILED (font.GetFaceNames (faceNames.resetAndGetPointerAddress())) || faceNames == nullptr)
return {};
return getLocalisedName (faceNames);
}
WindowsDirectWriteTypeface (const String& name,
const String& style,
ComSmartPtr<IDWriteFont> font,
ComSmartPtr<IDWriteFontFace> face,
HbFont hbFontIn,
ComSmartPtr<IDWriteFontCollection> collectionIn = nullptr)
: Typeface (name, style),
collection (std::move (collectionIn)),
dwFont (font),
dwFontFace (face),
hbFont (std::move (hbFontIn))
{
if (collection != nullptr)
factories->getFonts().addCollection (collection);
}
SharedResourcePointer<Direct2DFactories> factories;
ComSmartPtr<IDWriteFontCollection> collection;
ComSmartPtr<IDWriteFont> dwFont;
ComSmartPtr<IDWriteFontFace> dwFontFace;
HbFont hbFont;
};
#endif
struct DefaultFontNames
{
DefaultFontNames()
{
if (juce_isRunningInWine())
{
// If we're running in Wine, then use fonts that might be available on Linux.
defaultSans = "Bitstream Vera Sans";
defaultSerif = "Bitstream Vera Serif";
defaultFixed = "Bitstream Vera Sans Mono";
}
else
{
defaultSans = "Verdana";
defaultSerif = "Times New Roman";
defaultFixed = "Lucida Console";
defaultFallback = "Tahoma"; // (contains plenty of unicode characters)
}
}
String defaultSans, defaultSerif, defaultFixed, defaultFallback;
};
Typeface::Ptr Font::getDefaultTypefaceForFont (const Font& font)
{
static DefaultFontNames defaultNames;
Font newFont (font);
auto faceName = font.getTypefaceName();
if (faceName == getDefaultSansSerifFontName()) newFont.setTypefaceName (defaultNames.defaultSans);
else if (faceName == getDefaultSerifFontName()) newFont.setTypefaceName (defaultNames.defaultSerif);
else if (faceName == getDefaultMonospacedFontName()) newFont.setTypefaceName (defaultNames.defaultFixed);
if (font.getTypefaceStyle() == getDefaultStyle())
newFont.setTypefaceStyle ("Regular");
return Typeface::createSystemTypefaceFor (newFont);
}
Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font)
{
return WindowsDirectWriteTypeface::from (font);
}
Typeface::Ptr Typeface::createSystemTypefaceFor (Span<const std::byte> data)
{
return WindowsDirectWriteTypeface::from (data);
}
void Typeface::scanFolderForFonts (const File&)
{
// TODO(reuk)
}
//==============================================================================
bool TextLayout::createNativeLayout ([[maybe_unused]] const AttributedString& text)
{
// TODO(reuk) Currently unimplemented
return false;
}
} // namespace juce

View file

@ -35,72 +35,23 @@
namespace juce
{
struct DefaultFontNames
{
DefaultFontNames()
: defaultSans ("sans"),
defaultSerif ("serif"),
defaultFixed ("monospace"),
defaultFallback ("sans")
{
}
String getRealFontName (const String& faceName) const
{
if (faceName == Font::getDefaultSansSerifFontName()) return defaultSans;
if (faceName == Font::getDefaultSerifFontName()) return defaultSerif;
if (faceName == Font::getDefaultMonospacedFontName()) return defaultFixed;
return faceName;
}
String defaultSans, defaultSerif, defaultFixed, defaultFallback;
};
Typeface::Ptr Font::getDefaultTypefaceForFont (const Font& font)
{
static DefaultFontNames defaultNames;
Font f (font);
f.setTypefaceName (defaultNames.getRealFontName (font.getTypefaceName()));
f.setTypefaceName ([&]() -> String
{
const auto faceName = font.getTypefaceName();
if (faceName == Font::getDefaultSansSerifFontName()) return "Roboto";
if (faceName == Font::getDefaultSerifFontName()) return "Roboto";
if (faceName == Font::getDefaultMonospacedFontName()) return "Roboto";
return faceName;
}());
return Typeface::createSystemTypefaceFor (f);
}
//==============================================================================
#if JUCE_USE_FREETYPE
StringArray FTTypefaceList::getDefaultFontDirectories()
{
return StringArray ("/system/fonts");
}
Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font)
{
return new FreeTypeTypeface (font);
}
void Typeface::scanFolderForFonts (const File& folder)
{
FTTypefaceList::getInstance()->scanFontPaths (StringArray (folder.getFullPathName()));
}
StringArray Font::findAllTypefaceNames()
{
return FTTypefaceList::getInstance()->findAllFamilyNames();
}
StringArray Font::findAllTypefaceStyles (const String& family)
{
return FTTypefaceList::getInstance()->findAllTypefaceStyles (family);
}
bool TextLayout::createNativeLayout (const AttributedString&)
{
return false;
}
#else
//==============================================================================
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
STATICMETHOD (create, "create", "(Ljava/lang/String;I)Landroid/graphics/Typeface;") \
@ -135,417 +86,363 @@ DECLARE_JNI_CLASS (AndroidRectF, "android/graphics/RectF")
DECLARE_JNI_CLASS (JavaMessageDigest, "java/security/MessageDigest")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
METHOD (open, "open", "(Ljava/lang/String;)Ljava/io/InputStream;") \
DECLARE_JNI_CLASS (AndroidAssetManager, "android/content/res/AssetManager")
#undef JNI_CLASS_MEMBERS
// Defined in juce_core
std::unique_ptr<InputStream> makeAndroidInputStreamWrapper (jobject stream);
//==============================================================================
class MemoryFontCache : public DeletedAtShutdown
{
public:
~MemoryFontCache()
{
clearSingletonInstance();
}
struct Key
{
String name, style;
auto tie() const { return std::tuple (name, style); }
bool operator< (const Key& other) const { return tie() < other.tie(); }
bool operator== (const Key& other) const { return tie() == other.tie(); }
};
void add (const Key& key, std::shared_ptr<hb_font_t> ptr)
{
const std::scoped_lock lock { mutex };
cache.emplace (key, ptr);
}
void remove (const Key& p)
{
const std::scoped_lock lock { mutex };
cache.erase (p);
}
std::set<String> getAllNames() const
{
const std::scoped_lock lock { mutex };
std::set<String> result;
for (const auto& item : cache)
result.insert (item.first.name);
return result;
}
std::set<String> getStylesForFamily (const String& family) const
{
const std::scoped_lock lock { mutex };
const auto lower = std::lower_bound (cache.begin(), cache.end(), family, [] (const auto& a, const String& b)
{
return a.first.name < b;
});
const auto upper = std::upper_bound (cache.begin(), cache.end(), family, [] (const String& a, const auto& b)
{
return a < b.first.name;
});
std::set<String> result;
for (const auto& item : makeRange (lower, upper))
result.insert (item.first.style);
return result;
}
std::shared_ptr<hb_font_t> find (const Key& key)
{
const std::scoped_lock lock { mutex };
const auto iter = cache.find (key);
if (iter != cache.end())
return iter->second;
return nullptr;
}
JUCE_DECLARE_SINGLETON (MemoryFontCache, true)
private:
std::map<Key, std::shared_ptr<hb_font_t>> cache;
mutable std::mutex mutex;
};
JUCE_IMPLEMENT_SINGLETON (MemoryFontCache)
StringArray Font::findAllTypefaceNames()
{
StringArray results;
auto results = [&]
{
if (auto* cache = MemoryFontCache::getInstance())
return cache->getAllNames();
return std::set<String>{};
}();
for (auto& f : File ("/system/fonts").findChildFiles (File::findFiles, false, "*.ttf"))
results.addIfNotAlreadyThere (f.getFileNameWithoutExtension().upToLastOccurrenceOf ("-", false, false));
results.insert (f.getFileNameWithoutExtension().upToLastOccurrenceOf ("-", false, false));
return results;
StringArray s;
for (const auto& family : results)
s.add (family);
return s;
}
StringArray Font::findAllTypefaceStyles (const String& family)
{
StringArray results ("Regular");
auto results = [&]
{
if (auto* cache = MemoryFontCache::getInstance())
return cache->getStylesForFamily (family);
return std::set<String>{};
}();
for (auto& f : File ("/system/fonts").findChildFiles (File::findFiles, false, family + "-*.ttf"))
results.addIfNotAlreadyThere (f.getFileNameWithoutExtension().fromLastOccurrenceOf ("-", false, false));
results.insert (f.getFileNameWithoutExtension().fromLastOccurrenceOf ("-", false, false));
return results;
StringArray s;
for (const auto& style : results)
s.add (style);
return s;
}
const float referenceFontSize = 256.0f;
const float referenceFontToUnits = 1.0f / referenceFontSize;
//==============================================================================
class AndroidTypeface final : public Typeface
{
public:
AndroidTypeface (const Font& font)
: Typeface (font.getTypefaceName(), font.getTypefaceStyle()),
ascent (0), descent (0), heightToPointsFactor (1.0f)
static Typeface::Ptr from (const Font& font)
{
JNIEnv* const env = getEnv();
if (auto* cache = MemoryFontCache::getInstance())
if (auto result = cache->find ({ font.getTypefaceName(), font.getTypefaceStyle() }))
return new AndroidTypeface (DoCache::no, result, font.getTypefaceName(), font.getTypefaceStyle());
// First check whether there's an embedded asset with this font name:
typeface = GlobalRef (getTypefaceFromAsset (name));
auto blob = getBlobForFont (font);
auto face = FontStyleHelpers::getFaceForBlob ({ static_cast<const char*> (blob.getData()), blob.getSize() }, 0);
if (typeface.get() == nullptr)
if (face == nullptr)
{
const bool isBold = style.contains ("Bold");
const bool isItalic = style.contains ("Italic");
File fontFile (getFontFile (name, style));
if (! fontFile.exists())
fontFile = findFontFile (name, isBold, isItalic);
if (fontFile.exists())
typeface = GlobalRef (LocalRef<jobject> (env->CallStaticObjectMethod (TypefaceClass, TypefaceClass.createFromFile,
javaString (fontFile.getFullPathName()).get())));
else
typeface = GlobalRef (LocalRef<jobject> (env->CallStaticObjectMethod (TypefaceClass, TypefaceClass.create,
javaString (getName()).get(),
(isBold ? 1 : 0) + (isItalic ? 2 : 0))));
jassertfalse;
return {};
}
initialise (env);
HbFont hbFont { hb_font_create (face.get()) };
FontStyleHelpers::initSynthetics (hbFont.get(), font);
return new AndroidTypeface (DoCache::no, std::move (hbFont), font.getTypefaceName(), font.getTypefaceStyle());
}
AndroidTypeface (const void* data, size_t size)
: Typeface (String (static_cast<uint64> (reinterpret_cast<uintptr_t> (data))), String())
enum class DoCache
{
auto* env = getEnv();
auto cacheFile = getCacheFileForData (data, size);
no,
yes
};
typeface = GlobalRef (LocalRef<jobject> (env->CallStaticObjectMethod (TypefaceClass, TypefaceClass.createFromFile,
javaString (cacheFile.getFullPathName()).get())));
initialise (env);
}
void initialise (JNIEnv* const env)
static Typeface::Ptr from (Span<const std::byte> blob, unsigned int index = 0)
{
rect = GlobalRef (LocalRef<jobject> (env->NewObject (AndroidRect, AndroidRect.constructor, 0, 0, 0, 0)));
paint = GlobalRef (GraphicsHelpers::createPaint (Graphics::highResamplingQuality));
const LocalRef<jobject> ignored (paint.callObjectMethod (AndroidPaint.setTypeface, typeface.get()));
charArray = GlobalRef (LocalRef<jobject> ((jobject) env->NewCharArray (2)));
paint.callVoidMethod (AndroidPaint.setTextSize, referenceFontSize);
const float fullAscent = std::abs (paint.callFloatMethod (AndroidPaint.ascent));
const float fullDescent = paint.callFloatMethod (AndroidPaint.descent);
const float totalHeight = fullAscent + fullDescent;
ascent = fullAscent / totalHeight;
descent = fullDescent / totalHeight;
heightToPointsFactor = referenceFontSize / totalHeight;
return fromMemory (DoCache::yes, blob, index);
}
float getAscent() const override { return ascent; }
float getDescent() const override { return descent; }
float getHeightToPointsFactor() const override { return heightToPointsFactor; }
Native getNativeDetails() const override
{
return Native { hbFont.get() };
}
~AndroidTypeface() override
{
if (doCache == DoCache::yes)
if (auto* c = MemoryFontCache::getInstance())
c->remove ({ getName(), getStyle() });
}
float getStringWidth (const String& text) override
{
JNIEnv* env = getEnv();
auto numChars = CharPointer_UTF16::getBytesRequiredFor (text.getCharPointer());
jfloatArray widths = env->NewFloatArray ((int) numChars);
const auto heightToPoints = getNativeDetails().getLegacyMetrics().getHeightToPointsFactor();
const auto upem = hb_face_get_upem (hb_font_get_face (hbFont.get()));
const int numDone = paint.callIntMethod (AndroidPaint.getTextWidths, javaString (text).get(), widths);
HeapBlock<jfloat> localWidths (static_cast<size_t> (numDone));
env->GetFloatArrayRegion (widths, 0, numDone, localWidths);
env->DeleteLocalRef (widths);
float x = 0;
for (int i = 0; i < numDone; ++i)
x += localWidths[i];
return x * referenceFontToUnits * heightToPointsFactor;
hb_position_t x{};
doSimpleShape (text, [&] (const auto&, const auto& position)
{
x += position.x_advance;
});
return heightToPoints * (float) x / (float) upem;
}
void getGlyphPositions (const String& text, Array<int>& glyphs, Array<float>& xOffsets) override
{
JNIEnv* env = getEnv();
auto numChars = CharPointer_UTF16::getBytesRequiredFor (text.getCharPointer());
jfloatArray widths = env->NewFloatArray ((int) numChars);
const auto heightToPoints = getNativeDetails().getLegacyMetrics().getHeightToPointsFactor();
const auto upem = hb_face_get_upem (hb_font_get_face (hbFont.get()));
const int numDone = paint.callIntMethod (AndroidPaint.getTextWidths, javaString (text).get(), widths);
Point<hb_position_t> cursor{};
HeapBlock<jfloat> localWidths (static_cast<size_t> (numDone));
env->GetFloatArrayRegion (widths, 0, numDone, localWidths);
env->DeleteLocalRef (widths);
auto s = text.getCharPointer();
xOffsets.add (0);
float x = 0;
for (int i = 0; i < numDone; ++i)
doSimpleShape (text, [&] (const auto& info, const auto& position)
{
const float local = localWidths[i];
glyphs.add ((int) info.codepoint);
xOffsets.add (heightToPoints * ((float) cursor.x + (float) position.x_offset) / (float) upem);
cursor += Point { position.x_advance, position.y_advance };
});
// Android uses jchar (UTF-16) characters
jchar ch = (jchar) s.getAndAdvance();
// Android has no proper glyph support, so we have to do
// a hacky workaround for ligature detection
#if JUCE_STRING_UTF_TYPE <= 16
static_assert (sizeof (int) >= (sizeof (jchar) * 2), "Unable store two java chars in one glyph");
// if the width of this glyph is zero inside the string but has
// a width on it's own, then it's probably due to ligature
if (local == 0.0f && glyphs.size() > 0 && getStringWidth (String (ch)) > 0.0f)
{
// modify the previous glyph
int& glyphNumber = glyphs.getReference (glyphs.size() - 1);
// make sure this is not a three character ligature
if (glyphNumber < std::numeric_limits<jchar>::max())
{
const unsigned int previousGlyph
= static_cast<unsigned int> (glyphNumber) & ((1U << (sizeof (jchar) * 8U)) - 1U);
const unsigned int thisGlyph
= static_cast<unsigned int> (ch) & ((1U << (sizeof (jchar) * 8U)) - 1U);
glyphNumber = static_cast<int> ((thisGlyph << (sizeof (jchar) * 8U)) | previousGlyph);
ch = 0;
}
}
#endif
glyphs.add ((int) ch);
x += local;
xOffsets.add (x * referenceFontToUnits * heightToPointsFactor);
}
xOffsets.add (heightToPoints * (float) cursor.x / (float) upem);
}
bool getOutlineForGlyph (int /*glyphNumber*/, Path& /*destPath*/) override
{
return false;
}
EdgeTable* getEdgeTableForGlyph (int glyphNumber, const AffineTransform& t, float /*fontHeight*/) override
{
#if JUCE_STRING_UTF_TYPE <= 16
static_assert (sizeof (int) >= (sizeof (jchar) * 2), "Unable store two jni chars in one int");
// glyphNumber of zero is used to indicate that the last character was a ligature
if (glyphNumber == 0) return nullptr;
jchar ch1 = (static_cast<unsigned int> (glyphNumber) >> 0) & ((1U << (sizeof (jchar) * 8U)) - 1U);
jchar ch2 = (static_cast<unsigned int> (glyphNumber) >> (sizeof (jchar) * 8U)) & ((1U << (sizeof (jchar) * 8U)) - 1U);
#else
jchar ch1 = glyphNumber, ch2 = 0;
#endif
Rectangle<int> bounds;
auto* env = getEnv();
{
LocalRef<jobject> matrix (GraphicsHelpers::createMatrix (env, AffineTransform::scale (referenceFontToUnits * heightToPointsFactor).followedBy (t)));
jboolean isCopy;
auto* buffer = env->GetCharArrayElements ((jcharArray) charArray.get(), &isCopy);
buffer[0] = ch1; buffer[1] = ch2;
env->ReleaseCharArrayElements ((jcharArray) charArray.get(), buffer, 0);
LocalRef<jobject> path (env->NewObject (AndroidPath, AndroidPath.constructor));
LocalRef<jobject> boundsF (env->NewObject (AndroidRectF, AndroidRectF.constructor));
env->CallVoidMethod (paint.get(), AndroidPaint.getCharsPath, charArray.get(), 0, (ch2 != 0 ? 2 : 1), 0.0f, 0.0f, path.get());
env->CallVoidMethod (path.get(), AndroidPath.computeBounds, boundsF.get(), 1);
env->CallBooleanMethod (matrix.get(), AndroidMatrix.mapRect, boundsF.get());
env->CallVoidMethod (boundsF.get(), AndroidRectF.roundOut, rect.get());
bounds = Rectangle<int>::leftTopRightBottom (env->GetIntField (rect.get(), AndroidRect.left) - 1,
env->GetIntField (rect.get(), AndroidRect.top),
env->GetIntField (rect.get(), AndroidRect.right) + 1,
env->GetIntField (rect.get(), AndroidRect.bottom));
auto w = bounds.getWidth();
auto h = jmax (1, bounds.getHeight());
LocalRef<jobject> bitmapConfig (env->CallStaticObjectMethod (AndroidBitmapConfig, AndroidBitmapConfig.valueOf, javaString ("ARGB_8888").get()));
LocalRef<jobject> bitmap (env->CallStaticObjectMethod (AndroidBitmap, AndroidBitmap.createBitmap, w, h, bitmapConfig.get()));
LocalRef<jobject> canvas (env->NewObject (AndroidCanvas, AndroidCanvas.create, bitmap.get()));
env->CallBooleanMethod (matrix.get(), AndroidMatrix.postTranslate, (float) -bounds.getX(), (float) -bounds.getY());
env->CallVoidMethod (canvas.get(), AndroidCanvas.setMatrix, matrix.get());
env->CallVoidMethod (canvas.get(), AndroidCanvas.drawPath, path.get(), paint.get());
int requiredRenderArraySize = w * h;
if (requiredRenderArraySize > lastCachedRenderArraySize)
{
cachedRenderArray = GlobalRef (LocalRef<jobject> ((jobject) env->NewIntArray (requiredRenderArraySize)));
lastCachedRenderArraySize = requiredRenderArraySize;
}
env->CallVoidMethod (bitmap.get(), AndroidBitmap.getPixels, cachedRenderArray.get(), 0, w, 0, 0, w, h);
env->CallVoidMethod (bitmap.get(), AndroidBitmap.recycle);
}
EdgeTable* et = nullptr;
if (! bounds.isEmpty())
{
et = new EdgeTable (bounds);
jint* const maskDataElements = env->GetIntArrayElements ((jintArray) cachedRenderArray.get(), nullptr);
const jint* mask = maskDataElements;
for (int y = bounds.getY(); y < bounds.getBottom(); ++y)
{
#if JUCE_LITTLE_ENDIAN
const uint8* const lineBytes = ((const uint8*) mask) + 3;
#else
const uint8* const lineBytes = (const uint8*) mask;
#endif
et->clipLineToMask (bounds.getX(), y, lineBytes, 4, bounds.getWidth());
mask += bounds.getWidth();
}
env->ReleaseIntArrayElements ((jintArray) cachedRenderArray.get(), maskDataElements, 0);
}
return et;
}
GlobalRef typeface, paint, rect, charArray, cachedRenderArray;
float ascent, descent, heightToPointsFactor;
int lastCachedRenderArraySize = -1;
private:
static File findFontFile (const String& family,
const bool bold, const bool italic)
template <typename Consumer>
void doSimpleShape (const String& text, Consumer&& consumer)
{
File file;
HbBuffer buffer { hb_buffer_create() };
hb_buffer_add_utf8 (buffer.get(), text.toRawUTF8(), -1, 0, -1);
hb_buffer_guess_segment_properties (buffer.get());
if (bold || italic)
{
String suffix;
if (bold) suffix = "Bold";
if (italic) suffix << "Italic";
hb_shape (hbFont.get(), buffer.get(), nullptr, 0);
file = getFontFile (family, suffix);
unsigned int numGlyphs{};
auto* infos = hb_buffer_get_glyph_infos (buffer.get(), &numGlyphs);
auto* positions = hb_buffer_get_glyph_positions (buffer.get(), &numGlyphs);
if (file.exists())
return file;
}
for (auto i = decltype (numGlyphs){}; i < numGlyphs; ++i)
consumer (infos[i], positions[i]);
}
file = getFontFile (family, "Regular");
static Typeface::Ptr fromMemory (DoCache cache, Span<const std::byte> blob, unsigned int index = 0)
{
auto face = FontStyleHelpers::getFaceForBlob ({ reinterpret_cast<const char*> (blob.data()), blob.size() }, index);
if (face == nullptr)
return {};
return new AndroidTypeface (cache,
HbFont { hb_font_create (face.get()) },
readFontName (face.get(), HB_OT_NAME_ID_FONT_FAMILY, nullptr),
{});
}
static String readFontName (hb_face_t* face, hb_ot_name_id_t nameId, hb_language_t language)
{
unsigned int textSize{};
textSize = hb_ot_name_get_utf8 (face, nameId, language, &textSize, nullptr);
std::vector<char> nameString (textSize + 1, 0);
textSize = (unsigned int) nameString.size();
hb_ot_name_get_utf8 (face, nameId, language, &textSize, nameString.data());
return nameString.data();
}
AndroidTypeface (DoCache cache, std::shared_ptr<hb_font_t> fontIn, const String& name, const String& style)
: Typeface (name, style),
hbFont (std::move (fontIn)),
doCache (cache)
{
if (doCache == DoCache::yes)
if (auto* c = MemoryFontCache::getInstance())
c->add ({ name, style }, hbFont);
}
static MemoryBlock getBlobForFont (const Font& font)
{
auto memory = loadFontAsset (font.getTypefaceName());
if (! memory.isEmpty())
return memory;
const auto file = findFontFile (font);
if (! file.exists())
file = getFontFile (family, String());
{
// Failed to find file corresponding to this font
jassertfalse;
return {};
}
return file;
FileInputStream stream { file };
MemoryBlock result;
stream.readIntoMemoryBlock (result);
return stream.isExhausted() ? result : MemoryBlock{};
}
static File findFontFile (const Font& font)
{
const String styles[] { font.getTypefaceStyle(),
FontStyleHelpers::getStyleName (font.isBold(), font.isItalic()),
{} };
for (const auto& style : styles)
if (auto file = getFontFile (font.getTypefaceName(), style); file.exists())
return file;
for (auto& file : File ("/system/fonts").findChildFiles (File::findFiles, false, "*.ttf"))
if (file.getFileName().startsWith (font.getTypefaceName()))
return file;
return {};
}
static File getFontFile (const String& family, const String& fontStyle)
{
String path ("/system/fonts/" + family);
if (fontStyle.isNotEmpty())
path << '-' << fontStyle;
return File (path + ".ttf");
return "/system/fonts/" + family + (fontStyle.isNotEmpty() ? ("-" + fontStyle) : String{}) + ".ttf";
}
static LocalRef<jobject> getTypefaceFromAsset (const String& typefaceName)
static MemoryBlock loadFontAsset (const String& typefaceName)
{
auto* env = getEnv();
LocalRef<jobject> assetManager (env->CallObjectMethod (getAppContext().get(), AndroidContext.getAssets));
const LocalRef<jobject> assetManager { env->CallObjectMethod (getAppContext().get(), AndroidContext.getAssets) };
if (assetManager == nullptr)
return LocalRef<jobject>();
return {};
auto assetTypeface = env->CallStaticObjectMethod (TypefaceClass, TypefaceClass.createFromAsset, assetManager.get(),
javaString ("fonts/" + typefaceName).get());
const LocalRef<jobject> inputStream { env->CallObjectMethod (assetManager,
AndroidAssetManager.open,
javaString ("fonts/" + typefaceName).get()) };
// this may throw
if (env->ExceptionCheck() != 0)
{
env->ExceptionClear();
return LocalRef<jobject>();
}
// Opening an input stream for an asset might throw if the asset isn't found
jniCheckHasExceptionOccurredAndClear();
return LocalRef<jobject> (assetTypeface);
if (inputStream == nullptr)
return {};
auto streamWrapper = makeAndroidInputStreamWrapper (inputStream);
if (streamWrapper == nullptr)
return {};
MemoryBlock result;
streamWrapper->readIntoMemoryBlock (result);
return streamWrapper->isExhausted() ? result : MemoryBlock{};
}
static File getCacheDirectory()
{
static File result = []()
{
auto appContext = getAppContext();
if (appContext != nullptr)
{
auto* env = getEnv();
LocalRef<jobject> cacheFile (env->CallObjectMethod (appContext.get(), AndroidContext.getCacheDir));
LocalRef<jstring> jPath ((jstring) env->CallObjectMethod (cacheFile.get(), JavaFile.getAbsolutePath));
return File (juceString (env, jPath.get()));
}
jassertfalse;
return File();
}();
return result;
}
static HashMap<String, File>& getInMemoryFontCache()
{
static HashMap<String, File> cache;
return cache;
}
static File getCacheFileForData (const void* data, size_t size)
{
static CriticalSection cs;
JNIEnv* const env = getEnv();
String key;
{
LocalRef<jobject> digest (env->CallStaticObjectMethod (JavaMessageDigest, JavaMessageDigest.getInstance, javaString ("MD5").get()));
LocalRef<jbyteArray> bytes (env->NewByteArray ((int) size));
jboolean ignore;
auto* jbytes = env->GetByteArrayElements (bytes.get(), &ignore);
memcpy (jbytes, data, size);
env->ReleaseByteArrayElements (bytes.get(), jbytes, 0);
env->CallVoidMethod (digest.get(), JavaMessageDigest.update, bytes.get());
LocalRef<jbyteArray> result ((jbyteArray) env->CallObjectMethod (digest.get(), JavaMessageDigest.digest));
auto* md5Bytes = env->GetByteArrayElements (result.get(), &ignore);
key = String::toHexString (md5Bytes, env->GetArrayLength (result.get()), 0);
env->ReleaseByteArrayElements (result.get(), md5Bytes, 0);
}
ScopedLock lock (cs);
auto& mapEntry = getInMemoryFontCache().getReference (key);
if (mapEntry == File())
{
mapEntry = getCacheDirectory().getChildFile ("bindata_" + key);
mapEntry.replaceWithData (data, size);
}
return mapEntry;
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AndroidTypeface)
std::shared_ptr<hb_font_t> hbFont;
DoCache doCache;
};
//==============================================================================
Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font)
{
return new AndroidTypeface (font);
return AndroidTypeface::from (font);
}
Typeface::Ptr Typeface::createSystemTypefaceFor (const void* data, size_t size)
Typeface::Ptr Typeface::createSystemTypefaceFor (Span<const std::byte> data)
{
return new AndroidTypeface (data, size);
return AndroidTypeface::from (data);
}
void Typeface::scanFolderForFonts (const File&)
{
jassertfalse; // not available unless using FreeType
jassertfalse; // not currently available
}
bool TextLayout::createNativeLayout (const AttributedString&)
@ -553,6 +450,4 @@ bool TextLayout::createNativeLayout (const AttributedString&)
return false;
}
#endif
} // namespace juce

View file

@ -32,6 +32,10 @@
==============================================================================
*/
#include FT_TRUETYPE_TABLES_H
#include FT_GLYPH_H
#include FT_COLOR_H
namespace juce
{
@ -62,41 +66,62 @@ struct FTLibWrapper final : public ReferenceCountedObject
//==============================================================================
struct FTFaceWrapper final : public ReferenceCountedObject
{
FTFaceWrapper (const FTLibWrapper::Ptr& ftLib, const File& file, int faceIndex)
: library (ftLib)
using Ptr = ReferenceCountedObjectPtr<FTFaceWrapper>;
static FTFaceWrapper::Ptr selectUnicodeCharmap (FTFaceWrapper::Ptr face)
{
if (FT_New_Face (ftLib->library, file.getFullPathName().toUTF8(), faceIndex, &face) != 0)
face = {};
if (face != nullptr)
if (FT_Select_Charmap (face->face, ft_encoding_unicode) != 0)
FT_Set_Charmap (face->face, face->face->charmaps[0]);
return face;
}
FTFaceWrapper (const FTLibWrapper::Ptr& ftLib, const void* data, size_t dataSize, int faceIndex)
: library (ftLib), savedFaceData (data, dataSize)
static FTFaceWrapper::Ptr from (const FTLibWrapper::Ptr& ftLib, const File& file, int faceIndex)
{
if (FT_New_Memory_Face (ftLib->library, (const FT_Byte*) savedFaceData.getData(),
(FT_Long) savedFaceData.getSize(), faceIndex, &face) != 0)
face = {};
FT_Face result{};
if (FT_New_Face (ftLib->library, file.getFullPathName().toUTF8(), faceIndex, &result) != 0)
return {};
return selectUnicodeCharmap (new FTFaceWrapper (ftLib, result));
}
static FTFaceWrapper::Ptr from (const FTLibWrapper::Ptr& ftLib, const void* data, size_t dataSize, int faceIndex)
{
MemoryBlock storage (data, dataSize);
FT_Face result{};
if (FT_New_Memory_Face (ftLib->library,
static_cast<const FT_Byte*> (storage.getData()),
(FT_Long) storage.getSize(),
faceIndex,
&result) != 0)
return {};
return selectUnicodeCharmap (new FTFaceWrapper (ftLib, result, std::move (storage)));
}
FTFaceWrapper (const FTLibWrapper::Ptr& ftLib, FT_Face faceIn, MemoryBlock mb = {})
: library (ftLib), savedFaceData (std::move (mb)), face (faceIn) {}
~FTFaceWrapper()
{
if (face != nullptr)
FT_Done_Face (face);
}
FT_Face face = {};
FTLibWrapper::Ptr library;
MemoryBlock savedFaceData;
FT_Face face = {};
using Ptr = ReferenceCountedObjectPtr<FTFaceWrapper>;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FTFaceWrapper)
JUCE_HEAVYWEIGHT_LEAK_DETECTOR(FTFaceWrapper)
};
//==============================================================================
class FTTypefaceList final : private DeletedAtShutdown
{
public:
FTTypefaceList() : library (new FTLibWrapper())
FTTypefaceList()
{
scanFontPaths (getDefaultFontDirectories());
}
@ -109,42 +134,76 @@ public:
//==============================================================================
struct KnownTypeface
{
KnownTypeface (const File& f, int index, const FTFaceWrapper& face)
: file (f),
family (face.face->family_name),
explicit KnownTypeface (const FTFaceWrapper& face)
: family (face.face->family_name),
style (face.face->style_name),
faceIndex (index),
isMonospaced ((face.face->face_flags & FT_FACE_FLAG_FIXED_WIDTH) != 0),
isSansSerif (isFaceSansSerif (family))
faceIndex ((int) face.face->face_index),
flags (((face.face->style_flags & FT_STYLE_FLAG_BOLD) ? bold : 0)
| ((face.face->style_flags & FT_STYLE_FLAG_ITALIC) ? italic : 0)
| ((face.face->face_flags & FT_FACE_FLAG_FIXED_WIDTH) ? monospaced : 0)
| (isFaceSansSerif (family) ? sansSerif : 0))
{
}
const File file;
virtual ~KnownTypeface() = default;
virtual FTFaceWrapper::Ptr create (FTLibWrapper::Ptr) const = 0;
virtual bool holdsFace (FTFaceWrapper::Ptr) const { return false; }
enum Flag
{
bold = 1 << 0,
italic = 1 << 1,
monospaced = 1 << 2,
sansSerif = 1 << 3,
};
const String family, style;
const int faceIndex;
const bool isMonospaced, isSansSerif;
const int flags;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (KnownTypeface)
};
//==============================================================================
static FTFaceWrapper::Ptr selectUnicodeCharmap (FTFaceWrapper* face)
struct FileTypeface : public KnownTypeface
{
if (face != nullptr)
if (FT_Select_Charmap (face->face, ft_encoding_unicode) != 0)
FT_Set_Charmap (face->face, face->face->charmaps[0]);
FileTypeface (const FTFaceWrapper& face, const File& fileIn)
: KnownTypeface (face), file (fileIn) {}
return face;
}
FTFaceWrapper::Ptr create (FTLibWrapper::Ptr lib) const override
{
return FTFaceWrapper::from (lib, file, faceIndex);
}
const File file;
};
struct CachedTypeface : public KnownTypeface
{
explicit CachedTypeface (FTFaceWrapper::Ptr ptr)
: KnownTypeface (*ptr), face (ptr) {}
FTFaceWrapper::Ptr create (FTLibWrapper::Ptr) const override
{
return face;
}
bool holdsFace (FTFaceWrapper::Ptr p) const override
{
return face == p;
}
FTFaceWrapper::Ptr face;
};
//==============================================================================
FTFaceWrapper::Ptr createFace (const void* data, size_t dataSize, int index)
{
return selectUnicodeCharmap (new FTFaceWrapper (library, data, dataSize, index));
return FTFaceWrapper::from (library, data, dataSize, index);
}
FTFaceWrapper::Ptr createFace (const File& file, int index)
{
return selectUnicodeCharmap (new FTFaceWrapper (library, file, index));
return FTFaceWrapper::from (library, file, index);
}
FTFaceWrapper::Ptr createFace (const String& fontName, const String& fontStyle)
@ -155,7 +214,7 @@ public:
if (ftFace == nullptr) ftFace = matchTypeface (fontName, {});
if (ftFace != nullptr)
return createFace (ftFace->file, ftFace->faceIndex);
return ftFace->create (library);
return nullptr;
}
@ -165,7 +224,7 @@ public:
{
std::set<String> set;
for (auto* face : faces)
for (const auto& face : faces)
set.insert (face->family);
StringArray s;
@ -180,7 +239,7 @@ public:
{
StringArray s;
for (auto* face : faces)
for (const auto& face : faces)
if (face->family == family)
s.addIfNotAlreadyThere (face->style);
@ -199,38 +258,14 @@ public:
}
}
std::sort (faces.begin(), faces.end(), [] (const auto* a, const auto* b)
std::sort (faces.begin(), faces.end(), [] (const auto& a, const auto& b)
{
const auto tie = [] (const KnownTypeface& t)
{
// Used to order styles like "Regular", "Roman" etc. before "Bold", "Italic", etc.
const auto computeStyleNormalcy = [] (const String& style)
{
if (style == "Regular")
return 0;
if (style == "Roman")
return 1;
if (style == "Book")
return 2;
if (style.containsIgnoreCase ("Bold"))
return 3;
if (style.containsIgnoreCase ("Italic"))
return 4;
return 5;
};
return std::make_tuple (t.family,
computeStyleNormalcy (t.style),
t.flags,
t.style,
t.isSansSerif,
t.isMonospaced,
t.faceIndex,
t.file);
t.faceIndex);
};
return tie (*a) < tie (*b);
@ -239,30 +274,46 @@ public:
void getMonospacedNames (StringArray& monoSpaced) const
{
for (auto* face : faces)
if (face->isMonospaced)
for (const auto& face : faces)
if (face->flags & KnownTypeface::monospaced)
monoSpaced.addIfNotAlreadyThere (face->family);
}
void getSerifNames (StringArray& serif) const
{
for (auto* face : faces)
if (! (face->isSansSerif || face->isMonospaced))
for (const auto& face : faces)
if ((face->flags & (KnownTypeface::sansSerif | KnownTypeface::monospaced)) == 0)
serif.addIfNotAlreadyThere (face->family);
}
void getSansSerifNames (StringArray& sansSerif) const
{
for (auto* face : faces)
if (face->isSansSerif)
for (const auto& face : faces)
if (face->flags & KnownTypeface::sansSerif)
sansSerif.addIfNotAlreadyThere (face->family);
}
void addMemoryFace (FTFaceWrapper::Ptr ptr)
{
faces.insert (faces.begin(), std::make_unique<CachedTypeface> (ptr));
}
void removeMemoryFace (FTFaceWrapper::Ptr ptr)
{
const auto iter = std::find_if (faces.begin(), faces.end(), [&] (const auto& face)
{
return face->holdsFace (ptr);
});
if (iter != faces.end())
faces.erase (iter);
}
JUCE_DECLARE_SINGLETON_SINGLETHREADED_MINIMAL (FTTypefaceList)
private:
FTLibWrapper::Ptr library;
OwnedArray<KnownTypeface> faces;
FTLibWrapper::Ptr library = new FTLibWrapper;
std::vector<std::unique_ptr<KnownTypeface>> faces;
static StringArray getDefaultFontDirectories();
@ -273,15 +324,16 @@ private:
do
{
FTFaceWrapper face (library, file, faceIndex);
if (face.face != nullptr)
if (auto face = FTFaceWrapper::from (library, file, faceIndex))
{
if (faceIndex == 0)
numFaces = (int) face.face->num_faces;
if (face->face != nullptr)
{
if (faceIndex == 0)
numFaces = (int) face->face->num_faces;
if ((face.face->face_flags & FT_FACE_FLAG_SCALABLE) != 0)
faces.add (new KnownTypeface (file, faceIndex, face));
if ((face->face->face_flags & FT_FACE_FLAG_SCALABLE) != 0)
faces.push_back (std::make_unique<FileTypeface> (*face, file));
}
}
++faceIndex;
@ -291,10 +343,10 @@ private:
const KnownTypeface* matchTypeface (const String& familyName, const String& style) const noexcept
{
for (auto* face : faces)
for (const auto& face : faces)
if (face->family == familyName
&& (face->style.equalsIgnoreCase (style) || style.isEmpty()))
return face;
return face.get();
return nullptr;
}
@ -315,173 +367,164 @@ private:
JUCE_IMPLEMENT_SINGLETON (FTTypefaceList)
//==============================================================================
class FreeTypeTypeface final : public CustomTypeface
class FreeTypeTypeface final : public Typeface
{
using Ptr = ReferenceCountedObjectPtr<FreeTypeTypeface>;
enum class DoCache
{
no,
yes,
};
public:
FreeTypeTypeface (const Font& font)
: faceWrapper (FTTypefaceList::getInstance()->createFace (font.getTypefaceName(),
font.getTypefaceStyle()))
static Typeface::Ptr from (const Font& font)
{
if (faceWrapper != nullptr)
initialiseCharacteristics (font.getTypefaceName(),
font.getTypefaceStyle());
const auto name = font.getTypefaceName();
const auto style = font.getTypefaceStyle();
auto face = FTTypefaceList::getInstance()->createFace (name, style);
if (face == nullptr)
return {};
auto* hbFace = hb_ft_face_create_referenced (face->face);
const ScopeGuard scope { [&] { hb_face_destroy (hbFace); } };
HbFont hb { hb_font_create (hbFace) };
if (hb == nullptr)
return {};
FontStyleHelpers::initSynthetics (hb.get(), font);
return new FreeTypeTypeface (DoCache::no, face, std::move (hb), name, style);
}
FreeTypeTypeface (const void* data, size_t dataSize)
: faceWrapper (FTTypefaceList::getInstance()->createFace (data, dataSize, 0))
static Typeface::Ptr from (Span<const std::byte> data, int index = 0)
{
if (faceWrapper != nullptr)
initialiseCharacteristics (faceWrapper->face->family_name,
faceWrapper->face->style_name);
auto face = FTTypefaceList::getInstance()->createFace (data.data(), data.size(), index);
if (face == nullptr)
return {};
auto* hbFace = hb_ft_face_create_referenced (face->face);
const ScopeGuard scope { [&] { hb_face_destroy (hbFace); } };
HbFont hb { hb_font_create (hbFace) };
if (hb == nullptr)
return {};
return new FreeTypeTypeface (DoCache::yes, face, std::move (hb), face->face->family_name, face->face->style_name);
}
void initialiseCharacteristics (const String& fontName, const String& fontStyle)
Native getNativeDetails() const override
{
setCharacteristics (fontName, fontStyle,
faceWrapper->face->ascender / (float) (faceWrapper->face->ascender - faceWrapper->face->descender),
L' ');
return Native { hb.get() };
}
bool loadGlyphIfPossible (const juce_wchar character) override
~FreeTypeTypeface() override
{
if (faceWrapper != nullptr)
if (doCache == DoCache::yes)
if (auto* list = FTTypefaceList::getInstance())
list->removeMemoryFace (ftFace);
}
float getStringWidth (const String& text) override
{
const auto heightToPoints = getNativeDetails().getLegacyMetrics().getHeightToPointsFactor();
float x = 0;
for (auto iter = text.begin(), end = text.end(); iter != end; ++iter)
{
auto face = faceWrapper->face;
auto glyphIndex = FT_Get_Char_Index (face, (FT_ULong) character);
const auto currentChar = *iter;
const auto nextIter = iter + 1;
const auto nextChar = nextIter == end ? 0 : *nextIter;
if (FT_Load_Glyph (face, glyphIndex, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP | FT_LOAD_IGNORE_TRANSFORM | FT_LOAD_NO_HINTING) == 0
&& face->glyph->format == ft_glyph_format_outline)
{
auto scale = 1.0f / (float) (face->ascender - face->descender);
Path destShape;
if (getGlyphShape (destShape, face->glyph->outline, scale))
{
addGlyph (character, destShape, (float) face->glyph->metrics.horiAdvance * scale);
if ((face->face_flags & FT_FACE_FLAG_KERNING) != 0)
addKerning (face, (uint32) character, glyphIndex);
return true;
}
}
x += getSpacingForGlyphs (getNominalGlyphForCharacter (currentChar),
getNominalGlyphForCharacter (nextChar));
}
return false;
return x * heightToPoints;
}
void getGlyphPositions (const String& text, Array<int>& resultGlyphs, Array<float>& xOffsets) override
{
for (auto c : text)
resultGlyphs.add ((int) getNominalGlyphForCharacter (c));
const auto heightToPoints = getNativeDetails().getLegacyMetrics().getHeightToPointsFactor();
xOffsets.add (0);
for (auto iter = resultGlyphs.begin(); iter != resultGlyphs.end(); ++iter)
{
const auto currentGlyph = *iter;
const auto nextIter = iter + 1;
const auto nextGlyph = nextIter == resultGlyphs.end() ? 0 : *nextIter;
xOffsets.add (xOffsets.getLast() + getSpacingForGlyphs ((FT_UInt) currentGlyph, (FT_UInt) nextGlyph) * heightToPoints);
}
}
private:
FTFaceWrapper::Ptr faceWrapper;
bool getGlyphShape (Path& destShape, const FT_Outline& outline, float scaleX)
FreeTypeTypeface (DoCache cache,
FTFaceWrapper::Ptr ftFaceIn,
HbFont hbIn,
const String& nameIn,
const String& styleIn)
: Typeface (nameIn, styleIn),
ftFace (ftFaceIn),
hb (std::move (hbIn)),
doCache (cache)
{
auto scaleY = -scaleX;
auto* contours = outline.contours;
auto* tags = outline.tags;
auto* points = outline.points;
for (int c = 0; c < outline.n_contours; ++c)
{
const int startPoint = (c == 0) ? 0 : contours [c - 1] + 1;
const int endPoint = contours[c];
for (int p = startPoint; p <= endPoint; ++p)
{
auto x = scaleX * (float) points[p].x;
auto y = scaleY * (float) points[p].y;
if (p == startPoint)
{
if (FT_CURVE_TAG (tags[p]) == FT_Curve_Tag_Conic)
{
auto x2 = scaleX * (float) points[endPoint].x;
auto y2 = scaleY * (float) points[endPoint].y;
if (FT_CURVE_TAG (tags[endPoint]) != FT_Curve_Tag_On)
{
x2 = (x + x2) * 0.5f;
y2 = (y + y2) * 0.5f;
}
destShape.startNewSubPath (x2, y2);
}
else
{
destShape.startNewSubPath (x, y);
}
}
if (FT_CURVE_TAG (tags[p]) == FT_Curve_Tag_On)
{
if (p != startPoint)
destShape.lineTo (x, y);
}
else if (FT_CURVE_TAG (tags[p]) == FT_Curve_Tag_Conic)
{
const int nextIndex = (p == endPoint) ? startPoint : p + 1;
auto x2 = scaleX * (float) points[nextIndex].x;
auto y2 = scaleY * (float) points[nextIndex].y;
if (FT_CURVE_TAG (tags [nextIndex]) == FT_Curve_Tag_Conic)
{
x2 = (x + x2) * 0.5f;
y2 = (y + y2) * 0.5f;
}
else
{
++p;
}
destShape.quadraticTo (x, y, x2, y2);
}
else if (FT_CURVE_TAG (tags[p]) == FT_Curve_Tag_Cubic)
{
const int next1 = p + 1;
const int next2 = (p == (endPoint - 1)) ? startPoint : (p + 2);
if (p >= endPoint
|| FT_CURVE_TAG (tags[next1]) != FT_Curve_Tag_Cubic
|| FT_CURVE_TAG (tags[next2]) != FT_Curve_Tag_On)
return false;
auto x2 = scaleX * (float) points[next1].x;
auto y2 = scaleY * (float) points[next1].y;
auto x3 = scaleX * (float) points[next2].x;
auto y3 = scaleY * (float) points[next2].y;
destShape.cubicTo (x, y, x2, y2, x3, y3);
p += 2;
}
}
destShape.closeSubPath();
}
return true;
if (doCache == DoCache::yes)
if (auto* list = FTTypefaceList::getInstance())
list->addMemoryFace (ftFace);
}
void addKerning (FT_Face face, const uint32 character, const uint32 glyphIndex)
float getKerningForGlyphs (FT_UInt a, FT_UInt b) const
{
auto height = (float) (face->ascender - face->descender);
FT_Vector kerning{};
uint32 rightGlyphIndex;
auto rightCharCode = FT_Get_First_Char (face, &rightGlyphIndex);
if (FT_Get_Kerning (ftFace->face, a, b, FT_KERNING_UNSCALED, &kerning) != 0)
return {};
while (rightGlyphIndex != 0)
{
FT_Vector kerning;
if (FT_Get_Kerning (face, glyphIndex, rightGlyphIndex, ft_kerning_unscaled, &kerning) == 0
&& kerning.x != 0)
addKerningPair ((juce_wchar) character, (juce_wchar) rightCharCode, (float) kerning.x / height);
rightCharCode = FT_Get_Next_Char (face, rightCharCode, &rightGlyphIndex);
}
return ((float) kerning.x / (float) ftFace->face->units_per_EM);
}
float getSpacingForGlyphs (FT_UInt a, FT_UInt b) const
{
FT_Fixed advance{};
if (FT_Get_Advance (ftFace->face, a, FT_LOAD_ADVANCE_ONLY | FT_LOAD_NO_SCALE, &advance) != 0)
return {};
return getKerningForGlyphs (a, b) + ((float) advance / (float) ftFace->face->units_per_EM);
}
FT_UInt getNominalGlyphForCharacter (juce_wchar c) const
{
return FT_Get_Char_Index (ftFace->face, (FT_ULong) c);
}
FTFaceWrapper::Ptr ftFace;
HbFont hb;
DoCache doCache;
JUCE_DECLARE_NON_COPYABLE (FreeTypeTypeface)
};
Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font)
{
return FreeTypeTypeface::from (font);
}
Typeface::Ptr Typeface::createSystemTypefaceFor (Span<const std::byte> data)
{
return FreeTypeTypeface::from (data);
}
} // namespace juce

View file

@ -89,16 +89,6 @@ StringArray FTTypefaceList::getDefaultFontDirectories()
return fontDirs;
}
Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font)
{
return new FreeTypeTypeface (font);
}
Typeface::Ptr Typeface::createSystemTypefaceFor (const void* data, size_t dataSize)
{
return new FreeTypeTypeface (data, dataSize);
}
void Typeface::scanFolderForFonts (const File& folder)
{
FTTypefaceList::getInstance()->scanFontPaths (StringArray (folder.getFullPathName()));
@ -217,11 +207,6 @@ Typeface::Ptr Font::getDefaultTypefaceForFont (const Font& font)
const auto realName = defaultInfo.getRealFontName (name);
f.setTypefaceName (realName);
const auto styles = findAllTypefaceStyles (realName);
if (! styles.contains (font.getTypefaceStyle()))
f.setTypefaceStyle (styles[0]);
return Typeface::createSystemTypefaceFor (f);
}

View file

@ -37,35 +37,19 @@ namespace juce
static constexpr float referenceFontSize = 1024.0f;
static CTFontRef getCTFontFromTypeface (const Font&);
CTFontRef getCTFontFromTypeface (const Font& f);
namespace CoreTextTypeLayout
{
static String findBestAvailableStyle (const Font& font, CGAffineTransform& requiredTransform)
{
auto availableStyles = Font::findAllTypefaceStyles (font.getTypefaceName());
auto style = font.getTypefaceStyle();
if (! availableStyles.contains (style))
{
if (font.isItalic()) // Fake-up an italic font if there isn't a real one.
requiredTransform = CGAffineTransformMake (1.0f, 0, 0.1f, 1.0f, 0, 0);
return availableStyles[0];
}
return style;
}
static float getFontTotalHeight (CTFontRef font)
{
return std::abs ((float) CTFontGetAscent (font))
+ std::abs ((float) CTFontGetDescent (font));
+ std::abs ((float) CTFontGetDescent (font));
}
static float getHeightToPointsFactor (CTFontRef font)
{
return referenceFontSize / getFontTotalHeight (font);
return (float) CTFontGetSize (font) / (float) getFontTotalHeight (font);
}
static CFUniquePtr<CTFontRef> getFontWithPointSize (CTFontRef font, float pointSize)
@ -73,25 +57,6 @@ namespace CoreTextTypeLayout
return CFUniquePtr<CTFontRef> (CTFontCreateCopyWithAttributes (font, pointSize, nullptr, nullptr));
}
static CFUniquePtr<CTFontRef> createCTFont (const Font& font, const float fontSizePoints, CGAffineTransform& transformRequired)
{
CFUniquePtr<CFStringRef> cfFontFamily (FontStyleHelpers::getConcreteFamilyName (font).toCFString());
CFUniquePtr<CFStringRef> cfFontStyle (findBestAvailableStyle (font, transformRequired).toCFString());
CFStringRef keys[] = { kCTFontFamilyNameAttribute, kCTFontStyleNameAttribute };
CFTypeRef values[] = { cfFontFamily.get(), cfFontStyle.get() };
CFUniquePtr<CFDictionaryRef> fontDescAttributes (CFDictionaryCreate (nullptr,
(const void**) &keys,
(const void**) &values,
numElementsInArray (keys),
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks));
CFUniquePtr<CTFontDescriptorRef> ctFontDescRef (CTFontDescriptorCreateWithAttributes (fontDescAttributes.get()));
return CFUniquePtr<CTFontRef> (CTFontCreateWithFontDescriptor (ctFontDescRef.get(), fontSizePoints, nullptr));
}
//==============================================================================
struct Advances
{
@ -161,8 +126,7 @@ namespace CoreTextTypeLayout
return CFUniquePtr<CTFontRef> (ctf);
}
CGAffineTransform transform;
return createCTFont (f, referenceFontSize, transform);
return nullptr;
}
//==============================================================================
@ -384,101 +348,6 @@ namespace CoreTextTypeLayout
std::move (framesetterAndMap.fontMap) };
}
static Range<float> getLineVerticalRange (CTFrameRef frame, CFArrayRef lines, int lineIndex)
{
LineInfo info (frame, (CTLineRef) CFArrayGetValueAtIndex (lines, lineIndex), lineIndex);
return { (float) (info.origin.y - info.descent),
(float) (info.origin.y + info.ascent) };
}
static float findCTFrameHeight (CTFrameRef frame)
{
auto lines = CTFrameGetLines (frame);
auto numLines = CFArrayGetCount (lines);
if (numLines == 0)
return 0;
auto range = getLineVerticalRange (frame, lines, 0);
if (numLines > 1)
range = range.getUnionWith (getLineVerticalRange (frame, lines, (int) numLines - 1));
return range.getLength();
}
static bool areAllFontsDefaultWidth (const AttributedString& text)
{
auto numCharacterAttributes = text.getNumAttributes();
for (int i = 0; i < numCharacterAttributes; ++i)
if (text.getAttribute (i).font.getHorizontalScale() != 1.0f)
return false;
return true;
}
static bool drawToCGContext (const AttributedString& text, const Rectangle<float>& area,
const CGContextRef& context, float flipHeight)
{
if (! areAllFontsDefaultWidth (text))
return false;
auto framesetter = createCTFramesetter (text).framesetter;
// Ugly hack to fix a bug in OS X Sierra where the CTFrame needs to be slightly
// larger than the font height - otherwise the CTFrame will be invalid
CFRange fitrange;
auto suggestedSingleLineFrameSize =
CTFramesetterSuggestFrameSizeWithConstraints (framesetter.get(), CFRangeMake (0, 0), nullptr,
CGSizeMake (CGFLOAT_MAX, CGFLOAT_MAX), &fitrange);
auto minCTFrameHeight = (float) suggestedSingleLineFrameSize.height;
auto verticalJustification = text.getJustification().getOnlyVerticalFlags();
auto ctFrameArea = [area, minCTFrameHeight, verticalJustification]
{
if (minCTFrameHeight < area.getHeight())
return area;
if (verticalJustification == Justification::verticallyCentred)
return area.withSizeKeepingCentre (area.getWidth(), minCTFrameHeight);
auto frameArea = area.withHeight (minCTFrameHeight);
if (verticalJustification == Justification::bottom)
return frameArea.withBottomY (area.getBottom());
return frameArea;
}();
auto frame = createCTFrame (framesetter.get(), CGRectMake ((CGFloat) ctFrameArea.getX(), flipHeight - (CGFloat) ctFrameArea.getBottom(),
(CGFloat) ctFrameArea.getWidth(), (CGFloat) ctFrameArea.getHeight()));
auto textMatrix = CGContextGetTextMatrix (context);
CGContextSaveGState (context);
if (verticalJustification == Justification::verticallyCentred
|| verticalJustification == Justification::bottom)
{
auto adjust = ctFrameArea.getHeight() - findCTFrameHeight (frame.get());
if (verticalJustification == Justification::verticallyCentred)
adjust *= 0.5f;
CGContextTranslateCTM (context, 0, -adjust);
}
CTFrameDraw (frame.get(), context);
CGContextRestoreGState (context);
CGContextSetTextMatrix (context, textMatrix);
return true;
}
static void createLayout (TextLayout& glyphLayout, const AttributedString& text)
{
auto boundsHeight = glyphLayout.getHeight();
@ -567,7 +436,7 @@ namespace CoreTextTypeLayout
CGColorRef cgRunColor;
if (CFDictionaryGetValueIfPresent (runAttributes, kCTForegroundColorAttributeName, (const void**) &cgRunColor)
&& CGColorGetNumberOfComponents (cgRunColor) == 4)
&& CGColorGetNumberOfComponents (cgRunColor) == 4)
{
auto* components = CGColorGetComponents (cgRunColor);
@ -592,31 +461,115 @@ namespace CoreTextTypeLayout
}
}
//==============================================================================
class OSXTypeface final : public Typeface
class CoreTextTypeface final : public Typeface
{
public:
OSXTypeface (const Font& font)
: Typeface (font.getTypefaceName(), font.getTypefaceStyle()), canBeUsedForLayout (true)
static auto& getRegistered()
{
ctFontRef = CoreTextTypeLayout::createCTFont (font, referenceFontSize, renderingTransform);
if (ctFontRef != nullptr)
class Registered
{
fontRef = CTFontCopyGraphicsFont (ctFontRef.get(), nullptr);
initialiseMetrics();
}
public:
void add (CTFontRef ref)
{
const std::scoped_lock lock { mutex };
CFUniquePtr<CGFontRef> cgFont { CTFontCopyGraphicsFont (ref, nullptr) };
if (CTFontManagerRegisterGraphicsFont (cgFont.get(), nullptr))
map.emplace (ref, std::move (cgFont));
}
void remove (CTFontRef ref)
{
const std::scoped_lock lock { mutex };
if (const auto iter = map.find (ref); iter != map.end())
{
CTFontManagerUnregisterGraphicsFont (iter->second.get(), nullptr);
map.erase (iter);
}
}
std::set<String> getRegisteredFamilies() const
{
const std::scoped_lock lock { mutex };
std::set<String> result;
for (const auto& item : map)
{
const CFUniquePtr<CFStringRef> family { CTFontCopyName (item.first, kCTFontFamilyNameKey) };
result.insert (String::fromCFString (family.get()));
}
return result;
}
private:
std::map<CTFontRef, CFUniquePtr<CGFontRef>> map;
mutable std::mutex mutex;
};
static Registered registered;
return registered;
}
OSXTypeface (const void* data, size_t dataSize)
: Typeface ({}, {}), canBeUsedForLayout (false), dataCopy (data, dataSize)
public:
static Typeface::Ptr from (const Font& font)
{
CFUniquePtr<CFStringRef> cfFontFamily (FontStyleHelpers::getConcreteFamilyName (font).toCFString());
if (cfFontFamily == nullptr)
return {};
CFUniquePtr<CFStringRef> cfFontStyle (findBestAvailableStyle (font).toCFString());
if (cfFontStyle == nullptr)
return {};
CFStringRef keys[] { kCTFontFamilyNameAttribute, kCTFontStyleNameAttribute };
CFTypeRef values[] { cfFontFamily.get(), cfFontStyle.get() };
CFUniquePtr<CFDictionaryRef> fontDescAttributes (CFDictionaryCreate (nullptr,
(const void**) &keys,
(const void**) &values,
numElementsInArray (keys),
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks));
if (fontDescAttributes == nullptr)
return {};
CFUniquePtr<CTFontDescriptorRef> ctFontDescRef (CTFontDescriptorCreateWithAttributes (fontDescAttributes.get()));
if (ctFontDescRef == nullptr)
return {};
CFUniquePtr<CTFontRef> ctFont { CTFontCreateWithFontDescriptor (ctFontDescRef.get(), 1, nullptr) };
if (ctFont == nullptr)
return {};
HbFont result { hb_coretext_font_create (ctFont.get()) };
if (result == nullptr)
return {};
FontStyleHelpers::initSynthetics (result.get(), font);
return new CoreTextTypeface (std::move (ctFont), std::move (result), font.getTypefaceName(), font.getTypefaceStyle());
}
static Typeface::Ptr from (Span<const std::byte> data)
{
// We can't use CFDataCreate here as this triggers a false positive in ASAN
// so copy the data manually and use CFDataCreateWithBytesNoCopy
CFUniquePtr<CFDataRef> cfData (CFDataCreateWithBytesNoCopy (kCFAllocatorDefault, (const UInt8*) dataCopy.getData(),
(CFIndex) dataCopy.getSize(), kCFAllocatorNull));
auto provider = CGDataProviderCreateWithCFData (cfData.get());
MemoryBlock copy { data.data(), data.size() };
const CFUniquePtr<CFDataRef> cfData { CFDataCreateWithBytesNoCopy (kCFAllocatorDefault,
static_cast<const UInt8*> (copy.getData()),
(CFIndex) copy.getSize(),
kCFAllocatorNull) };
if (cfData == nullptr)
return {};
#if JUCE_IOS
// Workaround for a an obscure iOS bug which can cause the app to dead-lock
@ -625,79 +578,65 @@ public:
[UIFont systemFontOfSize: 12];
#endif
fontRef = CGFontCreateWithDataProvider (provider);
CGDataProviderRelease (provider);
const CFUniquePtr<CGDataProviderRef> provider { CGDataProviderCreateWithCFData (cfData.get()) };
if (fontRef != nullptr)
{
if (@available (macOS 10.11, *))
canBeUsedForLayout = CTFontManagerRegisterGraphicsFont (fontRef, nullptr);
if (provider == nullptr)
return {};
ctFontRef.reset (CTFontCreateWithGraphicsFont (fontRef, referenceFontSize, nullptr, nullptr));
const CFUniquePtr<CGFontRef> font { CGFontCreateWithDataProvider (provider.get()) };
if (ctFontRef != nullptr)
{
if (auto fontName = CFUniquePtr<CFStringRef> (CTFontCopyName (ctFontRef.get(), kCTFontFamilyNameKey)))
name = String::fromCFString (fontName.get());
if (font == nullptr)
return {};
if (auto fontStyle = CFUniquePtr<CFStringRef> (CTFontCopyName (ctFontRef.get(), kCTFontStyleNameKey)))
style = String::fromCFString (fontStyle.get());
CFUniquePtr<CTFontRef> ctFont { CTFontCreateWithGraphicsFont (font.get(), 1.0f, {}, {}) };
initialiseMetrics();
}
}
if (ctFont == nullptr)
return {};
HbFont result { hb_coretext_font_create (ctFont.get()) };
if (result == nullptr)
return {};
const CFUniquePtr<CFStringRef> family { CTFontCopyName (ctFont.get(), kCTFontFamilyNameKey) };
const CFUniquePtr<CFStringRef> style { CTFontCopyName (ctFont.get(), kCTFontStyleNameKey) };
return new CoreTextTypeface (std::move (ctFont),
std::move (result),
String::fromCFString (family.get()),
String::fromCFString (style.get()),
std::move (copy));
}
void initialiseMetrics()
Native getNativeDetails() const override
{
auto ctAscent = std::abs ((float) CTFontGetAscent (ctFontRef.get()));
auto ctDescent = std::abs ((float) CTFontGetDescent (ctFontRef.get()));
auto ctTotalHeight = ctAscent + ctDescent;
ascent = ctAscent / ctTotalHeight;
unitsToHeightScaleFactor = 1.0f / ctTotalHeight;
pathTransform = AffineTransform::scale (unitsToHeightScaleFactor);
fontHeightToPointsFactor = referenceFontSize / ctTotalHeight;
const short zero = 0;
CFUniquePtr<CFNumberRef> numberRef (CFNumberCreate (nullptr, kCFNumberShortType, &zero));
CFStringRef keys[] = { kCTFontAttributeName, kCTLigatureAttributeName };
CFTypeRef values[] = { ctFontRef.get(), numberRef.get() };
attributedStringAtts.reset (CFDictionaryCreate (nullptr, (const void**) &keys,
(const void**) &values, numElementsInArray (keys),
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
return Native { hb.get() };
}
~OSXTypeface() override
static std::set<String> getRegisteredFamilies()
{
if (fontRef != nullptr)
{
if (@available (macOS 10.8, *))
if (dataCopy.getSize() != 0)
CTFontManagerUnregisterGraphicsFont (fontRef, nullptr);
CGFontRelease (fontRef);
}
return getRegistered().getRegisteredFamilies();
}
float getAscent() const override { return ascent; }
float getDescent() const override { return 1.0f - ascent; }
float getHeightToPointsFactor() const override { return fontHeightToPointsFactor; }
~CoreTextTypeface() override
{
getRegistered().remove (ctFont.get());
}
float getStringWidth (const String& text) override
{
float x = 0;
if (ctFontRef != nullptr && text.isNotEmpty())
if (ctFont != nullptr && text.isNotEmpty())
{
CFUniquePtr<CFStringRef> cfText (text.toCFString());
CFUniquePtr<CFAttributedStringRef> attribString (CFAttributedStringCreate (kCFAllocatorDefault, cfText.get(), attributedStringAtts.get()));
CFUniquePtr<CFAttributedStringRef> attribString (CFAttributedStringCreate (kCFAllocatorDefault, cfText.get(), getAttributedStringAtts().get()));
CFUniquePtr<CTLineRef> line (CTLineCreateWithAttributedString (attribString.get()));
auto runArray = CTLineGetGlyphRuns (line.get());
const auto heightToPoints = getNativeDetails().getLegacyMetrics().getHeightToPointsFactor();
for (CFIndex i = 0; i < CFArrayGetCount (runArray); ++i)
{
auto run = (CTRunRef) CFArrayGetValueAtIndex (runArray, i);
@ -709,7 +648,7 @@ public:
x += (float) advances.advances[j].width;
}
x *= unitsToHeightScaleFactor;
x *= heightToPoints;
}
return x;
@ -719,16 +658,18 @@ public:
{
xOffsets.add (0);
if (ctFontRef != nullptr && text.isNotEmpty())
if (ctFont != nullptr && text.isNotEmpty())
{
float x = 0;
CFUniquePtr<CFStringRef> cfText (text.toCFString());
CFUniquePtr<CFAttributedStringRef> attribString (CFAttributedStringCreate (kCFAllocatorDefault, cfText.get(), attributedStringAtts.get()));
CFUniquePtr<CFAttributedStringRef> attribString (CFAttributedStringCreate (kCFAllocatorDefault, cfText.get(), getAttributedStringAtts().get()));
CFUniquePtr<CTLineRef> line (CTLineCreateWithAttributedString (attribString.get()));
auto runArray = CTLineGetGlyphRuns (line.get());
const auto heightToPointsFactor = getNativeDetails().getLegacyMetrics().getHeightToPointsFactor();
for (CFIndex i = 0; i < CFArrayGetCount (runArray); ++i)
{
auto run = (CTRunRef) CFArrayGetValueAtIndex (runArray, i);
@ -740,93 +681,101 @@ public:
for (int j = 0; j < length; ++j)
{
x += (float) advances.advances[j].width;
xOffsets.add (x * unitsToHeightScaleFactor);
xOffsets.add (x * heightToPointsFactor);
resultGlyphs.add (glyphs.glyphs[j]);
}
}
}
}
bool getOutlineForGlyph (int glyphNumber, Path& path) override
CTFontRef getFontRef() const
{
jassert (path.isEmpty()); // we might need to apply a transform to the path, so this must be empty
if (auto pathRef = CFUniquePtr<CGPathRef> (CTFontCreatePathForGlyph (ctFontRef.get(), (CGGlyph) glyphNumber, &renderingTransform)))
{
CGPathApply (pathRef.get(), &path, pathApplier);
if (! pathTransform.isIdentity())
path.applyTransform (pathTransform);
return true;
}
return false;
return ctFont.get();
}
//==============================================================================
CGFontRef fontRef = {};
CFUniquePtr<CTFontRef> ctFontRef;
float fontHeightToPointsFactor = 1.0f;
CGAffineTransform renderingTransform = CGAffineTransformIdentity;
bool canBeUsedForLayout;
private:
MemoryBlock dataCopy;
CFUniquePtr<CFDictionaryRef> attributedStringAtts;
float ascent = 0, unitsToHeightScaleFactor = 0;
AffineTransform pathTransform;
static void pathApplier (void* info, const CGPathElement* element)
CFUniquePtr<CFDictionaryRef> getAttributedStringAtts() const
{
auto& path = *static_cast<Path*> (info);
auto* p = element->points;
const short zero = 0;
CFUniquePtr<CFNumberRef> numberRef (CFNumberCreate (nullptr, kCFNumberShortType, &zero));
switch (element->type)
{
case kCGPathElementMoveToPoint: path.startNewSubPath ((float) p[0].x, (float) -p[0].y); break;
case kCGPathElementAddLineToPoint: path.lineTo ((float) p[0].x, (float) -p[0].y); break;
case kCGPathElementAddQuadCurveToPoint: path.quadraticTo ((float) p[0].x, (float) -p[0].y,
(float) p[1].x, (float) -p[1].y); break;
case kCGPathElementAddCurveToPoint: path.cubicTo ((float) p[0].x, (float) -p[0].y,
(float) p[1].x, (float) -p[1].y,
(float) p[2].x, (float) -p[2].y); break;
case kCGPathElementCloseSubpath: path.closeSubPath(); break;
default: jassertfalse; break;
}
CFStringRef keys[] = { kCTFontAttributeName, kCTLigatureAttributeName };
CFTypeRef values[] = { ctFont.get(), numberRef.get() };
return CFUniquePtr<CFDictionaryRef> (CFDictionaryCreate (nullptr, (const void**) &keys,
(const void**) &values, numElementsInArray (keys),
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OSXTypeface)
CoreTextTypeface (CFUniquePtr<CTFontRef> nativeFont,
HbFont fontIn,
const String& name,
const String& style,
MemoryBlock data = {})
: Typeface (name, style),
ctFont (std::move (nativeFont)),
hb (std::move (fontIn)),
storage (std::move (data))
{
if (! storage.isEmpty())
getRegistered().add (ctFont.get());
}
static String findBestAvailableStyle (const Font& font)
{
const auto availableStyles = Font::findAllTypefaceStyles (font.getTypefaceName());
const auto style = font.getTypefaceStyle();
if (availableStyles.contains (style))
return style;
return availableStyles[0];
}
// We store this, rather than calling hb_coretext_font_get_ct_font, because harfbuzz may
// override the font cascade list in the returned font.
CFUniquePtr<CTFontRef> ctFont;
HbFont hb;
MemoryBlock storage;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CoreTextTypeface)
};
CTFontRef getCTFontFromTypeface (const Font& f)
{
const auto typeface = f.getTypefacePtr();
if (auto* tf = dynamic_cast<OSXTypeface*> (typeface.get()))
return tf->ctFontRef.get();
if (auto* tf = dynamic_cast<CoreTextTypeface*> (typeface.get()))
return tf->getFontRef();
return {};
}
//==============================================================================
Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font)
{
return CoreTextTypeface::from (font);
}
Typeface::Ptr Typeface::createSystemTypefaceFor (Span<const std::byte> data)
{
return CoreTextTypeface::from (data);
}
void Typeface::scanFolderForFonts (const File& folder)
{
for (auto& file : folder.findChildFiles (File::findFiles, false, "*.otf;*.ttf"))
if (auto urlref = CFUniquePtr<CFURLRef> (CFURLCreateWithFileSystemPath (kCFAllocatorDefault, file.getFullPathName().toCFString(), kCFURLPOSIXPathStyle, true)))
CTFontManagerRegisterFontsForURL (urlref.get(), kCTFontManagerScopeProcess, nullptr);
}
StringArray Font::findAllTypefaceNames()
{
StringArray names;
#if JUCE_MAC
// CTFontManager only exists on OS X 10.6 and later, it does not exist on iOS
CFUniquePtr<CFArrayRef> fontFamilyArray (CTFontManagerCopyAvailableFontFamilyNames());
// The collection returned from CTFontCollectionCreateFromAvailableFonts doesn't include fonts registered by
// CTFontManagerRegisterGraphicsFont on iOS, so we need to keep track of registered fonts ourselves.
auto nameSet = CoreTextTypeface::getRegisteredFamilies();
for (CFIndex i = 0; i < CFArrayGetCount (fontFamilyArray.get()); ++i)
{
auto family = String::fromCFString ((CFStringRef) CFArrayGetValueAtIndex (fontFamilyArray.get(), i));
if (! family.startsWithChar ('.')) // ignore fonts that start with a '.'
names.addIfNotAlreadyThere (family);
}
#else
CFUniquePtr<CTFontCollectionRef> fontCollectionRef (CTFontCollectionCreateFromAvailableFonts (nullptr));
CFUniquePtr<CFArrayRef> fontDescriptorArray (CTFontCollectionCreateMatchingFontDescriptors (fontCollectionRef.get()));
@ -835,11 +784,12 @@ StringArray Font::findAllTypefaceNames()
auto ctFontDescriptorRef = (CTFontDescriptorRef) CFArrayGetValueAtIndex (fontDescriptorArray.get(), i);
CFUniquePtr<CFStringRef> cfsFontFamily ((CFStringRef) CTFontDescriptorCopyAttribute (ctFontDescriptorRef, kCTFontFamilyNameAttribute));
names.addIfNotAlreadyThere (String::fromCFString (cfsFontFamily.get()));
nameSet.insert (String::fromCFString (cfsFontFamily.get()));
}
#endif
names.sort (true);
for (auto& item : nameSet)
names.add (item);
return names;
}
@ -851,8 +801,8 @@ StringArray Font::findAllTypefaceStyles (const String& family)
StringArray results;
CFUniquePtr<CFStringRef> cfsFontFamily (family.toCFString());
CFStringRef keys[] = { kCTFontFamilyNameAttribute };
CFTypeRef values[] = { cfsFontFamily.get() };
CFStringRef keys[] { kCTFontFamilyNameAttribute };
CFTypeRef values[] { cfsFontFamily.get() };
CFUniquePtr<CFDictionaryRef> fontDescAttributes (CFDictionaryCreate (nullptr, (const void**) &keys, (const void**) &values, numElementsInArray (keys),
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
@ -875,18 +825,6 @@ StringArray Font::findAllTypefaceStyles (const String& family)
return results;
}
//==============================================================================
Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font) { return *new OSXTypeface (font); }
Typeface::Ptr Typeface::createSystemTypefaceFor (const void* data, size_t size) { return *new OSXTypeface (data, size); }
void Typeface::scanFolderForFonts (const File& folder)
{
for (auto& file : folder.findChildFiles (File::findFiles, false, "*.otf;*.ttf"))
if (auto urlref = CFUniquePtr<CFURLRef> (CFURLCreateWithFileSystemPath (kCFAllocatorDefault, file.getFullPathName().toCFString(), kCFURLPOSIXPathStyle, true)))
CTFontManagerRegisterFontsForURL (urlref.get(), kCTFontManagerScopeProcess, nullptr);
}
struct DefaultFontNames
{
#if JUCE_IOS
@ -917,33 +855,10 @@ Typeface::Ptr Font::getDefaultTypefaceForFont (const Font& font)
return Typeface::createSystemTypefaceFor (newFont);
}
static bool canAllTypefacesBeUsedInLayout (const AttributedString& text)
bool TextLayout::createNativeLayout (const AttributedString& text)
{
auto numCharacterAttributes = text.getNumAttributes();
for (int i = 0; i < numCharacterAttributes; ++i)
{
auto typeface = text.getAttribute (i).font.getTypefacePtr();
if (auto tf = dynamic_cast<OSXTypeface*> (typeface.get()))
if (tf->canBeUsedForLayout)
continue;
return false;
}
CoreTextTypeLayout::createLayout (*this, text);
return true;
}
bool TextLayout::createNativeLayout (const AttributedString& text)
{
if (canAllTypefacesBeUsedInLayout (text) && CoreTextTypeLayout::areAllFontsDefaultWidth (text))
{
CoreTextTypeLayout::createLayout (*this, text);
return true;
}
return false;
}
} // namespace juce

View file

@ -1,642 +0,0 @@
/*
==============================================================================
This file is part of the JUCE framework.
Copyright (c) Raw Material Software Limited
JUCE is an open source framework subject to commercial or open source
licensing.
By downloading, installing, or using the JUCE framework, or combining the
JUCE framework with any other source code, object code, content or any other
copyrightable work, you agree to the terms of the JUCE End User Licence
Agreement, and all incorporated terms including the JUCE Privacy Policy and
the JUCE Website Terms of Service, as applicable, which will bind you. If you
do not agree to the terms of these agreements, we will not license the JUCE
framework to you, and you must discontinue the installation or download
process and cease use of the JUCE framework.
JUCE End User Licence Agreement: https://juce.com/legal/juce-8-licence/
JUCE Privacy Policy: https://juce.com/juce-privacy-policy
JUCE Website Terms of Service: https://juce.com/juce-website-terms-of-service/
Or:
You may also use this code under the terms of the AGPLv3:
https://www.gnu.org/licenses/agpl-3.0.en.html
THE JUCE FRAMEWORK IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL
WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING WARRANTY OF
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
namespace juce
{
/* This is some quick-and-dirty code to extract the typeface name from a lump of TTF file data.
It's needed because although win32 will happily load a TTF file from in-memory data, it won't
tell you the name of the damned font that it just loaded.. and in order to actually use the font,
you need to know its name!! Anyway, this awful hack seems to work for most fonts.
*/
namespace TTFNameExtractor
{
struct OffsetTable
{
uint32 version;
uint16 numTables, searchRange, entrySelector, rangeShift;
};
struct TableDirectory
{
char tag[4];
uint32 checkSum, offset, length;
};
struct NamingTable
{
uint16 formatSelector;
uint16 numberOfNameRecords;
uint16 offsetStartOfStringStorage;
};
struct NameRecord
{
uint16 platformID, encodingID, languageID;
uint16 nameID, stringLength, offsetFromStorageArea;
};
static String parseNameRecord (MemoryInputStream& input, const NameRecord& nameRecord,
const int64 directoryOffset, const int64 offsetOfStringStorage)
{
String result;
auto oldPos = input.getPosition();
input.setPosition (directoryOffset + offsetOfStringStorage + ByteOrder::swapIfLittleEndian (nameRecord.offsetFromStorageArea));
auto stringLength = (int) ByteOrder::swapIfLittleEndian (nameRecord.stringLength);
auto platformID = ByteOrder::swapIfLittleEndian (nameRecord.platformID);
if (platformID == 0 || platformID == 3)
{
auto numChars = stringLength / 2 + 1;
HeapBlock<uint16> buffer;
buffer.calloc (numChars + 1);
input.read (buffer, stringLength);
for (int i = 0; i < numChars; ++i)
buffer[i] = ByteOrder::swapIfLittleEndian (buffer[i]);
static_assert (sizeof (CharPointer_UTF16::CharType) == sizeof (uint16), "Sanity check UTF-16 type");
result = CharPointer_UTF16 ((CharPointer_UTF16::CharType*) buffer.getData());
}
else
{
HeapBlock<char> buffer;
buffer.calloc (stringLength + 1);
input.read (buffer, stringLength);
result = CharPointer_UTF8 (buffer.getData());
}
input.setPosition (oldPos);
return result;
}
static String parseNameTable (MemoryInputStream& input, int64 directoryOffset)
{
input.setPosition (directoryOffset);
NamingTable namingTable = {};
input.read (&namingTable, sizeof (namingTable));
for (int i = 0; i < (int) ByteOrder::swapIfLittleEndian (namingTable.numberOfNameRecords); ++i)
{
NameRecord nameRecord = {};
input.read (&nameRecord, sizeof (nameRecord));
if (ByteOrder::swapIfLittleEndian (nameRecord.nameID) == 4)
{
const String result (parseNameRecord (input, nameRecord, directoryOffset,
ByteOrder::swapIfLittleEndian (namingTable.offsetStartOfStringStorage)));
if (result.isNotEmpty())
return result;
}
}
return {};
}
static String getTypefaceNameFromFile (MemoryInputStream& input)
{
OffsetTable offsetTable = {};
input.read (&offsetTable, sizeof (offsetTable));
for (int i = 0; i < (int) ByteOrder::swapIfLittleEndian (offsetTable.numTables); ++i)
{
TableDirectory tableDirectory;
zerostruct (tableDirectory);
input.read (&tableDirectory, sizeof (tableDirectory));
if (String (tableDirectory.tag, sizeof (tableDirectory.tag)).equalsIgnoreCase ("name"))
return parseNameTable (input, ByteOrder::swapIfLittleEndian (tableDirectory.offset));
}
return {};
}
}
namespace FontEnumerators
{
static int CALLBACK fontEnum2 (ENUMLOGFONTEXW* lpelfe, NEWTEXTMETRICEXW*, int type, LPARAM lParam)
{
if (lpelfe != nullptr && (type & RASTER_FONTTYPE) == 0)
{
const String fontName (lpelfe->elfLogFont.lfFaceName);
((StringArray*) lParam)->addIfNotAlreadyThere (fontName.removeCharacters ("@"));
}
return 1;
}
static int CALLBACK fontEnum1 (ENUMLOGFONTEXW* lpelfe, NEWTEXTMETRICEXW*, int type, LPARAM lParam)
{
if (lpelfe != nullptr && (type & RASTER_FONTTYPE) == 0)
{
LOGFONTW lf = {};
lf.lfWeight = FW_DONTCARE;
lf.lfOutPrecision = OUT_OUTLINE_PRECIS;
lf.lfQuality = DEFAULT_QUALITY;
lf.lfCharSet = DEFAULT_CHARSET;
lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
lf.lfPitchAndFamily = FF_DONTCARE;
const String fontName (lpelfe->elfLogFont.lfFaceName);
fontName.copyToUTF16 (lf.lfFaceName, sizeof (lf.lfFaceName));
auto dc = CreateCompatibleDC (nullptr);
EnumFontFamiliesEx (dc, &lf, (FONTENUMPROCW) &fontEnum2, lParam, 0);
DeleteDC (dc);
}
return 1;
}
}
StringArray Font::findAllTypefaceNames()
{
StringArray results;
#if JUCE_USE_DIRECTWRITE
SharedResourcePointer<Direct2DFactories> factories;
if (factories->systemFonts != nullptr)
{
ComSmartPtr<IDWriteFontFamily> fontFamily;
uint32 fontFamilyCount = 0;
fontFamilyCount = factories->systemFonts->GetFontFamilyCount();
for (uint32 i = 0; i < fontFamilyCount; ++i)
{
auto hr = factories->systemFonts->GetFontFamily (i, fontFamily.resetAndGetPointerAddress());
if (SUCCEEDED (hr))
results.addIfNotAlreadyThere (getFontFamilyName (fontFamily));
}
}
else
#endif
{
auto dc = CreateCompatibleDC (nullptr);
{
LOGFONTW lf = {};
lf.lfWeight = FW_DONTCARE;
lf.lfOutPrecision = OUT_OUTLINE_PRECIS;
lf.lfQuality = DEFAULT_QUALITY;
lf.lfCharSet = DEFAULT_CHARSET;
lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
lf.lfPitchAndFamily = FF_DONTCARE;
EnumFontFamiliesEx (dc, &lf,
(FONTENUMPROCW) &FontEnumerators::fontEnum1,
(LPARAM) &results, 0);
}
DeleteDC (dc);
}
results.sort (true);
return results;
}
StringArray Font::findAllTypefaceStyles (const String& family)
{
if (FontStyleHelpers::isPlaceholderFamilyName (family))
return findAllTypefaceStyles (FontStyleHelpers::getConcreteFamilyNameFromPlaceholder (family));
StringArray results;
#if JUCE_USE_DIRECTWRITE
SharedResourcePointer<Direct2DFactories> factories;
if (factories->systemFonts != nullptr)
{
BOOL fontFound = false;
uint32 fontIndex = 0;
[[maybe_unused]] auto hr = factories->systemFonts->FindFamilyName (family.toWideCharPointer(), &fontIndex, &fontFound);
if (! fontFound)
fontIndex = 0;
// Get the font family using the search results
// Fonts like: Times New Roman, Times New Roman Bold, Times New Roman Italic are all in the same font family
ComSmartPtr<IDWriteFontFamily> fontFamily;
hr = factories->systemFonts->GetFontFamily (fontIndex, fontFamily.resetAndGetPointerAddress());
// Get the font faces
ComSmartPtr<IDWriteFont> dwFont;
uint32 fontFacesCount = 0;
fontFacesCount = fontFamily->GetFontCount();
for (uint32 i = 0; i < fontFacesCount; ++i)
{
hr = fontFamily->GetFont (i, dwFont.resetAndGetPointerAddress());
// Ignore any algorithmically generated bold and oblique styles..
if (dwFont->GetSimulations() == DWRITE_FONT_SIMULATIONS_NONE)
results.addIfNotAlreadyThere (getFontFaceName (dwFont));
}
}
else
#endif
{
results.add ("Regular");
results.add ("Italic");
results.add ("Bold");
results.add ("Bold Italic");
}
return results;
}
extern bool juce_isRunningInWine();
struct DefaultFontNames
{
DefaultFontNames()
{
if (juce_isRunningInWine())
{
// If we're running in Wine, then use fonts that might be available on Linux..
defaultSans = "Bitstream Vera Sans";
defaultSerif = "Bitstream Vera Serif";
defaultFixed = "Bitstream Vera Sans Mono";
}
else
{
defaultSans = "Verdana";
defaultSerif = "Times New Roman";
defaultFixed = "Lucida Console";
defaultFallback = "Tahoma"; // (contains plenty of unicode characters)
}
}
String defaultSans, defaultSerif, defaultFixed, defaultFallback;
};
Typeface::Ptr Font::getDefaultTypefaceForFont (const Font& font)
{
static DefaultFontNames defaultNames;
Font newFont (font);
auto faceName = font.getTypefaceName();
if (faceName == getDefaultSansSerifFontName()) newFont.setTypefaceName (defaultNames.defaultSans);
else if (faceName == getDefaultSerifFontName()) newFont.setTypefaceName (defaultNames.defaultSerif);
else if (faceName == getDefaultMonospacedFontName()) newFont.setTypefaceName (defaultNames.defaultFixed);
if (font.getTypefaceStyle() == getDefaultStyle())
newFont.setTypefaceStyle ("Regular");
return Typeface::createSystemTypefaceFor (newFont);
}
//==============================================================================
class WindowsTypeface final : public Typeface
{
public:
WindowsTypeface (const Font& font) : Typeface (font.getTypefaceName(),
font.getTypefaceStyle())
{
loadFont();
}
WindowsTypeface (const void* data, size_t dataSize)
: Typeface (String(), String())
{
DWORD numInstalled = 0;
memoryFont = AddFontMemResourceEx (const_cast<void*> (data), (DWORD) dataSize,
nullptr, &numInstalled);
MemoryInputStream m (data, dataSize, false);
name = TTFNameExtractor::getTypefaceNameFromFile (m);
loadFont();
}
~WindowsTypeface() override
{
SelectObject (dc, previousFontH); // Replacing the previous font before deleting the DC avoids a warning in BoundsChecker
DeleteDC (dc);
if (fontH != nullptr)
DeleteObject (fontH);
if (memoryFont != nullptr)
RemoveFontMemResourceEx (memoryFont);
}
float getAscent() const override { return ascent; }
float getDescent() const override { return 1.0f - ascent; }
float getHeightToPointsFactor() const override { return heightToPointsFactor; }
float getStringWidth (const String& text) override
{
auto utf16 = text.toUTF16();
auto numChars = utf16.length();
HeapBlock<uint16> results (numChars);
float x = 0;
if (GetGlyphIndices (dc, utf16, (int) numChars, reinterpret_cast<WORD*> (results.getData()),
GGI_MARK_NONEXISTING_GLYPHS) != GDI_ERROR)
{
for (size_t i = 0; i < numChars; ++i)
x += getKerning (dc, results[i], (i + 1) < numChars ? results[i + 1] : -1);
}
return x;
}
void getGlyphPositions (const String& text, Array<int>& resultGlyphs, Array<float>& xOffsets) override
{
auto utf16 = text.toUTF16();
auto numChars = utf16.length();
HeapBlock<uint16> results (numChars);
float x = 0;
if (GetGlyphIndices (dc, utf16, (int) numChars, reinterpret_cast<WORD*> (results.getData()),
GGI_MARK_NONEXISTING_GLYPHS) != GDI_ERROR)
{
resultGlyphs.ensureStorageAllocated ((int) numChars);
xOffsets.ensureStorageAllocated ((int) numChars + 1);
for (size_t i = 0; i < numChars; ++i)
{
resultGlyphs.add (results[i]);
xOffsets.add (x);
x += getKerning (dc, results[i], (i + 1) < numChars ? results[i + 1] : -1);
}
}
xOffsets.add (x);
}
bool getOutlineForGlyph (int glyphNumber, Path& glyphPath) override
{
if (glyphNumber < 0)
glyphNumber = defaultGlyph;
GLYPHMETRICS gm;
// (although GetGlyphOutline returns a DWORD, it may be -1 on failure, so treat it as signed int..)
auto bufSize = (int) GetGlyphOutline (dc, (UINT) glyphNumber, GGO_NATIVE | GGO_GLYPH_INDEX,
&gm, 0, nullptr, &identityMatrix);
if (bufSize > 0)
{
HeapBlock<char> data (bufSize);
GetGlyphOutline (dc, (UINT) glyphNumber, GGO_NATIVE | GGO_GLYPH_INDEX, &gm,
(DWORD) bufSize, data, &identityMatrix);
auto pheader = reinterpret_cast<const TTPOLYGONHEADER*> (data.getData());
auto scaleX = 1.0f / (float) tm.tmHeight;
auto scaleY = -scaleX;
while ((char*) pheader < data + bufSize)
{
glyphPath.startNewSubPath (scaleX * pheader->pfxStart.x.value,
scaleY * pheader->pfxStart.y.value);
auto curve = unalignedPointerCast<const TTPOLYCURVE*> ((const char*) pheader + sizeof (TTPOLYGONHEADER));
auto curveEnd = ((const char*) pheader) + pheader->cb;
while ((const char*) curve < curveEnd)
{
if (curve->wType == TT_PRIM_LINE)
{
for (int i = 0; i < curve->cpfx; ++i)
glyphPath.lineTo (scaleX * curve->apfx[i].x.value,
scaleY * curve->apfx[i].y.value);
}
else if (curve->wType == TT_PRIM_QSPLINE)
{
for (int i = 0; i < curve->cpfx - 1; ++i)
{
auto x2 = scaleX * curve->apfx[i].x.value;
auto y2 = scaleY * curve->apfx[i].y.value;
auto x3 = scaleX * curve->apfx[i + 1].x.value;
auto y3 = scaleY * curve->apfx[i + 1].y.value;
if (i < curve->cpfx - 2)
{
x3 = 0.5f * (x2 + x3);
y3 = 0.5f * (y2 + y3);
}
glyphPath.quadraticTo (x2, y2, x3, y3);
}
}
curve = (const TTPOLYCURVE*) &(curve->apfx [curve->cpfx]);
}
pheader = unalignedPointerCast<const TTPOLYGONHEADER*> (curve);
glyphPath.closeSubPath();
}
}
return true;
}
private:
static const MAT2 identityMatrix;
HFONT fontH = {};
HGDIOBJ previousFontH = {};
HDC dc { CreateCompatibleDC (nullptr) };
TEXTMETRIC tm;
HANDLE memoryFont = {};
float ascent = 1.0f, heightToPointsFactor = 1.0f;
int defaultGlyph = -1, heightInPoints = 0;
std::unordered_map<uint64, float> kerningPairs;
static uint64 kerningPairIndex (int glyph1, int glyph2)
{
return (((uint64) (uint32) glyph1) << 32) | (uint64) (uint32) glyph2;
}
void loadFont()
{
SetMapperFlags (dc, 0);
SetMapMode (dc, MM_TEXT);
LOGFONTW lf = {};
lf.lfCharSet = DEFAULT_CHARSET;
lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
lf.lfOutPrecision = OUT_OUTLINE_PRECIS;
lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
lf.lfQuality = PROOF_QUALITY;
lf.lfItalic = (BYTE) (style.contains ("Italic") ? TRUE : FALSE);
lf.lfWeight = style.contains ("Bold") ? FW_BOLD : FW_NORMAL;
lf.lfHeight = -256;
name.copyToUTF16 (lf.lfFaceName, sizeof (lf.lfFaceName));
auto standardSizedFont = CreateFontIndirect (&lf);
if (standardSizedFont != nullptr)
{
if ((previousFontH = SelectObject (dc, standardSizedFont)) != nullptr)
{
fontH = standardSizedFont;
OUTLINETEXTMETRIC otm;
if (GetOutlineTextMetrics (dc, sizeof (otm), &otm) != 0)
{
heightInPoints = (int) otm.otmEMSquare;
lf.lfHeight = -heightInPoints;
fontH = CreateFontIndirect (&lf);
SelectObject (dc, fontH);
DeleteObject (standardSizedFont);
}
}
}
if (GetTextMetrics (dc, &tm))
{
auto dpi = (float) (GetDeviceCaps (dc, LOGPIXELSX) + GetDeviceCaps (dc, LOGPIXELSY)) / 2.0f;
heightToPointsFactor = (dpi / (float) GetDeviceCaps (dc, LOGPIXELSY)) * (float) heightInPoints / (float) tm.tmHeight;
ascent = (float) tm.tmAscent / (float) tm.tmHeight;
std::unordered_map<int, int> glyphsForChars;
defaultGlyph = getGlyphForChar (dc, glyphsForChars, tm.tmDefaultChar);
createKerningPairs (dc, glyphsForChars, (float) tm.tmHeight);
}
}
void createKerningPairs (HDC hdc, std::unordered_map<int, int>& glyphsForChars, float height)
{
HeapBlock<KERNINGPAIR> rawKerning;
auto numKPs = GetKerningPairs (hdc, 0, nullptr);
rawKerning.calloc (numKPs);
GetKerningPairs (hdc, numKPs, rawKerning);
std::unordered_map<int, int> widthsForGlyphs;
for (DWORD i = 0; i < numKPs; ++i)
{
auto glyph1 = getGlyphForChar (hdc, glyphsForChars, rawKerning[i].wFirst);
auto glyph2 = getGlyphForChar (hdc, glyphsForChars, rawKerning[i].wSecond);
auto standardWidth = getGlyphWidth (hdc, widthsForGlyphs, glyph1);
kerningPairs[kerningPairIndex (glyph1, glyph2)] = (float) (standardWidth + rawKerning[i].iKernAmount) / height;
kerningPairs[kerningPairIndex (glyph1, -1)] = (float) standardWidth / height;
}
}
static int getGlyphForChar (HDC dc, std::unordered_map<int, int>& cache, juce_wchar character)
{
auto existing = cache.find ((int) character);
if (existing != cache.end())
return existing->second;
const WCHAR charToTest[] = { (WCHAR) character, 0 };
WORD index = 0;
if (GetGlyphIndices (dc, charToTest, 1, &index, GGI_MARK_NONEXISTING_GLYPHS) == GDI_ERROR
|| index == 0xffff)
return -1;
cache[(int) character] = index;
return index;
}
static int getGlyphWidth (HDC dc, std::unordered_map<int, int>& cache, int glyphNumber)
{
auto existing = cache.find (glyphNumber);
if (existing != cache.end())
return existing->second;
auto width = getGlyphWidth (dc, glyphNumber);
cache[glyphNumber] = width;
return width;
}
static int getGlyphWidth (HDC dc, int glyphNumber)
{
GLYPHMETRICS gm;
gm.gmCellIncX = 0;
GetGlyphOutline (dc, (UINT) glyphNumber, GGO_NATIVE | GGO_GLYPH_INDEX, &gm, 0, nullptr, &identityMatrix);
return gm.gmCellIncX;
}
float getKerning (HDC hdc, int glyph1, int glyph2)
{
auto pair = kerningPairs.find (kerningPairIndex (glyph1, glyph2));
if (pair != kerningPairs.end())
return pair->second;
auto single = kerningPairs.find (kerningPairIndex (glyph1, -1));
if (single != kerningPairs.end())
return single->second;
auto width = (float) getGlyphWidth (hdc, glyph1) / (float) tm.tmHeight;
kerningPairs[kerningPairIndex (glyph1, -1)] = width;
return width;
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WindowsTypeface)
};
const MAT2 WindowsTypeface::identityMatrix = { { 0, 1 }, { 0, 0 }, { 0, 0 }, { 0, 1 } };
Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font)
{
#if JUCE_USE_DIRECTWRITE
SharedResourcePointer<Direct2DFactories> factories;
if (factories->systemFonts != nullptr)
{
std::unique_ptr<WindowsDirectWriteTypeface> wtf (new WindowsDirectWriteTypeface (font, factories->systemFonts));
if (wtf->loadedOk() && wtf->isFontFound())
return wtf.release();
}
#endif
return new WindowsTypeface (font);
}
Typeface::Ptr Typeface::createSystemTypefaceFor (const void* data, size_t dataSize)
{
return new WindowsTypeface (data, dataSize);
}
void Typeface::scanFolderForFonts (const File&)
{
jassertfalse; // not implemented on this platform
}
} // namespace juce

View file

@ -35,6 +35,49 @@
namespace juce::RenderingHelpers
{
template <typename Context>
void drawGlyphImpl (Context& context, int glyphNumber, const AffineTransform& trans)
{
using GlyphCacheType = typename Context::GlyphCacheType;
using EdgeTableRegionType = typename Context::EdgeTableRegionType;
if (context.clip == nullptr)
return;
if (trans.isOnlyTranslation() && ! context.transform.isRotated)
{
auto& cache = GlyphCacheType::getInstance();
const Point pos (trans.getTranslationX(), trans.getTranslationY());
if (context.transform.isOnlyTranslated)
{
cache.drawGlyph (context, context.font, glyphNumber, pos + context.transform.offset.toFloat());
}
else
{
auto f = context.font;
f.setHeight (f.getHeight() * context.transform.complexTransform.mat11);
auto xScale = context.transform.complexTransform.mat00 / context.transform.complexTransform.mat11;
if (std::abs (xScale - 1.0f) > 0.01f)
f.setHorizontalScale (xScale);
cache.drawGlyph (context, f, glyphNumber, context.transform.transformed (pos));
}
}
else
{
const auto fontHeight = context.font.getHeight();
const auto fontTransform = AffineTransform::scale (fontHeight * context.font.getHorizontalScale(),
fontHeight).followedBy (trans);
const auto fullTransform = context.transform.getTransformWith (fontTransform);
if (auto et = rawToUniquePtr (context.font.getTypefacePtr()->getEdgeTableForGlyph (glyphNumber, fullTransform, fontHeight)))
context.fillShape (*new EdgeTableRegionType (*et), false);
}
}
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4127)
//==============================================================================
@ -190,6 +233,11 @@ public:
}
}
private:
ReferenceCountedArray<CachedGlyphType> glyphs;
Atomic<int> accessCounter, hits, misses;
CriticalSection lock;
ReferenceCountedObjectPtr<CachedGlyphType> findOrCreateGlyph (const Font& font, int glyphNumber)
{
const ScopedLock sl (lock);
@ -207,11 +255,6 @@ public:
return g;
}
private:
ReferenceCountedArray<CachedGlyphType> glyphs;
Atomic<int> accessCounter, hits, misses;
CriticalSection lock;
ReferenceCountedObjectPtr<CachedGlyphType> findExistingGlyph (const Font& font, int glyphNumber) const noexcept
{
for (auto g : glyphs)
@ -287,9 +330,6 @@ public:
void draw (RendererType& state, Point<float> pos) const
{
if (snapToIntegerCoordinate)
pos.x = std::floor (pos.x + 0.5f);
if (edgeTable != nullptr)
state.fillEdgeTable (*edgeTable, pos.x, roundToInt (pos.y));
}
@ -298,7 +338,6 @@ public:
{
font = newFont;
auto typeface = newFont.getTypefacePtr();
snapToIntegerCoordinate = typeface->isHinted();
glyph = glyphNumber;
auto fontHeight = font.getHeight();
@ -310,7 +349,6 @@ public:
Font font;
std::unique_ptr<EdgeTable> edgeTable;
int glyph = 0, lastAccessCount = 0;
bool snapToIntegerCoordinate = false;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CachedGlyphEdgeTable)
};
@ -2542,45 +2580,7 @@ public:
//==============================================================================
void drawGlyph (int glyphNumber, const AffineTransform& trans)
{
if (clip != nullptr)
{
if (trans.isOnlyTranslation() && ! transform.isRotated)
{
auto& cache = GlyphCacheType::getInstance();
Point<float> pos (trans.getTranslationX(), trans.getTranslationY());
if (transform.isOnlyTranslated)
{
cache.drawGlyph (*this, font, glyphNumber, pos + transform.offset.toFloat());
}
else
{
pos = transform.transformed (pos);
Font f (font);
f.setHeight (font.getHeight() * transform.complexTransform.mat11);
auto xScale = transform.complexTransform.mat00 / transform.complexTransform.mat11;
if (std::abs (xScale - 1.0f) > 0.01f)
f.setHorizontalScale (xScale);
cache.drawGlyph (*this, f, glyphNumber, pos);
}
}
else
{
auto fontHeight = font.getHeight();
auto t = transform.getTransformWith (AffineTransform::scale (fontHeight * font.getHorizontalScale(), fontHeight)
.followedBy (trans));
std::unique_ptr<EdgeTable> et (font.getTypefacePtr()->getEdgeTableForGlyph (glyphNumber, t, fontHeight));
if (et != nullptr)
fillShape (*new EdgeTableRegionType (*et), false);
}
}
drawGlyphImpl (*this, glyphNumber, trans);
}
Rectangle<int> getMaximumBounds() const { return image.getBounds(); }
@ -2706,34 +2706,35 @@ template <class SavedStateType>
class StackBasedLowLevelGraphicsContext : public LowLevelGraphicsContext
{
public:
bool isVectorDevice() const override { return false; }
void setOrigin (Point<int> o) override { stack->transform.setOrigin (o); }
void addTransform (const AffineTransform& t) override { stack->transform.addTransform (t); }
float getPhysicalPixelScaleFactor() override { return stack->transform.getPhysicalPixelScaleFactor(); }
Rectangle<int> getClipBounds() const override { return stack->getClipBounds(); }
bool isClipEmpty() const override { return stack->clip == nullptr; }
bool clipRegionIntersects (const Rectangle<int>& r) override { return stack->clipRegionIntersects (r); }
bool clipToRectangle (const Rectangle<int>& r) override { return stack->clipToRectangle (r); }
bool clipToRectangleList (const RectangleList<int>& r) override { return stack->clipToRectangleList (r); }
void excludeClipRectangle (const Rectangle<int>& r) override { stack->excludeClipRectangle (r); }
void clipToPath (const Path& path, const AffineTransform& t) override { stack->clipToPath (path, t); }
void clipToImageAlpha (const Image& im, const AffineTransform& t) override { stack->clipToImageAlpha (im, t); }
void saveState() override { stack.save(); }
void restoreState() override { stack.restore(); }
void beginTransparencyLayer (float opacity) override { stack.beginTransparencyLayer (opacity); }
void endTransparencyLayer() override { stack.endTransparencyLayer(); }
void setFill (const FillType& fillType) override { stack->setFillType (fillType); }
void setOpacity (float newOpacity) override { stack->fillType.setOpacity (newOpacity); }
void setInterpolationQuality (Graphics::ResamplingQuality quality) override { stack->interpolationQuality = quality; }
void fillRect (const Rectangle<int>& r, bool replace) override { stack->fillRect (r, replace); }
void fillRect (const Rectangle<float>& r) override { stack->fillRect (r); }
void fillRectList (const RectangleList<float>& list) override { stack->fillRectList (list); }
void fillPath (const Path& path, const AffineTransform& t) override { stack->fillPath (path, t); }
void drawImage (const Image& im, const AffineTransform& t) override { stack->drawImage (im, t); }
void drawGlyph (int glyphNumber, const AffineTransform& t) override { stack->drawGlyph (glyphNumber, t); }
void drawLine (const Line<float>& line) override { stack->drawLine (line); }
void setFont (const Font& newFont) override { stack->font = newFont; }
const Font& getFont() override { return stack->font; }
bool isVectorDevice() const override { return false; }
Rectangle<int> getClipBounds() const override { return stack->getClipBounds(); }
bool isClipEmpty() const override { return stack->clip == nullptr; }
void setOrigin (Point<int> o) override { stack->transform.setOrigin (o); }
void addTransform (const AffineTransform& t) override { stack->transform.addTransform (t); }
float getPhysicalPixelScaleFactor() override { return stack->transform.getPhysicalPixelScaleFactor(); }
bool clipRegionIntersects (const Rectangle<int>& r) override { return stack->clipRegionIntersects (r); }
bool clipToRectangle (const Rectangle<int>& r) override { return stack->clipToRectangle (r); }
bool clipToRectangleList (const RectangleList<int>& r) override { return stack->clipToRectangleList (r); }
void excludeClipRectangle (const Rectangle<int>& r) override { stack->excludeClipRectangle (r); }
void clipToPath (const Path& path, const AffineTransform& t) override { stack->clipToPath (path, t); }
void clipToImageAlpha (const Image& im, const AffineTransform& t) override { stack->clipToImageAlpha (im, t); }
void saveState() override { stack.save(); }
void restoreState() override { stack.restore(); }
void beginTransparencyLayer (float opacity) override { stack.beginTransparencyLayer (opacity); }
void endTransparencyLayer() override { stack.endTransparencyLayer(); }
void setFill (const FillType& fillType) override { stack->setFillType (fillType); }
void setOpacity (float newOpacity) override { stack->fillType.setOpacity (newOpacity); }
void setInterpolationQuality (Graphics::ResamplingQuality quality) override { stack->interpolationQuality = quality; }
void fillRect (const Rectangle<int>& r, bool replace) override { stack->fillRect (r, replace); }
void fillRect (const Rectangle<float>& r) override { stack->fillRect (r); }
void fillRectList (const RectangleList<float>& list) override { stack->fillRectList (list); }
void fillPath (const Path& path, const AffineTransform& t) override { stack->fillPath (path, t); }
void drawImage (const Image& im, const AffineTransform& t) override { stack->drawImage (im, t); }
void drawGlyph (int i, const AffineTransform& t) override { stack->drawGlyph (i, t); }
void drawLine (const Line<float>& line) override { stack->drawLine (line); }
void setFont (const Font& newFont) override { stack->font = newFont; }
const Font& getFont() override { return stack->font; }
protected:
StackBasedLowLevelGraphicsContext (SavedStateType* initialState) : stack (initialState) {}