/** * Technically, the AFont API was introduced in Android 10 (Q, API 29). However... * * The AFontMatcher API implementation is broken from its introduction until at least API 33. What * is desired is to find a font for the given locale which contains the given character. However, * the implementation actually attempts to shape the string passed to it with the default font and * then returns the font chosen for the first run. However, this produces undesireable results, as * it will always prefer the default font over the locale, so any code points covered by the default * font will always come from the default font regardless of the requested locale. In addition, this * will claim coverage for code points "made up" by the shaper through normalization, * denormalization, whitespace synthesis, no-draw synthesis, etc, for the default font, when there * may be better choices later in fallback. * * On Android 10 (Q, API 29) AFont_getLocale always returns nullptr (if there is a locale set) or * whatever std::unique_ptr<std::string>()->c_str() returns, which happens to be 0x1. As a result, * AFont_getLocale cannot be used until Android 11 (R, API 30). This is b/139201432 and fixed with * "Make AFont_getLocale work" [0]. This change is in Android 11 (API 30) but does not appear to * have been cherry-picked into Android 10 (Q, API 29). * [0] https://cs.android.com/android/_/android/platform/frameworks/base/+/01709c7469b59e451f064c266bbe442e9bef0ab4 * * As a result, there is no correct way to use locale information from the Android 10 NDK. So this * font manager only works with Android 11 (R, API 30) and above.
*/ #define SK_FONTMGR_ANDROID_NDK_API_LEVEL __ANDROID_API_R__
/** Gets a BCP 47 language identifier for this SkLanguage. @return a BCP 47 language identifier representing this language
*/ const SkString& getTag() const { return fTag; }
/** Performs BCP 47 fallback to return an SkLanguage one step more general. @return an SkLanguage one step more general
*/
SkLanguage getParent() const {
SkASSERT(!fTag.isEmpty()); constchar* tag = fTag.c_str();
// strip off the rightmost "-.*" constchar* parentTagEnd = strrchr(tag, '-'); if (parentTagEnd == nullptr) { return SkLanguage();
}
size_t parentTagLen = parentTagEnd - tag; return SkLanguage(tag, parentTagLen);
}
booloperator==(const SkLanguage& b) const { return fTag == b.fTag;
} booloperator!=(const SkLanguage& b) const { return fTag != b.fTag;
}
using sk_is_trivially_relocatable = std::true_type; private: //! BCP 47 language identifier
SkString fTag;
static_assert(::sk_is_trivially_relocatable<decltype(fTag)>::value);
};
using sk_is_trivially_relocatable = std::true_type;
static_assert(::sk_is_trivially_relocatable<decltype(name)>::value);
static_assert(::sk_is_trivially_relocatable<decltype(normalizedName)>::value);
static_assert(::sk_is_trivially_relocatable<decltype(styleSet)>::value);
};
class SkFontMgr_AndroidNDK : public SkFontMgr { void addSystemTypeface(sk_sp<SkTypeface_AndroidNDK> typeface, const SkString& name) {
NameToFamily* nameToFamily = nullptr; for (NameToFamily& current : fNameToFamilyMap) { if (current.name == name) {
nameToFamily = ¤t; break;
}
} if (!nameToFamily) {
sk_sp<SkFontStyleSet_AndroidNDK> newSet(new SkFontStyleSet_AndroidNDK());
SkAutoAsciiToLC tolc(name.c_str());
nameToFamily = &fNameToFamilyMap.emplace_back(
NameToFamily{name, SkString(tolc.lc(), tolc.length()), newSet.get()});
fStyleSets.push_back(std::move(newSet));
} if constexpr (kSkFontMgrVerbose) { SkDebugf("SKIA: Adding member to %s\n", name.c_str()); }
nameToFamily->styleSet->fStyles.push_back(typeface);
}
public:
SkFontMgr_AndroidNDK(const AndroidFontAPI& androidFontAPI, boolconst cacheFontFiles,
std::unique_ptr<SkFontScanner> scanner)
: fAPI(androidFontAPI)
, fScanner(std::move(scanner))
{
SkASystemFontIterator fontIter(fAPI); if (!fontIter) { if constexpr (kSkFontMgrVerbose) { SkDebugf("SKIA: No ASystemFontIterator"); } return;
}
if constexpr (kSkFontMgrVerbose) { SkDebugf("SKIA: Iterating over AFonts\n"); } while (SkAFont font = fontIter.next()) {
sk_sp<SkTypeface_AndroidNDK> typeface = this->make(std::move(font), cacheFontFiles); if (!typeface) { continue;
}
// A font may have many localized family names.
sk_sp<SkTypeface::LocalizedStrings> names(typeface->createFamilyNameIterator());
SkTypeface::LocalizedString localeName; while (names->next(&localeName)) { if (localeName.fString != name) {
this->addSystemTypeface(typeface, localeName.fString);
}
}
// There nothing in the NDK to indicate how to handle generic font names like 'serif', // 'sans-serif`, 'monospace', etc.
}
if (fStyleSets.empty()) { if constexpr (kSkFontMgrVerbose) { SkDebugf("SKIA: No fonts!"); }
} else {
this->findDefaultStyleSet();
}
}
protected: /** Returns not how many families we have, but how many unique names * exist among the families.
*/ int onCountFamilies() const override { return fNameToFamilyMap.size();
}
std::unique_ptr<SkStreamAsset> stream = SkStream::MakeFromFile(filePath); if (!stream) { if constexpr (kSkFontMgrVerbose) {
SkDebugf("SKIA: Font file %s does not exist or cannot be opened.\n", filePath);
} return nullptr;
}
size_t collectionIndex = font.getCollectionIndex(); if constexpr (kSkFontMgrVerbose) {
SkDebugf("SKIA: Making font from %s#%zu\n", filePath, collectionIndex);
} if (!SkTFitsIn<int>(collectionIndex)) { if constexpr (kSkFontMgrVerbose) { SkDebugf("SKIA: Collection index invalid!"); } return nullptr;
} constint ttcIndex = SkTo<int>(collectionIndex);
SkString familyName;
SkFontStyle style; bool isFixedWidth;
SkFontScanner::AxisDefinitions axisDefinitions; if (!fScanner->scanInstance(stream.get(), ttcIndex, 0,
&familyName, &style, &isFixedWidth, &axisDefinitions))
{ if constexpr (kSkFontMgrVerbose) {
SkDebugf("SKIA: Font file %s exists, but is not a valid font.\n", filePath);
} return nullptr;
}
int weight = SkTo<int>(font.getWeight());
SkFontStyle::Slant slant = style.slant(); if (font.isItalic()) {
slant = SkFontStyle::kItalic_Slant;
} int width = style.width();
constexpr SkFourByteTag wdth = SkSetFourByteTag('w','d','t','h');
// The family name(s) are not reported. // This would be very helpful for aliases, like "sans-serif", "Arial", etc.
size_t requestAxisCount = font.getAxisCount(); if (!SkTFitsIn<int>(requestAxisCount)) { if constexpr (kSkFontMgrVerbose) { SkDebugf("SKIA: Axis count unreasonable!"); } return nullptr;
} using Coordinate = SkFontArguments::VariationPosition::Coordinate;
AutoSTMalloc<4, Coordinate> requestAxisValues(requestAxisCount); for (size_t i = 0; i < requestAxisCount; ++i) {
uint32_t tag = font.getAxisTag(i); float value = font.getAxisValue(i);
requestAxisValues[i] = { tag, value }; if (tag == wdth) { // Set the width based on the requested `wdth` axis value.
width = SkFontDescriptor::SkFontStyleWidthForWidthAxisValue(value);
}
}
if (face->unicharToGlyph(character) == 0) { returnfalse;
}
if constexpr (kSkFontMgrVerbose) {
SkString foundName;
face->getFamilyName(&foundName);
SkDebugf("SKIA: Found U+%" PRIx32 " in \"%s\" lang \"%s\" scope %s step %zu.\n",
character, foundName.c_str(), langTag.c_str(), scope, *step);
} returntrue;
}
sk_sp<SkTypeface> findByCharacterLocaleFamily(
SkTypeface_AndroidNDK* familyFace, const SkFontStyle& style, const SkString& langTag,
SkUnichar character) const
{
size_t step = 0; // First look at the familyFace if (familyFace && has_locale_and_character(familyFace, langTag, character, "face", &step)) { return sk_ref_sp(familyFace);
}
// Look through the styles that match in each family. for (int i = 0; i < fNameToFamilyMap.size(); ++i) {
SkFontStyleSet_AndroidNDK* family = fNameToFamilyMap[i].styleSet;
sk_sp<SkTypeface> face(family->matchStyle(style)); auto aface = static_cast<SkTypeface_AndroidNDK*>(face.get()); if (has_locale_and_character(aface, langTag, character, "style", &step)) { return face;
}
}
// Look through everything.
// Android by default has a setup like // /system/fonts/NotoSansSymbols-Regular-Subsetted.ttf#0 // /system/fonts/NotoSansSymbols-Regular-Subsetted2.ttf#0 // Which are both "Noto Sans Symbols" so end up in a "family" together. However, these // are not in the same family, these are two different fonts in different families and // should have been given different names. Internally this works because these are // in separate <family> tags, but the NDK API doesn't provide that information. // While Android internally depends on all fonts in a family having the same characters // mapped, this cannot be relied upon when guessing at the families by name.
for (int i = 0; i < fNameToFamilyMap.size(); ++i) {
SkFontStyleSet_AndroidNDK* family = fNameToFamilyMap[i].styleSet; for (int j = 0; j < family->count(); ++j) {
sk_sp<SkTypeface> face(family->createTypeface(j)); auto aface = static_cast<SkTypeface_AndroidNDK*>(face.get()); if (has_locale_and_character(aface, langTag, character, "anything", &step)) { return face;
}
}
}
sk_sp<SkTypeface> onLegacyMakeTypeface(constchar name[], SkFontStyle style) const override { if (name) { // On Android, we must return nullptr when we can't find the requested // named typeface so that the system/app can provide their own recovery // mechanism. On other platforms we'd provide a typeface from the // default family instead. return sk_sp<SkTypeface>(this->onMatchFamilyStyle(name, style));
} if (fDefaultStyleSet) { return sk_sp<SkTypeface>(fDefaultStyleSet->matchStyle(style));
} return nullptr;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.