Index: src/ports/SkFontConfigParser_android.cpp |
diff --git a/src/ports/SkFontConfigParser_android.cpp b/src/ports/SkFontConfigParser_android.cpp |
index fbbb6a627a2b21f62d52abebaca152fd8fd7875d..d1169b99fb60759b5ecfedf8e224f2085fbaea82 100644 |
--- a/src/ports/SkFontConfigParser_android.cpp |
+++ b/src/ports/SkFontConfigParser_android.cpp |
@@ -77,15 +77,190 @@ template <typename T> static bool parseNonNegativeInteger(const char* s, T* valu |
namespace lmpParser { |
-void startElementHandler(void* data, const char* tag, |
- const char** attributes) { |
- //SkDebugf("lmp started %s", tag); |
+void familyElementHandler(FontFamily* family, const char** attributes) { |
+ // A non-fallback <family> tag must have a canonical name attribute. |
+ // A (fallback) <family> tag may have lang and variant attributes. |
+ for (int i = 0; attributes[i] != NULL; i += 2) { |
palmer
2014/08/06 21:05:30
Is there a guarantee that |attributes| always has
reed1
2014/08/06 21:20:05
Skia uses int instead of size_t for most counters
tomhudson
2014/08/07 14:39:42
We expect that the XML Parser (expat) guarantees t
|
+ const char* name = attributes[i]; |
+ const char* value = attributes[i+1]; |
reed1
2014/08/06 21:20:05
could assert that value != NULL, to address the co
|
+ int nameLen = strlen(name); |
palmer
2014/08/06 21:05:29
strlen returns size_t, not int. (The 2 types diffe
reed1
2014/08/06 21:20:05
+1
tomhudson
2014/08/07 14:39:43
Done ~ 16x.
(Old code.)
There are many other inst
|
+ int valueLen = strlen(value); |
+ if (nameLen == 4 && !strncmp("name", name, nameLen)) { |
+ family->fNames.push_back().set(value); |
+ } else if (nameLen == 4 && !strncmp("lang", name, nameLen)) { |
+ family->fLanguage = SkLanguage (value); |
+ } else if (nameLen == 7 && !strncmp("variant", name, nameLen)) { |
+ // Value should be either elegant or compact. |
+ if (valueLen == 7 && !strncmp("elegant", value, valueLen)) { |
+ family->fVariant = SkPaintOptionsAndroid::kElegant_Variant; |
+ } else if (valueLen == 7 && !strncmp("compact", value, valueLen)) { |
+ family->fVariant = SkPaintOptionsAndroid::kCompact_Variant; |
+ } |
+ } |
+ } |
} |
-void endElementHandler(void* data, const char* tag) { |
+void fontFileNameHandler(void *data, const char *s, int len) { |
palmer
2014/08/06 21:05:30
Nit: "void *data" conflicts with the "FontFamily*
tomhudson
2014/08/07 14:39:42
Done. Unfortunately, https://sites.google.com/site
|
+ FamilyData *familyData = (FamilyData*) data; |
palmer
2014/08/06 21:05:30
Prefer C++ style cast?
|
+ familyData->currentFontInfo->fFileName.set(s, len); |
palmer
2014/08/06 21:05:30
If |set| is defined to take an int length argument
reed1
2014/08/06 21:20:05
+1
tomhudson
2014/08/07 14:39:43
SkString::set() takes size_t, but is happy to impl
|
+} |
- //SkDebugf("lmp ended %s", tag); |
+void familyElementEndHandler(FontFamily* family) { |
+ for (int i = 0; i < family->fFontFiles.count(); i++) { |
palmer
2014/08/06 21:05:29
size_t, and if |count| returns int, that's a bug
reed1
2014/08/06 21:20:05
count() returns an int.
tomhudson
2014/08/07 14:39:42
Acknowledged. Filed http://skbug.com/2811. Since w
|
+ family->fFontFiles[i].fPaintOptions.setLanguage(family->fLanguage); |
+ family->fFontFiles[i].fPaintOptions.setFontVariant(family->fVariant); |
+ } |
+} |
+ |
+void fontElementHandler(XML_Parser* parser, FontFileInfo* file, const char** attributes) { |
+ // A <font> should have weight (integer) and style (normal, italic) attributes. |
+ // NOTE: we ignore the style. |
+ // The element should contain a filename. |
+ for (int i = 0; attributes[i] != NULL; i += 2) { |
palmer
2014/08/06 21:05:29
Same int and += 2 comments as above
tomhudson
2014/08/07 14:39:43
Done.
|
+ const char* name = attributes[i]; |
+ const char* value = attributes[i+1]; |
+ int nameLen = strlen(name); |
+ if (nameLen == 6 && !strncmp("weight", name, nameLen)) { |
+ parseNonNegativeInteger(value, &file->fWeight); |
palmer
2014/08/06 21:05:30
What if parsing fails?
tomhudson
2014/08/07 14:39:42
Done.
|
+ } |
+ } |
+ XML_SetCharacterDataHandler(*parser, fontFileNameHandler); |
+} |
+ |
+FontFamily* findFamily(FamilyData* familyData, const char* familyName) { |
+ unsigned int nameLen = strlen(familyName); |
palmer
2014/08/06 21:05:29
size_t, not unsigned int
tomhudson
2014/08/07 14:39:42
Done.
|
+ for (int i = 0; i < familyData->families.count(); i++) { |
+ FontFamily* candidate = familyData->families[i]; |
+ for (int j = 0; j < candidate->fNames.count(); j++) { |
+ if (!strncmp(candidate->fNames[j].c_str(), familyName, nameLen) && |
+ nameLen == strlen(candidate->fNames[j].c_str())) { |
+ return candidate; |
+ } |
+ } |
+ } |
+ return NULL; |
+} |
+ |
+void aliasElementHandler(FamilyData* familyData, const char** attributes) { |
+ // An <alias> must have name and to attributes. |
+ // It may have weight (integer). |
+ // If it *does not* have a weight, it is a variant name for a <family>. |
+ // If it *does* have a weight, it names the <font>(s) of a specific weight |
+ // from a <family>. |
+ |
+ SkString aliasName; |
+ SkString to; |
+ int weight = 0; |
+ for (int i = 0; attributes[i] != NULL; i += 2) { |
+ const char* name = attributes[i]; |
+ const char* value = attributes[i+1]; |
+ int nameLen = strlen(name); |
+ if (nameLen == 4 && !strncmp("name", name, nameLen)) { |
+ aliasName.set(value); |
+ } else if (nameLen == 2 && !strncmp("to", name, nameLen)) { |
+ to.set(value); |
+ } else if (nameLen == 6 && !strncmp("weight", name, nameLen)) { |
+ parseNonNegativeInteger(value, &weight); |
+ } |
+ } |
+ |
+ // Assumes that the named family is already declared |
+ FontFamily* targetFamily = findFamily(familyData, to.c_str()); |
+ if (!targetFamily) { |
+ SkDebugf("---- Font alias target %s (NOT FOUND)", to.c_str()); |
+ return; |
+ } |
+ |
+ if (weight) { |
+ FontFamily* family = new FontFamily(); |
+ family->fNames.push_back().set(aliasName); |
+ |
+ for (int i = 0; i < targetFamily->fFontFiles.count(); i++) { |
+ if (targetFamily->fFontFiles[i].fWeight == weight) { |
+ family->fFontFiles.push_back(targetFamily->fFontFiles[i]); |
+ } |
+ } |
+ *familyData->families.append() = family; |
+ } else { |
+ targetFamily->fNames.push_back().set(aliasName); |
+ } |
+} |
+ |
+bool findWeight400(FontFamily* family) { |
+ for (int i = 0; i < family->fFontFiles.count(); i++) { |
+ if (family->fFontFiles[i].fWeight == 400) { |
+ return true; |
+ } |
+ } |
+ return false; |
+} |
+ |
+bool desiredWeight(int weight) { |
+ return (weight == 400 || weight == 700); |
+} |
+ |
+int countDesiredWeight(FontFamily* family) { |
+ int count = 0; |
+ for (int i = 0; i < family->fFontFiles.count(); i++) { |
+ if (desiredWeight(family->fFontFiles[i].fWeight)) { |
+ count++; |
+ } |
+ } |
+ return count; |
+} |
+ |
+// To meet Skia's expectations, any family that contains weight=400 |
+// fonts should *only* contain {400,700} |
+void purgeUndesiredWeights(FontFamily* family) { |
+ int count = countDesiredWeight(family); |
+ for (int i = 1, j = 0; i < family->fFontFiles.count(); i++) { |
+ if (desiredWeight(family->fFontFiles[j].fWeight)) { |
+ j++; |
+ } |
+ if ((i != j) && desiredWeight(family->fFontFiles[i].fWeight)) { |
+ family->fFontFiles[j] = family->fFontFiles[i]; |
+ } |
+ } |
+ family->fFontFiles.resize_back(count); |
+} |
+ |
+void familysetElementEndHandler(FamilyData* familyData) { |
+ for (int i = 0; i < familyData->families.count(); i++) { |
+ if (findWeight400(familyData->families[i])) { |
+ purgeUndesiredWeights(familyData->families[i]); |
+ } |
+ } |
+} |
+ |
+void startElementHandler(void* data, const char* tag, |
+ const char** attributes) { |
+ FamilyData* familyData = (FamilyData*) data; |
+ int len = strlen(tag); |
+ if (len == 6 && !strncmp(tag, "family", len)) { |
+ familyData->currentFamily = new FontFamily(); |
+ familyElementHandler(familyData->currentFamily, attributes); |
+ } else if (len == 4 && !strncmp(tag, "font", len)) { |
+ FontFileInfo* file = &familyData->currentFamily->fFontFiles.push_back(); |
+ familyData->currentFontInfo = file; |
+ fontElementHandler(familyData->parser, file, attributes); |
+ } else if (len == 5 && !strncmp(tag, "alias", len)) { |
+ aliasElementHandler(familyData, attributes); |
+ } |
+} |
+ |
+void endElementHandler(void* data, const char* tag) { |
+ FamilyData *familyData = (FamilyData*) data; |
+ int len = strlen(tag); |
+ if (len == 9 && strncmp(tag, "familyset", len) == 0) { |
+ familysetElementEndHandler(familyData); |
+ } else if (len == 6 && strncmp(tag, "family", len) == 0) { |
+ familyElementEndHandler(familyData->currentFamily); |
+ *familyData->families.append() = familyData->currentFamily; |
+ familyData->currentFamily = NULL; |
+ } else if (len == 4 && !strncmp(tag, "font", len)) { |
+ XML_SetCharacterDataHandler(*familyData->parser, NULL); |
+ } |
} |
} // lmpParser |