• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2014 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 // Running create_test_font generates ./tools/fonts/test_font_index.inc
9 // and ./tools/fonts/test_font_<generic name>.inc which are read by
10 // ./tools/fonts/sk_tool_utils_font.cpp
11 
12 #include "SkFontStyle.h"
13 #include "SkOSFile.h"
14 #include "SkOSPath.h"
15 #include "SkPaint.h"
16 #include "SkPath.h"
17 #include "SkStream.h"
18 #include "SkTArray.h"
19 #include "SkTSort.h"
20 #include "SkTypeface.h"
21 #include "SkUtils.h"
22 #include <stdio.h>
23 
24 #define DEFAULT_FONT_NAME "sans-serif"
25 
26 namespace {
27 
28 struct NamedFontStyle {
29     const char* fName;
30     SkFontStyle fStyle;
31 };
32 constexpr NamedFontStyle normal     = {"Normal",     SkFontStyle::Normal()    };
33 constexpr NamedFontStyle bold       = {"Bold",       SkFontStyle::Bold()      };
34 constexpr NamedFontStyle italic     = {"Italic",     SkFontStyle::Italic()    };
35 constexpr NamedFontStyle bolditalic = {"BoldItalic", SkFontStyle::BoldItalic()};
36 
37 struct FontDesc {
38     char const * const fGenericName;
39     NamedFontStyle const fNamedStyle;
40     char const * const fFontName;
41     char const * const fFile;
42     // fFontIndex is mutable and will be set later.
43     int fFontIndex;
44 } gFonts[] = {
45     {"monospace",  normal,     "Liberation Mono",  "LiberationMono-Regular.ttf",     -1},
46     {"monospace",  bold,       "Liberation Mono",  "LiberationMono-Bold.ttf",        -1},
47     {"monospace",  italic,     "Liberation Mono",  "LiberationMono-Italic.ttf",      -1},
48     {"monospace",  bolditalic, "Liberation Mono",  "LiberationMono-BoldItalic.ttf",  -1},
49     {"sans-serif", normal,     "Liberation Sans",  "LiberationSans-Regular.ttf",     -1},
50     {"sans-serif", bold,       "Liberation Sans",  "LiberationSans-Bold.ttf",        -1},
51     {"sans-serif", italic,     "Liberation Sans",  "LiberationSans-Italic.ttf",      -1},
52     {"sans-serif", bolditalic, "Liberation Sans",  "LiberationSans-BoldItalic.ttf",  -1},
53     {"serif",      normal,     "Liberation Serif", "LiberationSerif-Regular.ttf",    -1},
54     {"serif",      bold,       "Liberation Serif", "LiberationSerif-Bold.ttf",       -1},
55     {"serif",      italic,     "Liberation Serif", "LiberationSerif-Italic.ttf",     -1},
56     {"serif",      bolditalic, "Liberation Serif", "LiberationSerif-BoldItalic.ttf", -1},
57 };
58 
59 const int gFontsCount = (int) SK_ARRAY_COUNT(gFonts);
60 
61 const char gHeader[] =
62 "/*\n"
63 " * Copyright 2015 Google Inc.\n"
64 " *\n"
65 " * Use of this source code is governed by a BSD-style license that can be\n"
66 " * found in the LICENSE file.\n"
67 " */\n"
68 "\n"
69 "// Auto-generated by ";
70 
71 } // namespace
72 
font_header(const char * family)73 static FILE* font_header(const char* family) {
74     SkString outPath(SkOSPath::Join(".", "tools"));
75     outPath = SkOSPath::Join(outPath.c_str(), "fonts");
76     outPath = SkOSPath::Join(outPath.c_str(), "test_font_");
77     SkString fam(family);
78     do {
79         int dashIndex = fam.find("-");
80         if (dashIndex < 0) {
81             break;
82         }
83         fam.writable_str()[dashIndex] = '_';
84     } while (true);
85     outPath.append(fam);
86     outPath.append(".inc");
87     FILE* out = fopen(outPath.c_str(), "w");
88     fprintf(out, "%s%s\n\n", gHeader, SkOSPath::Basename(__FILE__).c_str());
89     return out;
90 }
91 
92 enum {
93     kMaxLineLength = 80,
94 };
95 
last_line_length(const SkString & str)96 static ptrdiff_t last_line_length(const SkString& str) {
97     const char* first = str.c_str();
98     const char* last = first + str.size();
99     const char* ptr = last;
100     while (ptr > first && *--ptr != '\n')
101         ;
102     return last - ptr - 1;
103 }
104 
output_fixed(SkScalar num,int emSize,SkString * out)105 static void output_fixed(SkScalar num, int emSize, SkString* out) {
106     int hex = (int) (num * 65536 / emSize);
107     out->appendf("0x%08x,", hex);
108     *out += (int) last_line_length(*out) >= kMaxLineLength ? '\n' : ' ';
109 }
110 
output_scalar(SkScalar num,int emSize,SkString * out)111 static void output_scalar(SkScalar num, int emSize, SkString* out) {
112     num /= emSize;
113     if (num == (int) num) {
114        out->appendS32((int) num);
115     } else {
116         SkString str;
117         str.printf("%1.6g", num);
118         int width = (int) str.size();
119         const char* cStr = str.c_str();
120         while (cStr[width - 1] == '0') {
121             --width;
122         }
123         str.remove(width, str.size() - width);
124         out->appendf("%sf", str.c_str());
125     }
126     *out += ',';
127     *out += (int) last_line_length(*out) >= kMaxLineLength ? '\n' : ' ';
128 }
129 
output_points(const SkPoint * pts,int emSize,int count,SkString * ptsOut)130 static int output_points(const SkPoint* pts, int emSize, int count, SkString* ptsOut) {
131     for (int index = 0; index < count; ++index) {
132 //        SkASSERT(floor(pts[index].fX) == pts[index].fX);
133         output_scalar(pts[index].fX, emSize, ptsOut);
134 //        SkASSERT(floor(pts[index].fY) == pts[index].fY);
135         output_scalar(pts[index].fY, emSize, ptsOut);
136     }
137     return count;
138 }
139 
output_path_data(const SkPaint & paint,int emSize,SkString * ptsOut,SkTDArray<SkPath::Verb> * verbs,SkTDArray<unsigned> * charCodes,SkTDArray<SkScalar> * widths)140 static void output_path_data(const SkPaint& paint,
141         int emSize, SkString* ptsOut, SkTDArray<SkPath::Verb>* verbs,
142         SkTDArray<unsigned>* charCodes, SkTDArray<SkScalar>* widths) {
143     for (int ch = 0x00; ch < 0x7f; ++ch) {
144         char str[1];
145         str[0] = ch;
146         const char* used = str;
147         SkUnichar index = SkUTF8_NextUnichar(&used);
148         SkPath path;
149         paint.getTextPath((const void*) &index, 2, 0, 0, &path);
150         SkPath::RawIter iter(path);
151         SkPath::Verb verb;
152         SkPoint pts[4];
153         while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
154             *verbs->append() = verb;
155             switch (verb) {
156                 case SkPath::kMove_Verb:
157                     output_points(&pts[0], emSize, 1, ptsOut);
158                     break;
159                 case SkPath::kLine_Verb:
160                     output_points(&pts[1], emSize, 1, ptsOut);
161                     break;
162                 case SkPath::kQuad_Verb:
163                     output_points(&pts[1], emSize, 2, ptsOut);
164                     break;
165                 case SkPath::kCubic_Verb:
166                     output_points(&pts[1], emSize, 3, ptsOut);
167                     break;
168                 case SkPath::kClose_Verb:
169                     break;
170                 default:
171                     SkDEBUGFAIL("bad verb");
172                     SkASSERT(0);
173             }
174         }
175         *verbs->append() = SkPath::kDone_Verb;
176         *charCodes->append() = index;
177         SkScalar width;
178         SkDEBUGCODE(int charCount =) paint.getTextWidths((const void*) &index, 2, &width);
179         SkASSERT(charCount == 1);
180      // SkASSERT(floor(width) == width);  // not true for Hiragino Maru Gothic Pro
181         *widths->append() = width;
182         if (!ch) {
183             ch = 0x1f;  // skip the rest of the control codes
184         }
185     }
186 }
187 
offset_str_len(unsigned num)188 static int offset_str_len(unsigned num) {
189     if (num == (unsigned) -1) {
190         return 10;
191     }
192     unsigned result = 1;
193     unsigned ref = 10;
194     while (ref <= num) {
195         ++result;
196         ref *= 10;
197     }
198     return result;
199 }
200 
strip_spaces(const SkString & str)201 static SkString strip_spaces(const SkString& str) {
202     SkString result;
203     int count = (int) str.size();
204     for (int index = 0; index < count; ++index) {
205         char c = str[index];
206         if (c != ' ' && c != '-') {
207             result += c;
208         }
209     }
210     return result;
211 }
212 
strip_final(const SkString & str)213 static SkString strip_final(const SkString& str) {
214     SkString result(str);
215     if (result.endsWith("\n")) {
216         result.remove(result.size() - 1, 1);
217     }
218     if (result.endsWith(" ")) {
219         result.remove(result.size() - 1, 1);
220     }
221     if (result.endsWith(",")) {
222         result.remove(result.size() - 1, 1);
223     }
224     return result;
225 }
226 
output_font(sk_sp<SkTypeface> face,const char * name,NamedFontStyle style,FILE * out)227 static void output_font(sk_sp<SkTypeface> face, const char* name, NamedFontStyle style, FILE* out) {
228     int emSize = face->getUnitsPerEm() * 2;
229     SkPaint paint;
230     paint.setAntiAlias(true);
231     paint.setTextAlign(SkPaint::kLeft_Align);
232     paint.setTextEncoding(SkPaint::kUTF16_TextEncoding);
233     paint.setTextSize(emSize);
234     paint.setTypeface(std::move(face));
235     SkTDArray<SkPath::Verb> verbs;
236     SkTDArray<unsigned> charCodes;
237     SkTDArray<SkScalar> widths;
238     SkString ptsOut;
239     output_path_data(paint, emSize, &ptsOut, &verbs, &charCodes, &widths);
240     SkString fontnameStr(name);
241     SkString strippedStr = strip_spaces(fontnameStr);
242     strippedStr.appendf("%s", style.fName);
243     const char* fontname = strippedStr.c_str();
244     fprintf(out, "const SkScalar %sPoints[] = {\n", fontname);
245     ptsOut = strip_final(ptsOut);
246     fprintf(out, "%s", ptsOut.c_str());
247     fprintf(out, "\n};\n\n");
248     fprintf(out, "const unsigned char %sVerbs[] = {\n", fontname);
249     int verbCount = verbs.count();
250     int outChCount = 0;
251     for (int index = 0; index < verbCount;) {
252         SkPath::Verb verb = verbs[index];
253         SkASSERT(verb >= SkPath::kMove_Verb && verb <= SkPath::kDone_Verb);
254         SkASSERT((unsigned) verb == (unsigned char) verb);
255         fprintf(out, "%u", verb);
256         if (++index < verbCount) {
257             outChCount += 3;
258             fprintf(out, "%c", ',');
259             if (outChCount >= kMaxLineLength) {
260                 outChCount = 0;
261                 fprintf(out, "%c", '\n');
262             } else {
263                 fprintf(out, "%c", ' ');
264             }
265         }
266     }
267     fprintf(out, "\n};\n\n");
268 
269     // all fonts are now 0x00, 0x20 - 0xFE
270     // don't need to generate or output character codes?
271     fprintf(out, "const unsigned %sCharCodes[] = {\n", fontname);
272     int offsetCount = charCodes.count();
273     for (int index = 0; index < offsetCount;) {
274         unsigned offset = charCodes[index];
275         fprintf(out, "%u", offset);
276         if (++index < offsetCount) {
277             outChCount += offset_str_len(offset) + 2;
278             fprintf(out, "%c", ',');
279             if (outChCount >= kMaxLineLength) {
280                 outChCount = 0;
281                 fprintf(out, "%c", '\n');
282             } else {
283                 fprintf(out, "%c", ' ');
284             }
285         }
286     }
287     fprintf(out, "\n};\n\n");
288 
289     SkString widthsStr;
290     fprintf(out, "const SkFixed %sWidths[] = {\n", fontname);
291     for (int index = 0; index < offsetCount; ++index) {
292         output_fixed(widths[index], emSize, &widthsStr);
293     }
294     widthsStr = strip_final(widthsStr);
295     fprintf(out, "%s\n};\n\n", widthsStr.c_str());
296 
297     fprintf(out, "const int %sCharCodesCount = (int) SK_ARRAY_COUNT(%sCharCodes);\n\n",
298             fontname, fontname);
299 
300     SkPaint::FontMetrics metrics;
301     paint.getFontMetrics(&metrics);
302     fprintf(out, "const SkPaint::FontMetrics %sMetrics = {\n", fontname);
303     SkString metricsStr;
304     metricsStr.printf("0x%08x, ", metrics.fFlags);
305     output_scalar(metrics.fTop, emSize, &metricsStr);
306     output_scalar(metrics.fAscent, emSize, &metricsStr);
307     output_scalar(metrics.fDescent, emSize, &metricsStr);
308     output_scalar(metrics.fBottom, emSize, &metricsStr);
309     output_scalar(metrics.fLeading, emSize, &metricsStr);
310     output_scalar(metrics.fAvgCharWidth, emSize, &metricsStr);
311     output_scalar(metrics.fMaxCharWidth, emSize, &metricsStr);
312     output_scalar(metrics.fXMin, emSize, &metricsStr);
313     output_scalar(metrics.fXMax, emSize, &metricsStr);
314     output_scalar(metrics.fXHeight, emSize, &metricsStr);
315     output_scalar(metrics.fCapHeight, emSize, &metricsStr);
316     output_scalar(metrics.fUnderlineThickness, emSize, &metricsStr);
317     output_scalar(metrics.fUnderlinePosition, emSize, &metricsStr);
318     output_scalar(metrics.fStrikeoutThickness, emSize, &metricsStr);
319     output_scalar(metrics.fStrikeoutPosition, emSize, &metricsStr);
320     metricsStr = strip_final(metricsStr);
321     fprintf(out, "%s\n};\n\n", metricsStr.c_str());
322 }
323 
324 struct FontWritten {
325     const char* fFontName;
326     NamedFontStyle fNamedStyle;
327 };
328 
329 static SkTDArray<FontWritten> gWritten;
330 
written_index(const FontDesc & fontDesc)331 static int written_index(const FontDesc& fontDesc) {
332     for (int index = 0; index < gWritten.count(); ++index) {
333         const FontWritten& writ = gWritten[index];
334         if (!strcmp(fontDesc.fFontName, writ.fFontName) &&
335             fontDesc.fNamedStyle.fStyle == writ.fNamedStyle.fStyle)
336         {
337             return index;
338         }
339     }
340     return -1;
341 }
342 
generate_fonts(const char * basepath)343 static void generate_fonts(const char* basepath) {
344     FILE* out = nullptr;
345     for (int index = 0; index < gFontsCount; ++index) {
346         FontDesc& fontDesc = gFonts[index];
347         if ((index & 3) == 0) {
348             out = font_header(fontDesc.fGenericName);
349         }
350         int fontIndex = written_index(fontDesc);
351         if (fontIndex >= 0) {
352             fontDesc.fFontIndex = fontIndex;
353             continue;
354         }
355         SkString filepath(SkOSPath::Join(basepath, fontDesc.fFile));
356         SkASSERTF(sk_exists(filepath.c_str()), "The file %s does not exist.", filepath.c_str());
357         sk_sp<SkTypeface> resourceTypeface = SkTypeface::MakeFromFile(filepath.c_str());
358         SkASSERTF(resourceTypeface, "The file %s is not a font.", filepath.c_str());
359         output_font(std::move(resourceTypeface), fontDesc.fFontName, fontDesc.fNamedStyle, out);
360         fontDesc.fFontIndex = gWritten.count();
361         FontWritten* writ = gWritten.append();
362         writ->fFontName = fontDesc.fFontName;
363         writ->fNamedStyle = fontDesc.fNamedStyle;
364         if ((index & 3) == 3) {
365             fclose(out);
366         }
367     }
368 }
369 
slant_to_string(SkFontStyle::Slant slant)370 static const char* slant_to_string(SkFontStyle::Slant slant) {
371     switch (slant) {
372         case SkFontStyle::kUpright_Slant: return "SkFontStyle::kUpright_Slant";
373         case SkFontStyle::kItalic_Slant : return "SkFontStyle::kItalic_Slant" ;
374         case SkFontStyle::kOblique_Slant: return "SkFontStyle::kOblique_Slant";
375         default: SK_ABORT("Unknown slant"); return "";
376     }
377 }
378 
generate_index(const char * defaultName)379 static void generate_index(const char* defaultName) {
380     FILE* out = font_header("index");
381     fprintf(out, "static SkTestFontData gTestFonts[] = {\n");
382     for (const FontWritten& writ : gWritten) {
383         const char* name = writ.fFontName;
384         SkString strippedStr = strip_spaces(SkString(name));
385         strippedStr.appendf("%s", writ.fNamedStyle.fName);
386         const char* strip = strippedStr.c_str();
387         fprintf(out,
388                 "    {    %sPoints, %sVerbs, %sCharCodes,\n"
389                 "         %sCharCodesCount, %sWidths,\n"
390                 "         %sMetrics, \"Toy %s\", SkFontStyle(%d,%d,%s), nullptr\n"
391                 "    },\n",
392                 strip, strip, strip, strip, strip, strip, name,
393                 writ.fNamedStyle.fStyle.weight(), writ.fNamedStyle.fStyle.width(),
394                 slant_to_string(writ.fNamedStyle.fStyle.slant()));
395     }
396     fprintf(out, "};\n\n");
397     fprintf(out, "const int gTestFontsCount = (int) SK_ARRAY_COUNT(gTestFonts);\n\n");
398     fprintf(out,
399                 "struct SubFont {\n"
400                 "    const char* fName;\n"
401                 "    SkFontStyle fStyle;\n"
402                 "    SkTestFontData& fFont;\n"
403                 "    const char* fFile;\n"
404                 "};\n\n"
405                 "const SubFont gSubFonts[] = {\n");
406     int defaultIndex = -1;
407     for (int subIndex = 0; subIndex < gFontsCount; subIndex++) {
408         const FontDesc& desc = gFonts[subIndex];
409         if (defaultIndex < 0 && !strcmp(defaultName, desc.fGenericName)) {
410             defaultIndex = subIndex;
411         }
412         fprintf(out,
413                 "    { \"%s\", SkFontStyle(%d,%d,%s), gTestFonts[%d], \"%s\" },\n",
414                 desc.fGenericName,
415                 desc.fNamedStyle.fStyle.weight(), desc.fNamedStyle.fStyle.width(),
416                 slant_to_string(desc.fNamedStyle.fStyle.slant()), desc.fFontIndex, desc.fFile);
417     }
418     for (int subIndex = 0; subIndex < gFontsCount; subIndex++) {
419         const FontDesc& desc = gFonts[subIndex];
420         fprintf(out,
421                 "    { \"Toy %s\", SkFontStyle(%d,%d,%s), gTestFonts[%d], \"%s\" },\n",
422                 desc.fFontName,
423                 desc.fNamedStyle.fStyle.weight(), desc.fNamedStyle.fStyle.width(),
424                 slant_to_string(desc.fNamedStyle.fStyle.slant()), desc.fFontIndex, desc.fFile);
425     }
426     fprintf(out, "};\n\n");
427     fprintf(out, "const int gSubFontsCount = (int) SK_ARRAY_COUNT(gSubFonts);\n\n");
428     SkASSERT(defaultIndex >= 0);
429     fprintf(out, "const int gDefaultFontIndex = %d;\n", defaultIndex);
430     fclose(out);
431 }
432 
main(int,char * const[])433 int main(int , char * const []) {
434     generate_fonts("/Library/Fonts/"); // or /usr/share/fonts/truetype/ttf-liberation/
435     generate_index(DEFAULT_FONT_NAME);
436     return 0;
437 }
438