blob: b95adad78f897f285b5368c3b52113ec2692d814 [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;
46};
47
48struct ASystemFont {
49 std::string mFilePath;
50 std::unique_ptr<std::string> mLocale;
51 uint16_t mWeight;
52 bool mItalic;
53 uint32_t mCollectionIndex;
54 std::vector<std::pair<uint32_t, float>> mAxes;
55};
56
57namespace {
58
59std::string xmlTrim(const std::string& in) {
60 if (in.empty()) {
61 return in;
62 }
63 const char XML_SPACES[] = "\u0020\u000D\u000A\u0009";
64 const size_t start = in.find_first_not_of(XML_SPACES); // inclusive
65 if (start == std::string::npos) {
66 return "";
67 }
68 const size_t end = in.find_last_not_of(XML_SPACES); // inclusive
69 if (end == std::string::npos) {
70 return "";
71 }
72 return in.substr(start, end - start + 1 /* +1 since end is inclusive */);
73}
74
75const xmlChar* FAMILY_TAG = BAD_CAST("family");
76const xmlChar* FONT_TAG = BAD_CAST("font");
77
78xmlNode* firstElement(xmlNode* node, const xmlChar* tag) {
79 for (xmlNode* child = node->children; child; child = child->next) {
80 if (xmlStrEqual(child->name, tag)) {
81 return child;
82 }
83 }
84 return nullptr;
85}
86
87xmlNode* nextSibling(xmlNode* node, const xmlChar* tag) {
88 while ((node = node->next) != nullptr) {
89 if (xmlStrEqual(node->name, tag)) {
90 return node;
91 }
92 }
93 return nullptr;
94}
95
96void copyFont(ASystemFontIterator* ite, ASystemFont* out) {
97 const xmlChar* LOCALE_ATTR_NAME = BAD_CAST("lang");
98 XmlCharUniquePtr filePathStr(
99 xmlNodeListGetString(ite->mXmlDoc.get(), ite->mFontNode->xmlChildrenNode, 1));
100 out->mFilePath = "/system/fonts/" + xmlTrim(
101 std::string(filePathStr.get(), filePathStr.get() + xmlStrlen(filePathStr.get())));
102
103 const xmlChar* WEIGHT_ATTR_NAME = BAD_CAST("weight");
104 XmlCharUniquePtr weightStr(xmlGetProp(ite->mFontNode, WEIGHT_ATTR_NAME));
105 out->mWeight = weightStr ?
106 strtol(reinterpret_cast<const char*>(weightStr.get()), nullptr, 10) : 400;
107
108 const xmlChar* STYLE_ATTR_NAME = BAD_CAST("style");
109 const xmlChar* ITALIC_ATTR_VALUE = BAD_CAST("italic");
110 XmlCharUniquePtr styleStr(xmlGetProp(ite->mFontNode, STYLE_ATTR_NAME));
111 out->mItalic = styleStr ? xmlStrEqual(styleStr.get(), ITALIC_ATTR_VALUE) : false;
112
113 const xmlChar* INDEX_ATTR_NAME = BAD_CAST("index");
114 XmlCharUniquePtr indexStr(xmlGetProp(ite->mFontNode, INDEX_ATTR_NAME));
115 out->mCollectionIndex = indexStr ?
116 strtol(reinterpret_cast<const char*>(indexStr.get()), nullptr, 10) : 0;
117
118 XmlCharUniquePtr localeStr(xmlGetProp(ite->mXmlDoc->parent, LOCALE_ATTR_NAME));
119 out->mLocale.reset(
120 localeStr ? new std::string(reinterpret_cast<const char*>(localeStr.get())) : nullptr);
121
122 const xmlChar* TAG_ATTR_NAME = BAD_CAST("tag");
123 const xmlChar* STYLEVALUE_ATTR_NAME = BAD_CAST("stylevalue");
124 const xmlChar* AXIS_TAG = BAD_CAST("axis");
125 out->mAxes.clear();
126 for (xmlNode* axis = firstElement(ite->mFontNode, AXIS_TAG); axis;
127 axis = nextSibling(axis, AXIS_TAG)) {
128 XmlCharUniquePtr tagStr(xmlGetProp(axis, TAG_ATTR_NAME));
129 if (!tagStr || xmlStrlen(tagStr.get()) != 4) {
130 continue; // Tag value must be 4 char string
131 }
132
133 XmlCharUniquePtr styleValueStr(xmlGetProp(axis, STYLEVALUE_ATTR_NAME));
134 if (!styleValueStr) {
135 continue;
136 }
137
138 uint32_t tag =
139 static_cast<uint32_t>(tagStr.get()[0] << 24) |
140 static_cast<uint32_t>(tagStr.get()[1] << 16) |
141 static_cast<uint32_t>(tagStr.get()[2] << 8) |
142 static_cast<uint32_t>(tagStr.get()[3]);
143 float styleValue = strtod(reinterpret_cast<const char*>(styleValueStr.get()), nullptr);
144 out->mAxes.push_back(std::make_pair(tag, styleValue));
145 }
146}
147
148bool isFontFileAvailable(const std::string& filePath) {
149 std::string fullPath = filePath;
150 struct stat st = {};
151 if (stat(fullPath.c_str(), &st) != 0) {
152 return false;
153 }
154 return S_ISREG(st.st_mode);
155}
156
157xmlNode* findFirstFontNode(xmlDoc* doc) {
158 xmlNode* familySet = xmlDocGetRootElement(doc);
159 if (familySet == nullptr) {
160 return nullptr;
161 }
162 xmlNode* family = firstElement(familySet, FAMILY_TAG);
163 if (family == nullptr) {
164 return nullptr;
165 }
166
167 xmlNode* font = firstElement(family, FONT_TAG);
168 while (font == nullptr) {
169 family = nextSibling(family, FAMILY_TAG);
170 if (family == nullptr) {
171 return nullptr;
172 }
173 font = firstElement(family, FONT_TAG);
174 }
175 return font;
176}
177
178} // namespace
179
180ASystemFontIterator* ASystemFontIterator_open() {
181 std::unique_ptr<ASystemFontIterator> ite(new ASystemFontIterator());
182 ite->mXmlDoc.reset(xmlReadFile("/system/etc/fonts.xml", nullptr, 0));
183 return ite.release();
184}
185
186void ASystemFontIterator_close(ASystemFontIterator* ite) {
187 delete ite;
188}
189
190ASystemFont* ASystemFontIterator_next(ASystemFontIterator* ite) {
191 LOG_ALWAYS_FATAL_IF(ite == nullptr, "nullptr has passed as iterator argument");
192 if (ite->mFontNode == nullptr) {
193 if (ite->mXmlDoc == nullptr) {
194 return nullptr; // Already at the end.
195 } else {
196 // First time to query font.
197 ite->mFontNode = findFirstFontNode(ite->mXmlDoc.get());
198 if (ite->mFontNode == nullptr) {
199 ite->mXmlDoc.reset();
200 return nullptr; // No font node found.
201 }
202 std::unique_ptr<ASystemFont> font = std::make_unique<ASystemFont>();
203 copyFont(ite, font.get());
204 return font.release();
205 }
206 } else {
207 xmlNode* nextNode = nextSibling(ite->mFontNode, FONT_TAG);
208 while (nextNode == nullptr) {
209 xmlNode* family = nextSibling(ite->mFontNode->parent, FAMILY_TAG);
210 if (family == nullptr) {
211 break;
212 }
213 nextNode = firstElement(family, FONT_TAG);
214 }
215 ite->mFontNode = nextNode;
216 if (nextNode == nullptr) {
217 ite->mXmlDoc.reset();
218 return nullptr;
219 }
220
221 std::unique_ptr<ASystemFont> font = std::make_unique<ASystemFont>();
222 copyFont(ite, font.get());
223 if (!isFontFileAvailable(font->mFilePath)) {
224 // fonts.xml intentionally contains missing font configuration. Skip it.
225 return ASystemFontIterator_next(ite);
226 }
227 return font.release();
228 }
229}
230
231void ASystemFont_close(ASystemFont* font) {
232 delete font;
233}
234
235const char* ASystemFont_getFontFilePath(const ASystemFont* font) {
236 LOG_ALWAYS_FATAL_IF(font == nullptr, "nullptr has passed as font argument");
237 return font->mFilePath.c_str();
238}
239
240uint16_t ASystemFont_getWeight(const ASystemFont* font) {
241 LOG_ALWAYS_FATAL_IF(font == nullptr, "nullptr has passed as font argument");
242 return font->mWeight;
243}
244
245bool ASystemFont_isItalic(const ASystemFont* font) {
246 LOG_ALWAYS_FATAL_IF(font == nullptr, "nullptr has passed as font argument");
247 return font->mItalic;
248}
249
250const char* ASystemFont_getLocale(const ASystemFont* font) {
251 LOG_ALWAYS_FATAL_IF(font == nullptr, "nullptr has passed to font argument");
252 return font->mLocale ? nullptr : font->mLocale->c_str();
253}
254
255size_t ASystemFont_getCollectionIndex(const ASystemFont* font) {
256 LOG_ALWAYS_FATAL_IF(font == nullptr, "nullptr has passed to font argument");
257 return font->mCollectionIndex;
258}
259
260size_t ASystemFont_getAxisCount(const ASystemFont* font) {
261 LOG_ALWAYS_FATAL_IF(font == nullptr, "nullptr has passed to font argument");
262 return font->mAxes.size();
263}
264
265uint32_t ASystemFont_getAxisTag(const ASystemFont* font, uint32_t axisIndex) {
266 LOG_ALWAYS_FATAL_IF(font == nullptr, "nullptr has passed to font argument");
267 LOG_ALWAYS_FATAL_IF(axisIndex >= font->mAxes.size(),
268 "given axis index is out of bounds. (< %zd", font->mAxes.size());
269 return font->mAxes[axisIndex].first;
270}
271
272float ASystemFont_getAxisValue(const ASystemFont* font, uint32_t axisIndex) {
273 LOG_ALWAYS_FATAL_IF(font == nullptr, "nullptr has passed to font argument");
274 LOG_ALWAYS_FATAL_IF(axisIndex >= font->mAxes.size(),
275 "given axis index is out of bounds. (< %zd", font->mAxes.size());
276 return font->mAxes[axisIndex].second;
277}