Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit bde72d8

Browse files
authored
[Impeller] libImpeller: Allow custom font registrations. (#55934)
Fixes flutter/flutter#156361 cc @lyceel. A bit of miscommunication on the priority of this one. I thought the default font factories were sufficient for the next release. But it turns out that was not true on the board which is a custom platform. In any case, this should unblock custom font registrations. See the `CanCreateParagraphsWithCustomFont` case for an example. If you don't provide the family name alias for the custom font, the name will be read from the font data. This example uses the custom "WhatTheFlutter" custom font family in "wtf.otf". <img width="1136" alt="Screenshot 2024-10-17 at 1 26 36 PM" src="https://github.com/user-attachments/assets/dbbef2fd-f854-4330-8c82-fc8378489769">
1 parent 6ae2021 commit bde72d8

File tree

5 files changed

+160
-0
lines changed

5 files changed

+160
-0
lines changed

impeller/toolkit/interop/impeller.cc

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1133,4 +1133,20 @@ void ImpellerTypographyContextRelease(ImpellerTypographyContext context) {
11331133
ObjectBase::SafeRelease(context);
11341134
}
11351135

1136+
IMPELLER_EXTERN_C
1137+
bool ImpellerTypographyContextRegisterFont(ImpellerTypographyContext context,
1138+
const ImpellerMapping* contents,
1139+
void* contents_on_release_user_data,
1140+
const char* family_name_alias) {
1141+
auto wrapped_contents = std::make_unique<fml::NonOwnedMapping>(
1142+
contents->data, // data ptr
1143+
contents->length, // data length
1144+
[contents, contents_on_release_user_data](auto, auto) {
1145+
contents->on_release(contents_on_release_user_data);
1146+
} // release callback
1147+
);
1148+
return GetPeer(context)->RegisterFont(std::move(wrapped_contents),
1149+
family_name_alias);
1150+
}
1151+
11361152
} // namespace impeller::interop

impeller/toolkit/interop/impeller.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -851,6 +851,13 @@ IMPELLER_EXPORT
851851
void ImpellerTypographyContextRelease(
852852
ImpellerTypographyContext IMPELLER_NULLABLE context);
853853

854+
IMPELLER_EXPORT
855+
bool ImpellerTypographyContextRegisterFont(
856+
ImpellerTypographyContext IMPELLER_NONNULL context,
857+
const ImpellerMapping* IMPELLER_NONNULL contents,
858+
void* IMPELLER_NULLABLE contents_on_release_user_data,
859+
const char* IMPELLER_NULLABLE family_name_alias);
860+
854861
//------------------------------------------------------------------------------
855862
// Paragraph Style
856863
//------------------------------------------------------------------------------

impeller/toolkit/interop/impeller_unittests.cc

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,80 @@ TEST_P(InteropPlaygroundTest, CanCreateParagraphs) {
245245
}));
246246
}
247247

248+
TEST_P(InteropPlaygroundTest, CanCreateParagraphsWithCustomFont) {
249+
// Create a typography context.
250+
auto type_context = Adopt<TypographyContext>(ImpellerTypographyContextNew());
251+
ASSERT_TRUE(type_context);
252+
253+
// Open the custom font file.
254+
std::unique_ptr<fml::Mapping> font_data =
255+
flutter::testing::OpenFixtureAsMapping("wtf.otf");
256+
ASSERT_NE(font_data, nullptr);
257+
ASSERT_GT(font_data->GetSize(), 0u);
258+
ImpellerMapping font_data_mapping = {
259+
.data = font_data->GetMapping(),
260+
.length = font_data->GetSize(),
261+
.on_release = [](auto ctx) {
262+
delete reinterpret_cast<fml::Mapping*>(ctx);
263+
}};
264+
auto registered =
265+
ImpellerTypographyContextRegisterFont(type_context.GetC(), //
266+
&font_data_mapping, //
267+
font_data.release(), //
268+
nullptr //
269+
);
270+
ASSERT_TRUE(registered);
271+
272+
// Create a builder.
273+
auto builder =
274+
Adopt<ParagraphBuilder>(ImpellerParagraphBuilderNew(type_context.GetC()));
275+
ASSERT_TRUE(builder);
276+
277+
// Create a paragraph style with the font size and foreground and background
278+
// colors.
279+
auto style = Adopt<ParagraphStyle>(ImpellerParagraphStyleNew());
280+
ASSERT_TRUE(style);
281+
ImpellerParagraphStyleSetFontSize(style.GetC(), 150.0f);
282+
ImpellerParagraphStyleSetFontFamily(style.GetC(), "WhatTheFlutter");
283+
284+
{
285+
auto paint = Adopt<Paint>(ImpellerPaintNew());
286+
ASSERT_TRUE(paint);
287+
ImpellerColor color = {0.0, 1.0, 1.0, 1.0};
288+
ImpellerPaintSetColor(paint.GetC(), &color);
289+
ImpellerParagraphStyleSetForeground(style.GetC(), paint.GetC());
290+
}
291+
292+
// Push the style onto the style stack.
293+
ImpellerParagraphBuilderPushStyle(builder.GetC(), style.GetC());
294+
std::string text = "0F0F0F0";
295+
296+
// Add the paragraph text data.
297+
ImpellerParagraphBuilderAddText(builder.GetC(),
298+
reinterpret_cast<const uint8_t*>(text.data()),
299+
text.size());
300+
301+
// Layout and build the paragraph.
302+
auto paragraph = Adopt<Paragraph>(
303+
ImpellerParagraphBuilderBuildParagraphNew(builder.GetC(), 1200.0f));
304+
ASSERT_TRUE(paragraph);
305+
306+
// Create a display list with just the paragraph drawn into it.
307+
auto dl_builder =
308+
Adopt<DisplayListBuilder>(ImpellerDisplayListBuilderNew(nullptr));
309+
ImpellerPoint point = {20, 20};
310+
ImpellerDisplayListBuilderDrawParagraph(dl_builder.GetC(), paragraph.GetC(),
311+
&point);
312+
auto dl = Adopt<DisplayList>(
313+
ImpellerDisplayListBuilderCreateDisplayListNew(dl_builder.GetC()));
314+
315+
ASSERT_TRUE(
316+
OpenPlaygroundHere([&](const auto& context, const auto& surface) -> bool {
317+
ImpellerSurfaceDrawDisplayList(surface.GetC(), dl.GetC());
318+
return true;
319+
}));
320+
} // namespace impeller::interop::testing
321+
248322
static void DrawTextFrame(ImpellerTypographyContext tc,
249323
ImpellerDisplayListBuilder builder,
250324
ImpellerParagraphStyle p_style,

impeller/toolkit/interop/typography_context.cc

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
#include <mutex>
88

99
#include "flutter/fml/icu_util.h"
10+
#include "flutter/third_party/txt/src/txt/platform.h"
11+
#include "impeller/base/validation.h"
1012
#include "impeller/toolkit/interop/embedded_icu_data.h"
1113

1214
namespace impeller::interop {
@@ -19,7 +21,12 @@ TypographyContext::TypographyContext()
1921
impeller_embedded_icu_data_data, impeller_embedded_icu_data_length);
2022
fml::icu::InitializeICUFromMapping(std::move(icu_data));
2123
});
24+
// The fallback for all fonts. Looks in platform specific locations.
2225
collection_->SetupDefaultFontManager(0u);
26+
27+
// Looks for fonts in user supplied blobs.
28+
asset_font_manager_ = sk_make_sp<skia::textlayout::TypefaceFontProvider>();
29+
collection_->SetAssetFontManager(asset_font_manager_);
2330
}
2431

