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 */ 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 47 static unsigned FileIdFromUniqueId(unsigned uniqueid) { 48 return uniqueid >> 8; 49 } 50 51 static SkTypeface::Style StyleFromUniqueId(unsigned uniqueid) { 52 return static_cast<SkTypeface::Style>(uniqueid & 0xff); 53 } 54 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: 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 */ 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 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 196 SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) { 197 SkDEBUGFAIL("SkFontHost::CreateTypefaceFromStream unimplemented"); 198 return NULL; 199 } 200 201 // static 202 SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) { 203 SkDEBUGFAIL("SkFontHost::CreateTypefaceFromFile unimplemented"); 204 return NULL; 205 } 206 207 // static 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 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 239 void SkFontHost::Serialize(const SkTypeface*, SkWStream*) { 240 SkDEBUGFAIL("SkFontHost::Serialize unimplemented"); 241 } 242 243 SkTypeface* SkFontHost::Deserialize(SkStream* stream) { 244 SkDEBUGFAIL("SkFontHost::Deserialize unimplemented"); 245 return NULL; 246 } 247 248 SkFontID SkFontHost::NextLogicalFont(SkFontID currFontID, SkFontID origFontID) { 249 // We don't handle font fallback, WebKit does. 250 return 0; 251 } 252