blob: 761a475a27732fd110925cbc042c16472f665c8a [file] [log] [blame]
Seigo Nonaka50692ca2018-08-31 12:27:15 -07001/*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <jni.h>
18
19#include <android/system_fonts.h>
20
21#include <memory>
22#include <string>
23#include <vector>
24
25#include <errno.h>
26#include <fcntl.h>
27#include <libxml/tree.h>
28#include <log/log.h>
29#include <sys/stat.h>
30#include <unistd.h>
31
32struct XmlCharDeleter {
33 void operator()(xmlChar* b) { xmlFree(b); }
34};
35
36struct XmlDocDeleter {
37 void operator()(xmlDoc* d) { xmlFreeDoc(d); }
38};
39
40using XmlCharUniquePtr = std::unique_ptr<xmlChar, XmlCharDeleter>;
41using XmlDocUniquePtr = std::unique_ptr<xmlDoc, XmlDocDeleter>;
42
43struct ASystemFontIterator {
44 XmlDocUniquePtr mXmlDoc;
45 xmlNode* mFontNode;
Seigo Nonaka36758982018-10-01 19:06:11 -070046
47 // The OEM customization XML.
48 XmlDocUniquePtr mCustomizationXmlDoc;
Seigo Nonaka50692ca2018-08-31 12:27:15 -070049};
50
51struct ASystemFont {
52 std::string mFilePath;
53 std::unique_ptr<std::string> mLocale;
54 uint16_t mWeight;
55 bool mItalic;
56 uint32_t mCollectionIndex;
57 std::vector<std::pair<uint32_t, float>> mAxes;
58};
59
60namespace {
61
62std::string xmlTrim(const std::string& in) {
63 if (in.empty()) {
64 return in;
65 }
66 const char XML_SPACES[] = "\u0020\u000D\u000A\u0009";
67 const size_t start = in.find_first_not_of(XML_SPACES); // inclusive
68 if (start == std::string::npos) {
69 return "";
70 }
71 const size_t end = in.find_last_not_of(XML_SPACES); // inclusive
72 if (end == std::string::npos) {
73 return "";
74 }
75 return in.substr(start, end - start + 1 /* +1 since end is inclusive */);
76}
77
78const xmlChar* FAMILY_TAG = BAD_CAST("family");
79const xmlChar* FONT_TAG = BAD_CAST("font");
80
81xmlNode* firstElement(xmlNode* node, const xmlChar* tag) {
82 for (xmlNode* child = node->children; child; child = child->next) {
83 if (xmlStrEqual(child->name, tag)) {
84 return child;
85 }
86 }
87 return nullptr;
88}
89
90xmlNode* nextSibling(xmlNode* node, const xmlChar* tag) {
91 while ((node = node->next) != nullptr) {
92 if (xmlStrEqual(node->name, tag)) {
93 return node;
94 }
95 }
96 return nullptr;
97}
98
Seigo Nonaka36758982018-10-01 19:06:11 -070099void copyFont(const XmlDocUniquePtr& xmlDoc, xmlNode* fontNode, ASystemFont* out,
100 const std::string& pathPrefix) {
Seigo Nonaka50692ca2018-08-31 12:27:15 -0700101 const xmlChar* LOCALE_ATTR_NAME = BAD_CAST("lang");
102 XmlCharUniquePtr filePathStr(
Seigo Nonaka36758982018-10-01 19:06:11 -0700103 xmlNodeListGetString(xmlDoc.get(), fontNode->xmlChildrenNode, 1));
104 out->mFilePath = pathPrefix + xmlTrim(
Seigo Nonaka50692ca2018-08-31 12:27:15 -0700105 std::string(filePathStr.get(), filePathStr.get() + xmlStrlen(filePathStr.get())));
106
107 const xmlChar* WEIGHT_ATTR_NAME = BAD_CAST("weight");
Seigo Nonaka36758982018-10-01 19:06:11 -0700108 XmlCharUniquePtr weightStr(xmlGetProp(fontNode, WEIGHT_ATTR_NAME));
Seigo Nonaka50692ca2018-08-31 12:27:15 -0700109 out->mWeight = weightStr ?
110 strtol(reinterpret_cast<const char*>(weightStr.get()), nullptr, 10) : 400;
111
112 const xmlChar* STYLE_ATTR_NAME = BAD_CAST("style");
113 const xmlChar* ITALIC_ATTR_VALUE = BAD_CAST("italic");
Seigo Nonaka36758982018-10-01 19:06:11 -0700114 XmlCharUniquePtr styleStr(xmlGetProp(fontNode, STYLE_ATTR_NAME));
Seigo Nonaka50692ca2018-08-31 12:27:15 -0700115 out->mItalic = styleStr ? xmlStrEqual(styleStr.get(), ITALIC_ATTR_VALUE) : false;
116
117 const xmlChar* INDEX_ATTR_NAME = BAD_CAST("index");
Seigo Nonaka36758982018-10-01 19:06:11 -0700118 XmlCharUniquePtr indexStr(xmlGetProp(fontNode, INDEX_ATTR_NAME));
Seigo Nonaka50692ca2018-08-31 12:27:15 -0700119 out->mCollectionIndex = indexStr ?
120 strtol(reinterpret_cast<const char*>(indexStr.get()), nullptr, 10) : 0;
121
Seigo Nonaka36758982018-10-01 19:06:11 -0700122 XmlCharUniquePtr localeStr(xmlGetProp(xmlDoc->parent, LOCALE_ATTR_NAME));
Seigo Nonaka50692ca2018-08-31 12:27:15 -0700123 out->mLocale.reset(
124 localeStr ? new std::string(reinterpret_cast<const char*>(localeStr.get())) : nullptr);
125
126 const xmlChar* TAG_ATTR_NAME = BAD_CAST("tag");
127 const xmlChar* STYLEVALUE_ATTR_NAME = BAD_CAST("stylevalue");
128 const xmlChar* AXIS_TAG = BAD_CAST("axis");
129 out->mAxes.clear();
Seigo Nonaka36758982018-10-01 19:06:11 -0700130 for (xmlNode* axis = firstElement(fontNode, AXIS_TAG); axis;
Seigo Nonaka50692ca2018-08-31 12:27:15 -0700131 axis = nextSibling(axis, AXIS_TAG)) {
132 XmlCharUniquePtr tagStr(xmlGetProp(axis, TAG_ATTR_NAME));
133 if (!tagStr || xmlStrlen(tagStr.get()) != 4) {
134 continue; // Tag value must be 4 char string
135 }
136
137 XmlCharUniquePtr styleValueStr(xmlGetProp(axis, STYLEVALUE_ATTR_NAME));
138 if (!styleValueStr) {
139 continue;
140 }
141
142 uint32_t tag =
143 static_cast<uint32_t>(tagStr.get()[0] << 24) |
144 static_cast<uint32_t>(tagStr.get()[1] << 16) |
145 static_cast<uint32_t>(tagStr.get()[2] << 8) |
146 static_cast<uint32_t>(tagStr.get()[3]);
147 float styleValue = strtod(reinterpret_cast<const char*>(styleValueStr.get()), nullptr);
148 out->mAxes.push_back(std::make_pair(tag, styleValue));
149 }
150}
151
152bool isFontFileAvailable(const std::string& filePath) {
153 std::string fullPath = filePath;
154 struct stat st = {};
155 if (stat(fullPath.c_str(), &st) != 0) {
156 return false;
157 }
158 return S_ISREG(st.st_mode);
159}
160
Seigo Nonaka36758982018-10-01 19:06:11 -0700161xmlNode* findFirstFontNode(const XmlDocUniquePtr& doc) {
162 xmlNode* familySet = xmlDocGetRootElement(doc.get());
Seigo Nonaka50692ca2018-08-31 12:27:15 -0700163 if (familySet == nullptr) {
164 return nullptr;
165 }
166 xmlNode* family = firstElement(familySet, FAMILY_TAG);
167 if (family == nullptr) {
168 return nullptr;
169 }
170
171 xmlNode* font = firstElement(family, FONT_TAG);
172 while (font == nullptr) {
173 family = nextSibling(family, FAMILY_TAG);
174 if (family == nullptr) {
175 return nullptr;
176 }
177 font = firstElement(family, FONT_TAG);
178 }
179 return font;
180}
181
182} // namespace
183
184ASystemFontIterator* ASystemFontIterator_open() {
185 std::unique_ptr<ASystemFontIterator> ite(new ASystemFontIterator());
186 ite->mXmlDoc.reset(xmlReadFile("/system/etc/fonts.xml", nullptr, 0));
Seigo Nonaka36758982018-10-01 19:06:11 -0700187 ite->mCustomizationXmlDoc.reset(xmlReadFile("/product/etc/fonts_customization.xml", nullptr, 0));
Seigo Nonaka50692ca2018-08-31 12:27:15 -0700188 return ite.release();
189}
190
191void ASystemFontIterator_close(ASystemFontIterator* ite) {
192 delete ite;
193}
194
Seigo Nonaka36758982018-10-01 19:06:11 -0700195xmlNode* findNextFontNode(const XmlDocUniquePtr& xmlDoc, xmlNode* fontNode) {
196 if (fontNode == nullptr) {
197 if (!xmlDoc) {
Seigo Nonaka50692ca2018-08-31 12:27:15 -0700198 return nullptr; // Already at the end.
199 } else {
200 // First time to query font.
Seigo Nonaka36758982018-10-01 19:06:11 -0700201 return findFirstFontNode(xmlDoc);
Seigo Nonaka50692ca2018-08-31 12:27:15 -0700202 }
203 } else {
Seigo Nonaka36758982018-10-01 19:06:11 -0700204 xmlNode* nextNode = nextSibling(fontNode, FONT_TAG);
Seigo Nonaka50692ca2018-08-31 12:27:15 -0700205 while (nextNode == nullptr) {
Seigo Nonaka36758982018-10-01 19:06:11 -0700206 xmlNode* family = nextSibling(fontNode->parent, FAMILY_TAG);
Seigo Nonaka50692ca2018-08-31 12:27:15 -0700207 if (family == nullptr) {
208 break;
209 }
210 nextNode = firstElement(family, FONT_TAG);
211 }
Seigo Nonaka36758982018-10-01 19:06:11 -0700212 return nextNode;
Seigo Nonaka50692ca2018-08-31 12:27:15 -0700213 }
214}
215
Seigo Nonaka36758982018-10-01 19:06:11 -0700216ASystemFont* ASystemFontIterator_next(ASystemFontIterator* ite) {
217 LOG_ALWAYS_FATAL_IF(ite == nullptr, "nullptr has passed as iterator argument");
218 if (ite->mXmlDoc) {
219 ite->mFontNode = findNextFontNode(ite->mXmlDoc, ite->mFontNode);
220 if (ite->mFontNode == nullptr) {
221 // Reached end of the XML file. Continue OEM customization.
222 ite->mXmlDoc.reset();
223 ite->mFontNode = nullptr;
224 } else {
225 std::unique_ptr<ASystemFont> font = std::make_unique<ASystemFont>();
226 copyFont(ite->mXmlDoc, ite->mFontNode, font.get(), "/system/fonts/");
227 if (!isFontFileAvailable(font->mFilePath)) {
228 return ASystemFontIterator_next(ite);
229 }
230 return font.release();
231 }
232 }
233 if (ite->mCustomizationXmlDoc) {
234 // TODO: Filter only customizationType="new-named-family"
235 ite->mFontNode = findNextFontNode(ite->mCustomizationXmlDoc, ite->mFontNode);
236 if (ite->mFontNode == nullptr) {
237 // Reached end of the XML file. Finishing
238 ite->mCustomizationXmlDoc.reset();
239 ite->mFontNode = nullptr;
240 return nullptr;
241 } else {
242 std::unique_ptr<ASystemFont> font = std::make_unique<ASystemFont>();
243 copyFont(ite->mCustomizationXmlDoc, ite->mFontNode, font.get(), "/product/fonts/");
244 if (!isFontFileAvailable(font->mFilePath)) {
245 return ASystemFontIterator_next(ite);
246 }
247 return font.release();
248 }
249 }
250 return nullptr;
251}
252
Seigo Nonaka50692ca2018-08-31 12:27:15 -0700253void ASystemFont_close(ASystemFont* font) {
254 delete font;
255}
256
257const char* ASystemFont_getFontFilePath(const ASystemFont* font) {
258 LOG_ALWAYS_FATAL_IF(font == nullptr, "nullptr has passed as font argument");
259 return font->mFilePath.c_str();
260}
261
262uint16_t ASystemFont_getWeight(const ASystemFont* font) {
263 LOG_ALWAYS_FATAL_IF(font == nullptr, "nullptr has passed as font argument");
264 return font->mWeight;
265}
266
267bool ASystemFont_isItalic(const ASystemFont* font) {
268 LOG_ALWAYS_FATAL_IF(font == nullptr, "nullptr has passed as font argument");
269 return font->mItalic;
270}
271
272const char* ASystemFont_getLocale(const ASystemFont* font) {
273 LOG_ALWAYS_FATAL_IF(font == nullptr, "nullptr has passed to font argument");
274 return font->mLocale ? nullptr : font->mLocale->c_str();
275}
276
277size_t ASystemFont_getCollectionIndex(const ASystemFont* font) {
278 LOG_ALWAYS_FATAL_IF(font == nullptr, "nullptr has passed to font argument");
279 return font->mCollectionIndex;
280}
281
282size_t ASystemFont_getAxisCount(const ASystemFont* font) {
283 LOG_ALWAYS_FATAL_IF(font == nullptr, "nullptr has passed to font argument");
284 return font->mAxes.size();
285}
286
287uint32_t ASystemFont_getAxisTag(const ASystemFont* font, uint32_t axisIndex) {
288 LOG_ALWAYS_FATAL_IF(font == nullptr, "nullptr has passed to font argument");
289 LOG_ALWAYS_FATAL_IF(axisIndex >= font->mAxes.size(),
290 "given axis index is out of bounds. (< %zd", font->mAxes.size());
291 return font->mAxes[axisIndex].first;
292}
293
294float ASystemFont_getAxisValue(const ASystemFont* font, uint32_t axisIndex) {
295 LOG_ALWAYS_FATAL_IF(font == nullptr, "nullptr has passed to font argument");
296 LOG_ALWAYS_FATAL_IF(axisIndex >= font->mAxes.size(),
297 "given axis index is out of bounds. (< %zd", font->mAxes.size());
298 return font->mAxes[axisIndex].second;
299}