2532
TypographyContext::~TypographyContext() = default;
@@ -33,4 +40,45 @@ TypographyContext::GetFontCollection() const {
3340
return collection_;
3441
}
3542

43+
static sk_sp<SkTypeface> CreateTypefaceFromFontData(
44+
std::unique_ptr<fml::Mapping> font_data) {
45+
if (!font_data) {
46+
VALIDATION_LOG << "Invalid font data.";
47+
return nullptr;
48+
}
49+
auto sk_data_context = font_data.release();
50+
auto sk_data = SkData::MakeWithProc(
51+
sk_data_context->GetMapping(), // data ptr
52+
sk_data_context->GetSize(), // data size
53+
[](const void*, void* context) {
54+
delete reinterpret_cast<decltype(sk_data_context)>(context);
55+
}, // release callback
56+
sk_data_context // release callback context
57+
);
58+
auto sk_data_stream = SkMemoryStream::Make(sk_data);
59+
auto sk_typeface =
60+
txt::GetDefaultFontManager()->makeFromStream(std::move(sk_data_stream));
61+
if (!sk_typeface) {
62+
VALIDATION_LOG << "Could not create typeface with data.";
63+
return nullptr;
64+
}
65+
return sk_typeface;
66+
}
67+
68+
bool TypographyContext::RegisterFont(std::unique_ptr<fml::Mapping> font_data,
69+
const char* family_name_alias) {
70+
auto typeface = CreateTypefaceFromFontData(std::move(font_data));
71+
if (typeface == nullptr) {
72+
return false;
73+
}
74+
size_t result = 0u;
75+
if (family_name_alias == nullptr) {
76+
result = asset_font_manager_->registerTypeface(std::move(typeface));
77+
} else {
78+
result = asset_font_manager_->registerTypeface(std::move(typeface),
79+
SkString{family_name_alias});
80+
}
81+
return result != 0;
82+
}
83+
3684
} // namespace impeller::interop

impeller/toolkit/interop/typography_context.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
#include <memory>
99

10+
#include "flutter/third_party/skia/modules/skparagraph/include/TypefaceFontProvider.h"
1011
#include "flutter/third_party/txt/src/txt/font_collection.h"
1112
#include "impeller/toolkit/interop/impeller.h"
1213
#include "impeller/toolkit/interop/object.h"
@@ -29,8 +30,22 @@ class TypographyContext final
2930

3031
const std::shared_ptr<txt::FontCollection>& GetFontCollection() const;
3132

33+
//----------------------------------------------------------------------------
34+
/// @brief Registers custom font data. If an alias for the family name is
35+
/// provided, subsequent lookups will need to use that same alias.
36+
/// If not, the family name will be read from the font data.
37+
///
38+
/// @param[in] font_data The font data
39+
/// @param[in] family_name_alias The family name alias
40+
///
41+
/// @return If the font data could be successfully registered.
42+
///
43+
bool RegisterFont(std::unique_ptr<fml::Mapping> font_data,
44+
const char* family_name_alias);
45+
3246
private:
3347
std::shared_ptr<txt::FontCollection> collection_;
48+
sk_sp<skia::textlayout::TypefaceFontProvider> asset_font_manager_;
3449
};
3550

3651
} // namespace impeller::interop

0 commit comments

Comments
 (0)