1 /*
2 * Copyright 2008 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 #include <map>
9 #include <string>
10
11 #include <fontconfig/fontconfig.h>
12
13 #include "SkFontHost.h"
14 #include "SkStream.h"
15
16 /** An extern from SkFontHost_FreeType. */
17 SkTypeface::Style find_name_and_style(SkStream* stream, SkString* name);
18
19 /** This lock must be held while modifying global_fc_* globals. */
20 SK_DECLARE_STATIC_MUTEX(global_fc_map_lock);
21
22 /** Map from file names to file ids. */
23 static std::map<std::string, unsigned> global_fc_map;
24 /** Map from file ids to file names. */
25 static std::map<unsigned, std::string> global_fc_map_inverted;
26 /** The next file id. */
27 static unsigned global_fc_map_next_id = 0;
28
29 /**
30 * Check to see if the filename has already been assigned a fileid and, if so, use it.
31 * Otherwise, assign one. Return the resulting fileid.
32 */
FileIdFromFilename(const char * filename)33 static unsigned FileIdFromFilename(const char* filename) {
34 SkAutoMutexAcquire ac(global_fc_map_lock);
35
36 std::map<std::string, unsigned>::const_iterator i = global_fc_map.find(filename);
37 if (i == global_fc_map.end()) {
38 const unsigned fileid = global_fc_map_next_id++;
39 global_fc_map[filename] = fileid;
40 global_fc_map_inverted[fileid] = filename;
41 return fileid;
42 } else {
43 return i->second;
44 }
45 }
46
FileIdFromUniqueId(unsigned uniqueid)47 static unsigned FileIdFromUniqueId(unsigned uniqueid) {
48 return uniqueid >> 8;
49 }
50
StyleFromUniqueId(unsigned uniqueid)51 static SkTypeface::Style StyleFromUniqueId(unsigned uniqueid) {
52 return static_cast<SkTypeface::Style>(uniqueid & 0xff);
53 }
54
UniqueIdFromFileIdAndStyle(unsigned fileid,SkTypeface::Style style)55 static unsigned UniqueIdFromFileIdAndStyle(unsigned fileid, SkTypeface::Style style) {
56 SkASSERT((style & 0xff) == style);
57 return (fileid << 8) | static_cast<int>(style);
58 }
59
60 class FontConfigTypeface : public SkTypeface {
61 public:
FontConfigTypeface(Style style,uint32_t id)62 FontConfigTypeface(Style style, uint32_t id) : SkTypeface(style, id) { }
63 };
64
65 /**
66 * Find a matching font where @type (one of FC_*) is equal to @value. For a
67 * list of types, see http://fontconfig.org/fontconfig-devel/x19.html#AEN27.
68 * The variable arguments are a list of triples, just like the first three
69 * arguments, and must be NULL terminated.
70 *
71 * For example,
72 * FontMatchString(FC_FILE, FcTypeString, "/usr/share/fonts/myfont.ttf", NULL);
73 */
FontMatch(const char * type,FcType vtype,const void * value,...)74 static FcPattern* FontMatch(const char* type, FcType vtype, const void* value, ...) {
75 va_list ap;
76 va_start(ap, value);
77
78 FcPattern* pattern = FcPatternCreate();
79
80 for (;;) {
81 FcValue fcvalue;
82 fcvalue.type = vtype;
83 switch (vtype) {
84 case FcTypeString:
85 fcvalue.u.s = (FcChar8*) value;
86 break;
87 case FcTypeInteger:
88 fcvalue.u.i = (int)(intptr_t)value;
89 break;
90 default:
91 SkDEBUGFAIL("FontMatch unhandled type");
92 }
93 FcPatternAdd(pattern, type, fcvalue, FcFalse);
94
95 type = va_arg(ap, const char *);
96 if (!type)
97 break;
98 // FcType is promoted to int when passed through ...
99 vtype = static_cast<FcType>(va_arg(ap, int));
100 value = va_arg(ap, const void *);
101 };
102 va_end(ap);
103
104 FcConfigSubstitute(NULL, pattern, FcMatchPattern);
105 FcDefaultSubstitute(pattern);
106
107 FcResult result;
108 FcPattern* match = FcFontMatch(NULL, pattern, &result);
109 FcPatternDestroy(pattern);
110
111 return match;
112 }
113
114 // static
CreateTypeface(const SkTypeface * familyFace,const char familyName[],SkTypeface::Style style)115 SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace,
116 const char familyName[],
117 SkTypeface::Style style)
118 {
119 const char* resolved_family_name = NULL;
120 FcPattern* face_match = NULL;
121
122 {
123 SkAutoMutexAcquire ac(global_fc_map_lock);
124 if (FcTrue != FcInit()) {
125 SkASSERT(false && "Could not initialize fontconfig.");
126 }
127 }
128
129 if (familyFace) {
130 // Here we use the inverted global id map to find the filename from the
131 // SkTypeface object. Given the filename we can ask fontconfig for the
132 // familyname of the font.
133 SkAutoMutexAcquire ac(global_fc_map_lock);
134
135 const unsigned fileid = FileIdFromUniqueId(familyFace->uniqueID());
136 std::map<unsigned, std::string>::const_iterator i = global_fc_map_inverted.find(fileid);
137 if (i == global_fc_map_inverted.end()) {
138 return NULL;
139 }
140
141 face_match = FontMatch(FC_FILE, FcTypeString, i->second.c_str(), NULL);
142 if (!face_match) {
143 return NULL;
144 }
145
146 FcChar8* family;
147 if (FcPatternGetString(face_match, FC_FAMILY, 0, &family)) {
148 FcPatternDestroy(face_match);
149 return NULL;
150 }
151 // At this point, @family is pointing into the @face_match object so we
152 // cannot release it yet.
153
154 resolved_family_name = reinterpret_cast<char*>(family);
155 } else if (familyName) {
156 resolved_family_name = familyName;
157 }
158
159 const int bold = (style & SkTypeface::kBold) ? FC_WEIGHT_BOLD : FC_WEIGHT_NORMAL;
160 const int italic = (style & SkTypeface::kItalic) ? FC_SLANT_ITALIC : FC_SLANT_ROMAN;
161
162 FcPattern* match;
163 if (resolved_family_name) {
164 match = FontMatch(FC_FAMILY, FcTypeString, resolved_family_name,
165 FC_WEIGHT, FcTypeInteger, bold,
166 FC_SLANT, FcTypeInteger, italic,
167 NULL);
168 } else {
169 match = FontMatch(FC_WEIGHT, FcTypeInteger, reinterpret_cast<void*>(bold),
170 FC_SLANT, FcTypeInteger, italic,
171 NULL);
172 }
173
174 if (face_match)
175 FcPatternDestroy(face_match);
176
177 if (!match)
178 return NULL;
179
180 FcChar8* filename;
181 if (FcPatternGetString(match, FC_FILE, 0, &filename) != FcResultMatch) {
182 FcPatternDestroy(match);
183 return NULL;
184 }
185 // Now @filename is pointing into @match
186
187 const unsigned fileid = FileIdFromFilename(reinterpret_cast<char*>(filename));
188 const unsigned id = UniqueIdFromFileIdAndStyle(fileid, style);
189 SkTypeface* typeface = SkNEW_ARGS(FontConfigTypeface, (style, id));
190 FcPatternDestroy(match);
191
192 return typeface;
193 }
194
195 // static
CreateTypefaceFromStream(SkStream * stream)196 SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) {
197 SkDEBUGFAIL("SkFontHost::CreateTypefaceFromStream unimplemented");
198 return NULL;
199 }
200
201 // static
CreateTypefaceFromFile(const char path[])202 SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) {
203 SkDEBUGFAIL("SkFontHost::CreateTypefaceFromFile unimplemented");
204 return NULL;
205 }
206
207 // static
OpenStream(uint32_t id)208 SkStream* SkFontHost::OpenStream(uint32_t id) {
209 SkAutoMutexAcquire ac(global_fc_map_lock);
210 const unsigned fileid = FileIdFromUniqueId(id);
211
212 std::map<unsigned, std::string>::const_iterator i = global_fc_map_inverted.find(fileid);
213 if (i == global_fc_map_inverted.end()) {
214 return NULL;
215 }
216
217 return SkNEW_ARGS(SkFILEStream, (i->second.c_str()));
218 }
219
GetFileName(SkFontID fontID,char path[],size_t length,int32_t * index)220 size_t SkFontHost::GetFileName(SkFontID fontID, char path[], size_t length, int32_t* index) {
221 SkAutoMutexAcquire ac(global_fc_map_lock);
222 const unsigned fileid = FileIdFromUniqueId(fontID);
223
224 std::map<unsigned, std::string>::const_iterator i = global_fc_map_inverted.find(fileid);
225 if (i == global_fc_map_inverted.end()) {
226 return 0;
227 }
228
229 const std::string& str = i->second;
230 if (path) {
231 memcpy(path, str.c_str(), SkMin32(str.size(), length));
232 }
233 if (index) { // TODO: check if we're in a TTC
234 *index = 0;
235 }
236 return str.size();
237 }
238
Serialize(const SkTypeface *,SkWStream *)239 void SkFontHost::Serialize(const SkTypeface*, SkWStream*) {
240 SkDEBUGFAIL("SkFontHost::Serialize unimplemented");
241 }
242
Deserialize(SkStream * stream)243 SkTypeface* SkFontHost::Deserialize(SkStream* stream) {
244 SkDEBUGFAIL("SkFontHost::Deserialize unimplemented");
245 return NULL;
246 }
247
NextLogicalFont(SkFontID currFontID,SkFontID origFontID)248 SkFontID SkFontHost::NextLogicalFont(SkFontID currFontID, SkFontID origFontID) {
249 // We don't handle font fallback, WebKit does.
250 return 0;
251 }
252