1 /*
2 * Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
3 * (C) 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org>
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 #include "config.h"
28 #include "CSSFontSelector.h"
29
30 #include "AtomicString.h"
31 #include "CachedFont.h"
32 #include "CSSFontFace.h"
33 #include "CSSFontFaceRule.h"
34 #include "CSSFontFaceSource.h"
35 #include "CSSFontFaceSrcValue.h"
36 #include "CSSMutableStyleDeclaration.h"
37 #include "CSSPrimitiveValue.h"
38 #include "CSSPropertyNames.h"
39 #include "CSSSegmentedFontFace.h"
40 #include "CSSUnicodeRangeValue.h"
41 #include "CSSValueKeywords.h"
42 #include "CSSValueList.h"
43 #include "DocLoader.h"
44 #include "Document.h"
45 #include "FontCache.h"
46 #include "FontFamilyValue.h"
47 #include "Frame.h"
48 #include "NodeList.h"
49 #include "RenderObject.h"
50 #include "Settings.h"
51 #include "SimpleFontData.h"
52
53 #if ENABLE(SVG)
54 #include "SVGFontFaceElement.h"
55 #include "SVGNames.h"
56 #endif
57
58 namespace WebCore {
59
CSSFontSelector(Document * document)60 CSSFontSelector::CSSFontSelector(Document* document)
61 : m_document(document)
62 {
63 // FIXME: An old comment used to say there was no need to hold a reference to m_document
64 // because "we are guaranteed to be destroyed before the document". But there does not
65 // seem to be any such guarantee.
66
67 ASSERT(m_document);
68 fontCache()->addClient(this);
69 }
70
~CSSFontSelector()71 CSSFontSelector::~CSSFontSelector()
72 {
73 fontCache()->removeClient(this);
74 deleteAllValues(m_fontFaces);
75 deleteAllValues(m_locallyInstalledFontFaces);
76 deleteAllValues(m_fonts);
77 }
78
isEmpty() const79 bool CSSFontSelector::isEmpty() const
80 {
81 return m_fonts.isEmpty();
82 }
83
docLoader() const84 DocLoader* CSSFontSelector::docLoader() const
85 {
86 return m_document ? m_document->docLoader() : 0;
87 }
88
addFontFaceRule(const CSSFontFaceRule * fontFaceRule)89 void CSSFontSelector::addFontFaceRule(const CSSFontFaceRule* fontFaceRule)
90 {
91 // Obtain the font-family property and the src property. Both must be defined.
92 const CSSMutableStyleDeclaration* style = fontFaceRule->style();
93 RefPtr<CSSValue> fontFamily = style->getPropertyCSSValue(CSSPropertyFontFamily);
94 RefPtr<CSSValue> src = style->getPropertyCSSValue(CSSPropertySrc);
95 RefPtr<CSSValue> unicodeRange = style->getPropertyCSSValue(CSSPropertyUnicodeRange);
96 if (!fontFamily || !src || !fontFamily->isValueList() || !src->isValueList() || (unicodeRange && !unicodeRange->isValueList()))
97 return;
98
99 CSSValueList* familyList = static_cast<CSSValueList*>(fontFamily.get());
100 if (!familyList->length())
101 return;
102
103 CSSValueList* srcList = static_cast<CSSValueList*>(src.get());
104 if (!srcList->length())
105 return;
106
107 CSSValueList* rangeList = static_cast<CSSValueList*>(unicodeRange.get());
108
109 unsigned traitsMask = 0;
110
111 if (RefPtr<CSSValue> fontStyle = style->getPropertyCSSValue(CSSPropertyFontStyle)) {
112 if (fontStyle->isPrimitiveValue()) {
113 RefPtr<CSSValueList> list = CSSValueList::createCommaSeparated();
114 list->append(fontStyle);
115 fontStyle = list;
116 } else if (!fontStyle->isValueList())
117 return;
118
119 CSSValueList* styleList = static_cast<CSSValueList*>(fontStyle.get());
120 unsigned numStyles = styleList->length();
121 if (!numStyles)
122 return;
123
124 for (unsigned i = 0; i < numStyles; ++i) {
125 switch (static_cast<CSSPrimitiveValue*>(styleList->itemWithoutBoundsCheck(i))->getIdent()) {
126 case CSSValueAll:
127 traitsMask |= FontStyleMask;
128 break;
129 case CSSValueNormal:
130 traitsMask |= FontStyleNormalMask;
131 break;
132 case CSSValueItalic:
133 case CSSValueOblique:
134 traitsMask |= FontStyleItalicMask;
135 break;
136 default:
137 break;
138 }
139 }
140 } else
141 traitsMask |= FontStyleMask;
142
143 if (RefPtr<CSSValue> fontWeight = style->getPropertyCSSValue(CSSPropertyFontWeight)) {
144 if (fontWeight->isPrimitiveValue()) {
145 RefPtr<CSSValueList> list = CSSValueList::createCommaSeparated();
146 list->append(fontWeight);
147 fontWeight = list;
148 } else if (!fontWeight->isValueList())
149 return;
150
151 CSSValueList* weightList = static_cast<CSSValueList*>(fontWeight.get());
152 unsigned numWeights = weightList->length();
153 if (!numWeights)
154 return;
155
156 for (unsigned i = 0; i < numWeights; ++i) {
157 switch (static_cast<CSSPrimitiveValue*>(weightList->itemWithoutBoundsCheck(i))->getIdent()) {
158 case CSSValueAll:
159 traitsMask |= FontWeightMask;
160 break;
161 case CSSValueBolder:
162 case CSSValueBold:
163 case CSSValue700:
164 traitsMask |= FontWeight700Mask;
165 break;
166 case CSSValueNormal:
167 case CSSValue400:
168 traitsMask |= FontWeight400Mask;
169 break;
170 case CSSValue900:
171 traitsMask |= FontWeight900Mask;
172 break;
173 case CSSValue800:
174 traitsMask |= FontWeight800Mask;
175 break;
176 case CSSValue600:
177 traitsMask |= FontWeight600Mask;
178 break;
179 case CSSValue500:
180 traitsMask |= FontWeight500Mask;
181 break;
182 case CSSValue300:
183 traitsMask |= FontWeight300Mask;
184 break;
185 case CSSValueLighter:
186 case CSSValue200:
187 traitsMask |= FontWeight200Mask;
188 break;
189 case CSSValue100:
190 traitsMask |= FontWeight100Mask;
191 break;
192 default:
193 break;
194 }
195 }
196 } else
197 traitsMask |= FontWeightMask;
198
199 if (RefPtr<CSSValue> fontVariant = style->getPropertyCSSValue(CSSPropertyFontVariant)) {
200 if (fontVariant->isPrimitiveValue()) {
201 RefPtr<CSSValueList> list = CSSValueList::createCommaSeparated();
202 list->append(fontVariant);
203 fontVariant = list;
204 } else if (!fontVariant->isValueList())
205 return;
206
207 CSSValueList* variantList = static_cast<CSSValueList*>(fontVariant.get());
208 unsigned numVariants = variantList->length();
209 if (!numVariants)
210 return;
211
212 for (unsigned i = 0; i < numVariants; ++i) {
213 switch (static_cast<CSSPrimitiveValue*>(variantList->itemWithoutBoundsCheck(i))->getIdent()) {
214 case CSSValueAll:
215 traitsMask |= FontVariantMask;
216 break;
217 case CSSValueNormal:
218 traitsMask |= FontVariantNormalMask;
219 break;
220 case CSSValueSmallCaps:
221 traitsMask |= FontVariantSmallCapsMask;
222 break;
223 default:
224 break;
225 }
226 }
227 } else
228 traitsMask |= FontVariantNormalMask;
229
230 // Each item in the src property's list is a single CSSFontFaceSource. Put them all into a CSSFontFace.
231 RefPtr<CSSFontFace> fontFace;
232
233 int srcLength = srcList->length();
234
235 bool foundLocal = false;
236 bool foundSVGFont = false;
237
238 for (int i = 0; i < srcLength; i++) {
239 // An item in the list either specifies a string (local font name) or a URL (remote font to download).
240 CSSFontFaceSrcValue* item = static_cast<CSSFontFaceSrcValue*>(srcList->itemWithoutBoundsCheck(i));
241 CSSFontFaceSource* source = 0;
242
243 #if ENABLE(SVG_FONTS)
244 foundSVGFont = item->isSVGFontFaceSrc() || item->svgFontFaceElement();
245 #endif
246 if (!item->isLocal()) {
247 Settings* settings = m_document ? m_document->frame() ? m_document->frame()->settings() : 0 : 0;
248 bool allowDownloading = foundSVGFont || (settings && settings->downloadableBinaryFontsEnabled());
249 if (allowDownloading && item->isSupportedFormat() && m_document) {
250 CachedFont* cachedFont = m_document->docLoader()->requestFont(item->resource());
251 if (cachedFont) {
252 #if ENABLE(SVG_FONTS)
253 if (foundSVGFont)
254 cachedFont->setSVGFont(true);
255 #endif
256 source = new CSSFontFaceSource(item->resource(), cachedFont);
257 }
258 }
259 } else {
260 source = new CSSFontFaceSource(item->resource());
261 foundLocal = true;
262 }
263
264 if (!fontFace)
265 fontFace = CSSFontFace::create(static_cast<FontTraitsMask>(traitsMask));
266
267 if (source) {
268 #if ENABLE(SVG_FONTS)
269 source->setSVGFontFaceElement(item->svgFontFaceElement());
270 #endif
271 fontFace->addSource(source);
272 }
273 }
274
275 ASSERT(fontFace);
276
277 if (fontFace && !fontFace->isValid())
278 return;
279
280 if (rangeList) {
281 unsigned numRanges = rangeList->length();
282 for (unsigned i = 0; i < numRanges; i++) {
283 CSSUnicodeRangeValue* range = static_cast<CSSUnicodeRangeValue*>(rangeList->itemWithoutBoundsCheck(i));
284 fontFace->addRange(range->from(), range->to());
285 }
286 }
287
288 // Hash under every single family name.
289 int familyLength = familyList->length();
290 for (int i = 0; i < familyLength; i++) {
291 CSSPrimitiveValue* item = static_cast<CSSPrimitiveValue*>(familyList->itemWithoutBoundsCheck(i));
292 String familyName;
293 if (item->primitiveType() == CSSPrimitiveValue::CSS_STRING)
294 familyName = static_cast<FontFamilyValue*>(item)->familyName();
295 else if (item->primitiveType() == CSSPrimitiveValue::CSS_IDENT) {
296 // We need to use the raw text for all the generic family types, since @font-face is a way of actually
297 // defining what font to use for those types.
298 String familyName;
299 switch (item->getIdent()) {
300 case CSSValueSerif:
301 familyName = "-webkit-serif";
302 break;
303 case CSSValueSansSerif:
304 familyName = "-webkit-sans-serif";
305 break;
306 case CSSValueCursive:
307 familyName = "-webkit-cursive";
308 break;
309 case CSSValueFantasy:
310 familyName = "-webkit-fantasy";
311 break;
312 case CSSValueMonospace:
313 familyName = "-webkit-monospace";
314 break;
315 default:
316 break;
317 }
318 }
319
320 if (familyName.isEmpty())
321 continue;
322
323 #if ENABLE(SVG_FONTS)
324 // SVG allows several <font> elements with the same font-family, differing only
325 // in ie. font-variant. Be sure to pick up the right one - in getFontData below.
326 if (foundSVGFont && (traitsMask & FontVariantSmallCapsMask))
327 familyName += "-webkit-svg-small-caps";
328 #endif
329
330 Vector<RefPtr<CSSFontFace> >* familyFontFaces = m_fontFaces.get(familyName);
331 if (!familyFontFaces) {
332 familyFontFaces = new Vector<RefPtr<CSSFontFace> >;
333 m_fontFaces.set(familyName, familyFontFaces);
334
335 ASSERT(!m_locallyInstalledFontFaces.contains(familyName));
336 Vector<RefPtr<CSSFontFace> >* familyLocallyInstalledFaces;
337
338 Vector<unsigned> locallyInstalledFontsTraitsMasks;
339 fontCache()->getTraitsInFamily(familyName, locallyInstalledFontsTraitsMasks);
340 unsigned numLocallyInstalledFaces = locallyInstalledFontsTraitsMasks.size();
341 if (numLocallyInstalledFaces) {
342 familyLocallyInstalledFaces = new Vector<RefPtr<CSSFontFace> >;
343 m_locallyInstalledFontFaces.set(familyName, familyLocallyInstalledFaces);
344
345 for (unsigned i = 0; i < numLocallyInstalledFaces; ++i) {
346 RefPtr<CSSFontFace> locallyInstalledFontFace = CSSFontFace::create(static_cast<FontTraitsMask>(locallyInstalledFontsTraitsMasks[i]));
347 locallyInstalledFontFace->addSource(new CSSFontFaceSource(familyName));
348 ASSERT(locallyInstalledFontFace->isValid());
349 familyLocallyInstalledFaces->append(locallyInstalledFontFace);
350 }
351 }
352 }
353
354 familyFontFaces->append(fontFace);
355 }
356 }
357
fontLoaded()358 void CSSFontSelector::fontLoaded()
359 {
360 if (!m_document || m_document->inPageCache() || !m_document->renderer())
361 return;
362 m_document->recalcStyle(Document::Force);
363 m_document->renderer()->setNeedsLayoutAndPrefWidthsRecalc();
364 }
365
fontCacheInvalidated()366 void CSSFontSelector::fontCacheInvalidated()
367 {
368 if (!m_document || m_document->inPageCache() || !m_document->renderer())
369 return;
370 m_document->recalcStyle(Document::Force);
371 m_document->renderer()->setNeedsLayoutAndPrefWidthsRecalc();
372 }
373
fontDataForGenericFamily(Document * document,const FontDescription & fontDescription,const AtomicString & familyName)374 static FontData* fontDataForGenericFamily(Document* document, const FontDescription& fontDescription, const AtomicString& familyName)
375 {
376 if (!document || !document->frame())
377 return 0;
378
379 const Settings* settings = document->frame()->settings();
380 if (!settings)
381 return 0;
382
383 AtomicString genericFamily;
384 if (familyName == "-webkit-serif")
385 genericFamily = settings->serifFontFamily();
386 else if (familyName == "-webkit-sans-serif")
387 genericFamily = settings->sansSerifFontFamily();
388 else if (familyName == "-webkit-cursive")
389 genericFamily = settings->cursiveFontFamily();
390 else if (familyName == "-webkit-fantasy")
391 genericFamily = settings->fantasyFontFamily();
392 else if (familyName == "-webkit-monospace")
393 genericFamily = settings->fixedFontFamily();
394 else if (familyName == "-webkit-standard")
395 genericFamily = settings->standardFontFamily();
396
397 if (!genericFamily.isEmpty())
398 return fontCache()->getCachedFontData(fontCache()->getCachedFontPlatformData(fontDescription, genericFamily));
399
400 return 0;
401 }
402
403 static FontTraitsMask desiredTraitsMaskForComparison;
404
compareFontFaces(CSSFontFace * first,CSSFontFace * second)405 static inline bool compareFontFaces(CSSFontFace* first, CSSFontFace* second)
406 {
407 FontTraitsMask firstTraitsMask = first->traitsMask();
408 FontTraitsMask secondTraitsMask = second->traitsMask();
409
410 bool firstHasDesiredVariant = firstTraitsMask & desiredTraitsMaskForComparison & FontVariantMask;
411 bool secondHasDesiredVariant = secondTraitsMask & desiredTraitsMaskForComparison & FontVariantMask;
412
413 if (firstHasDesiredVariant != secondHasDesiredVariant)
414 return firstHasDesiredVariant;
415
416 bool firstHasDesiredStyle = firstTraitsMask & desiredTraitsMaskForComparison & FontStyleMask;
417 bool secondHasDesiredStyle = secondTraitsMask & desiredTraitsMaskForComparison & FontStyleMask;
418
419 if (firstHasDesiredStyle != secondHasDesiredStyle)
420 return firstHasDesiredStyle;
421
422 if (secondTraitsMask & desiredTraitsMaskForComparison & FontWeightMask)
423 return false;
424 if (firstTraitsMask & desiredTraitsMaskForComparison & FontWeightMask)
425 return true;
426
427 // http://www.w3.org/TR/2002/WD-css3-webfonts-20020802/#q46 says: "If there are fewer then 9 weights in the family, the default algorithm
428 // for filling the "holes" is as follows. If '500' is unassigned, it will be assigned the same font as '400'. If any of the values '600',
429 // '700', '800', or '900' remains unassigned, they are assigned to the same face as the next darker assigned keyword, if any, or the next
430 // lighter one otherwise. If any of '300', '200', or '100' remains unassigned, it is assigned to the next lighter assigned keyword, if any,
431 // or the next darker otherwise."
432 // For '400', we made up our own rule (which then '500' follows).
433
434 static const unsigned fallbackRuleSets = 9;
435 static const unsigned rulesPerSet = 8;
436 static const FontTraitsMask weightFallbackRuleSets[fallbackRuleSets][rulesPerSet] = {
437 { FontWeight200Mask, FontWeight300Mask, FontWeight400Mask, FontWeight500Mask, FontWeight600Mask, FontWeight700Mask, FontWeight800Mask, FontWeight900Mask },
438 { FontWeight100Mask, FontWeight300Mask, FontWeight400Mask, FontWeight500Mask, FontWeight600Mask, FontWeight700Mask, FontWeight800Mask, FontWeight900Mask },
439 { FontWeight200Mask, FontWeight100Mask, FontWeight400Mask, FontWeight500Mask, FontWeight600Mask, FontWeight700Mask, FontWeight800Mask, FontWeight900Mask },
440 { FontWeight500Mask, FontWeight300Mask, FontWeight600Mask, FontWeight200Mask, FontWeight700Mask, FontWeight100Mask, FontWeight800Mask, FontWeight900Mask },
441 { FontWeight400Mask, FontWeight300Mask, FontWeight600Mask, FontWeight200Mask, FontWeight700Mask, FontWeight100Mask, FontWeight800Mask, FontWeight900Mask },
442 { FontWeight700Mask, FontWeight800Mask, FontWeight900Mask, FontWeight500Mask, FontWeight400Mask, FontWeight300Mask, FontWeight200Mask, FontWeight100Mask },
443 { FontWeight800Mask, FontWeight900Mask, FontWeight600Mask, FontWeight500Mask, FontWeight400Mask, FontWeight300Mask, FontWeight200Mask, FontWeight100Mask },
444 { FontWeight900Mask, FontWeight700Mask, FontWeight600Mask, FontWeight500Mask, FontWeight400Mask, FontWeight300Mask, FontWeight200Mask, FontWeight100Mask },
445 { FontWeight800Mask, FontWeight700Mask, FontWeight600Mask, FontWeight500Mask, FontWeight400Mask, FontWeight300Mask, FontWeight200Mask, FontWeight100Mask }
446 };
447
448 unsigned ruleSetIndex = 0;
449 unsigned w = FontWeight100Bit;
450 while (!(desiredTraitsMaskForComparison & (1 << w))) {
451 w++;
452 ruleSetIndex++;
453 }
454
455 ASSERT(ruleSetIndex < fallbackRuleSets);
456 const FontTraitsMask* weightFallbackRule = weightFallbackRuleSets[ruleSetIndex];
457 for (unsigned i = 0; i < rulesPerSet; ++i) {
458 if (secondTraitsMask & weightFallbackRule[i])
459 return false;
460 if (firstTraitsMask & weightFallbackRule[i])
461 return true;
462 }
463
464 return false;
465 }
466
getFontData(const FontDescription & fontDescription,const AtomicString & familyName)467 FontData* CSSFontSelector::getFontData(const FontDescription& fontDescription, const AtomicString& familyName)
468 {
469 if (m_fontFaces.isEmpty()) {
470 if (familyName.startsWith("-webkit-"))
471 return fontDataForGenericFamily(m_document, fontDescription, familyName);
472 return 0;
473 }
474
475 String family = familyName.string();
476
477 #if ENABLE(SVG_FONTS)
478 if (fontDescription.smallCaps())
479 family += "-webkit-svg-small-caps";
480 #endif
481
482 Vector<RefPtr<CSSFontFace> >* familyFontFaces = m_fontFaces.get(family);
483 // If no face was found, then return 0 and let the OS come up with its best match for the name.
484 if (!familyFontFaces || familyFontFaces->isEmpty()) {
485 // If we were handed a generic family, but there was no match, go ahead and return the correct font based off our
486 // settings.
487 return fontDataForGenericFamily(m_document, fontDescription, familyName);
488 }
489
490 HashMap<unsigned, RefPtr<CSSSegmentedFontFace> >* segmentedFontFaceCache = m_fonts.get(family);
491 if (!segmentedFontFaceCache) {
492 segmentedFontFaceCache = new HashMap<unsigned, RefPtr<CSSSegmentedFontFace> >;
493 m_fonts.set(family, segmentedFontFaceCache);
494 }
495
496 FontTraitsMask traitsMask = fontDescription.traitsMask();
497
498 RefPtr<CSSSegmentedFontFace> face = segmentedFontFaceCache->get(traitsMask);
499
500 if (!face) {
501 face = CSSSegmentedFontFace::create(this);
502 segmentedFontFaceCache->set(traitsMask, face);
503 // Collect all matching faces and sort them in order of preference.
504 Vector<CSSFontFace*, 32> candidateFontFaces;
505 for (int i = familyFontFaces->size() - 1; i >= 0; --i) {
506 CSSFontFace* candidate = familyFontFaces->at(i).get();
507 unsigned candidateTraitsMask = candidate->traitsMask();
508 if ((traitsMask & FontStyleNormalMask) && !(candidateTraitsMask & FontStyleNormalMask))
509 continue;
510 if ((traitsMask & FontVariantNormalMask) && !(candidateTraitsMask & FontVariantNormalMask))
511 continue;
512 candidateFontFaces.append(candidate);
513 }
514
515 if (Vector<RefPtr<CSSFontFace> >* familyLocallyInstalledFontFaces = m_locallyInstalledFontFaces.get(family)) {
516 unsigned numLocallyInstalledFontFaces = familyLocallyInstalledFontFaces->size();
517 for (unsigned i = 0; i < numLocallyInstalledFontFaces; ++i) {
518 CSSFontFace* candidate = familyLocallyInstalledFontFaces->at(i).get();
519 unsigned candidateTraitsMask = candidate->traitsMask();
520 if ((traitsMask & FontStyleNormalMask) && !(candidateTraitsMask & FontStyleNormalMask))
521 continue;
522 if ((traitsMask & FontVariantNormalMask) && !(candidateTraitsMask & FontVariantNormalMask))
523 continue;
524 candidateFontFaces.append(candidate);
525 }
526 }
527
528 desiredTraitsMaskForComparison = traitsMask;
529 std::stable_sort(candidateFontFaces.begin(), candidateFontFaces.end(), compareFontFaces);
530 unsigned numCandidates = candidateFontFaces.size();
531 for (unsigned i = 0; i < numCandidates; ++i)
532 face->appendFontFace(candidateFontFaces[i]);
533 }
534
535 // We have a face. Ask it for a font data. If it cannot produce one, it will fail, and the OS will take over.
536 return face->getFontData(fontDescription);
537 }
538
539 }
540