2 * Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
3 * (C) 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
28 #include "CSSFontSelector.h"
30 #include "CachedFont.h"
31 #include "CSSFontFace.h"
32 #include "CSSFontFaceRule.h"
33 #include "CSSFontFaceSource.h"
34 #include "CSSFontFaceSrcValue.h"
35 #include "CSSMutableStyleDeclaration.h"
36 #include "CSSPrimitiveValue.h"
37 #include "CSSPropertyNames.h"
38 #include "CSSSegmentedFontFace.h"
39 #include "CSSUnicodeRangeValue.h"
40 #include "CSSValueKeywords.h"
41 #include "CSSValueList.h"
42 #include "CachedResourceLoader.h"
44 #include "FontCache.h"
45 #include "FontFamilyValue.h"
47 #include "RenderObject.h"
49 #include "SimpleFontData.h"
50 #include "WebKitFontFamilyNames.h"
51 #include <wtf/text/AtomicString.h>
54 #include "SVGFontFaceElement.h"
60 CSSFontSelector::CSSFontSelector(Document* document)
61 : m_document(document)
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.
68 fontCache()->addClient(this);
71 CSSFontSelector::~CSSFontSelector()
73 fontCache()->removeClient(this);
74 deleteAllValues(m_fontFaces);
75 deleteAllValues(m_locallyInstalledFontFaces);
76 deleteAllValues(m_fonts);
79 bool CSSFontSelector::isEmpty() const
81 return m_fonts.isEmpty();
84 CachedResourceLoader* CSSFontSelector::cachedResourceLoader() const
86 return m_document ? m_document->cachedResourceLoader() : 0;
89 void CSSFontSelector::addFontFaceRule(const CSSFontFaceRule* fontFaceRule)
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()))
99 CSSValueList* familyList = static_cast<CSSValueList*>(fontFamily.get());
100 if (!familyList->length())
103 CSSValueList* srcList = static_cast<CSSValueList*>(src.get());
104 if (!srcList->length())
107 CSSValueList* rangeList = static_cast<CSSValueList*>(unicodeRange.get());
109 unsigned traitsMask = 0;
111 if (RefPtr<CSSValue> fontStyle = style->getPropertyCSSValue(CSSPropertyFontStyle)) {
112 if (fontStyle->isPrimitiveValue()) {
113 RefPtr<CSSValueList> list = CSSValueList::createCommaSeparated();
114 list->append(fontStyle);
116 } else if (!fontStyle->isValueList())
119 CSSValueList* styleList = static_cast<CSSValueList*>(fontStyle.get());
120 unsigned numStyles = styleList->length();
124 for (unsigned i = 0; i < numStyles; ++i) {
125 switch (static_cast<CSSPrimitiveValue*>(styleList->itemWithoutBoundsCheck(i))->getIdent()) {
127 traitsMask |= FontStyleMask;
130 traitsMask |= FontStyleNormalMask;
133 case CSSValueOblique:
134 traitsMask |= FontStyleItalicMask;
141 traitsMask |= FontStyleMask;
143 if (RefPtr<CSSValue> fontWeight = style->getPropertyCSSValue(CSSPropertyFontWeight)) {
144 if (fontWeight->isPrimitiveValue()) {
145 RefPtr<CSSValueList> list = CSSValueList::createCommaSeparated();
146 list->append(fontWeight);
148 } else if (!fontWeight->isValueList())
151 CSSValueList* weightList = static_cast<CSSValueList*>(fontWeight.get());
152 unsigned numWeights = weightList->length();
156 for (unsigned i = 0; i < numWeights; ++i) {
157 switch (static_cast<CSSPrimitiveValue*>(weightList->itemWithoutBoundsCheck(i))->getIdent()) {
159 traitsMask |= FontWeightMask;
164 traitsMask |= FontWeight700Mask;
168 traitsMask |= FontWeight400Mask;
171 traitsMask |= FontWeight900Mask;
174 traitsMask |= FontWeight800Mask;
177 traitsMask |= FontWeight600Mask;
180 traitsMask |= FontWeight500Mask;
183 traitsMask |= FontWeight300Mask;
185 case CSSValueLighter:
187 traitsMask |= FontWeight200Mask;
190 traitsMask |= FontWeight100Mask;
197 traitsMask |= FontWeightMask;
199 if (RefPtr<CSSValue> fontVariant = style->getPropertyCSSValue(CSSPropertyFontVariant)) {
200 if (fontVariant->isPrimitiveValue()) {
201 RefPtr<CSSValueList> list = CSSValueList::createCommaSeparated();
202 list->append(fontVariant);
204 } else if (!fontVariant->isValueList())
207 CSSValueList* variantList = static_cast<CSSValueList*>(fontVariant.get());
208 unsigned numVariants = variantList->length();
212 for (unsigned i = 0; i < numVariants; ++i) {
213 switch (static_cast<CSSPrimitiveValue*>(variantList->itemWithoutBoundsCheck(i))->getIdent()) {
215 traitsMask |= FontVariantMask;
218 traitsMask |= FontVariantNormalMask;
220 case CSSValueSmallCaps:
221 traitsMask |= FontVariantSmallCapsMask;
228 traitsMask |= FontVariantMask;
230 // Each item in the src property's list is a single CSSFontFaceSource. Put them all into a CSSFontFace.
231 RefPtr<CSSFontFace> fontFace;
233 int srcLength = srcList->length();
235 bool foundSVGFont = false;
237 for (int i = 0; i < srcLength; i++) {
238 // An item in the list either specifies a string (local font name) or a URL (remote font to download).
239 CSSFontFaceSrcValue* item = static_cast<CSSFontFaceSrcValue*>(srcList->itemWithoutBoundsCheck(i));
240 CSSFontFaceSource* source = 0;
242 #if ENABLE(SVG_FONTS)
243 foundSVGFont = item->isSVGFontFaceSrc() || item->svgFontFaceElement();
245 if (!item->isLocal()) {
246 Settings* settings = m_document ? m_document->frame() ? m_document->frame()->settings() : 0 : 0;
247 bool allowDownloading = foundSVGFont || (settings && settings->downloadableBinaryFontsEnabled());
248 if (allowDownloading && item->isSupportedFormat() && m_document) {
249 ResourceRequest request(m_document->completeURL(item->resource()));
250 CachedFont* cachedFont = m_document->cachedResourceLoader()->requestFont(request);
252 source = new CSSFontFaceSource(item->resource(), cachedFont);
253 #if ENABLE(SVG_FONTS)
255 source->setHasExternalSVGFont(true);
260 source = new CSSFontFaceSource(item->resource());
264 fontFace = CSSFontFace::create(static_cast<FontTraitsMask>(traitsMask));
267 #if ENABLE(SVG_FONTS)
268 source->setSVGFontFaceElement(item->svgFontFaceElement());
270 fontFace->addSource(source);
276 if (fontFace && !fontFace->isValid())
280 unsigned numRanges = rangeList->length();
281 for (unsigned i = 0; i < numRanges; i++) {
282 CSSUnicodeRangeValue* range = static_cast<CSSUnicodeRangeValue*>(rangeList->itemWithoutBoundsCheck(i));
283 fontFace->addRange(range->from(), range->to());
287 // Hash under every single family name.
288 int familyLength = familyList->length();
289 for (int i = 0; i < familyLength; i++) {
290 CSSPrimitiveValue* item = static_cast<CSSPrimitiveValue*>(familyList->itemWithoutBoundsCheck(i));
292 if (item->primitiveType() == CSSPrimitiveValue::CSS_STRING)
293 familyName = static_cast<FontFamilyValue*>(item)->familyName();
294 else if (item->primitiveType() == CSSPrimitiveValue::CSS_IDENT) {
295 // We need to use the raw text for all the generic family types, since @font-face is a way of actually
296 // defining what font to use for those types.
298 switch (item->getIdent()) {
300 familyName = serifFamily;
302 case CSSValueSansSerif:
303 familyName = sansSerifFamily;
305 case CSSValueCursive:
306 familyName = cursiveFamily;
308 case CSSValueFantasy:
309 familyName = fantasyFamily;
311 case CSSValueMonospace:
312 familyName = monospaceFamily;
314 case CSSValueWebkitPictograph:
315 familyName = pictographFamily;
322 if (familyName.isEmpty())
325 Vector<RefPtr<CSSFontFace> >* familyFontFaces = m_fontFaces.get(familyName);
326 if (!familyFontFaces) {
327 familyFontFaces = new Vector<RefPtr<CSSFontFace> >;
328 m_fontFaces.set(familyName, familyFontFaces);
330 ASSERT(!m_locallyInstalledFontFaces.contains(familyName));
331 Vector<RefPtr<CSSFontFace> >* familyLocallyInstalledFaces;
333 Vector<unsigned> locallyInstalledFontsTraitsMasks;
334 fontCache()->getTraitsInFamily(familyName, locallyInstalledFontsTraitsMasks);
335 unsigned numLocallyInstalledFaces = locallyInstalledFontsTraitsMasks.size();
336 if (numLocallyInstalledFaces) {
337 familyLocallyInstalledFaces = new Vector<RefPtr<CSSFontFace> >;
338 m_locallyInstalledFontFaces.set(familyName, familyLocallyInstalledFaces);
340 for (unsigned i = 0; i < numLocallyInstalledFaces; ++i) {
341 RefPtr<CSSFontFace> locallyInstalledFontFace = CSSFontFace::create(static_cast<FontTraitsMask>(locallyInstalledFontsTraitsMasks[i]), true);
342 locallyInstalledFontFace->addSource(new CSSFontFaceSource(familyName));
343 ASSERT(locallyInstalledFontFace->isValid());
344 familyLocallyInstalledFaces->append(locallyInstalledFontFace);
349 familyFontFaces->append(fontFace);
353 void CSSFontSelector::registerForInvalidationCallbacks(FontSelectorClient* client)
355 m_clients.add(client);
358 void CSSFontSelector::unregisterForInvalidationCallbacks(FontSelectorClient* client)
360 m_clients.remove(client);
363 void CSSFontSelector::dispatchInvalidationCallbacks()
365 Vector<FontSelectorClient*> clients;
366 copyToVector(m_clients, clients);
367 for (size_t i = 0; i < clients.size(); ++i)
368 clients[i]->fontsNeedUpdate(this);
370 // FIXME: Make Document a FontSelectorClient so that it can simply register for invalidation callbacks.
371 if (!m_document || m_document->inPageCache() || !m_document->renderer())
373 m_document->scheduleForcedStyleRecalc();
376 void CSSFontSelector::fontLoaded()
378 dispatchInvalidationCallbacks();
381 void CSSFontSelector::fontCacheInvalidated()
383 dispatchInvalidationCallbacks();
386 static FontData* fontDataForGenericFamily(Document* document, const FontDescription& fontDescription, const AtomicString& familyName)
388 if (!document || !document->frame())
391 const Settings* settings = document->frame()->settings();
395 AtomicString genericFamily;
396 UScriptCode script = fontDescription.script();
398 if (familyName == serifFamily)
399 genericFamily = settings->serifFontFamily(script);
400 else if (familyName == sansSerifFamily)
401 genericFamily = settings->sansSerifFontFamily(script);
402 else if (familyName == cursiveFamily)
403 genericFamily = settings->cursiveFontFamily(script);
404 else if (familyName == fantasyFamily)
405 genericFamily = settings->fantasyFontFamily(script);
406 else if (familyName == monospaceFamily)
407 genericFamily = settings->fixedFontFamily(script);
408 else if (familyName == pictographFamily)
409 genericFamily = settings->pictographFontFamily(script);
410 else if (familyName == standardFamily)
411 genericFamily = settings->standardFontFamily(script);
413 if (!genericFamily.isEmpty())
414 return fontCache()->getCachedFontData(fontDescription, genericFamily);
419 static FontTraitsMask desiredTraitsMaskForComparison;
421 static inline bool compareFontFaces(CSSFontFace* first, CSSFontFace* second)
423 FontTraitsMask firstTraitsMask = first->traitsMask();
424 FontTraitsMask secondTraitsMask = second->traitsMask();
426 bool firstHasDesiredVariant = firstTraitsMask & desiredTraitsMaskForComparison & FontVariantMask;
427 bool secondHasDesiredVariant = secondTraitsMask & desiredTraitsMaskForComparison & FontVariantMask;
429 if (firstHasDesiredVariant != secondHasDesiredVariant)
430 return firstHasDesiredVariant;
432 if ((desiredTraitsMaskForComparison & FontVariantSmallCapsMask) && !first->isLocalFallback() && !second->isLocalFallback()) {
433 // Prefer a font that has indicated that it can only support small-caps to a font that claims to support
434 // all variants. The specialized font is more likely to be true small-caps and not require synthesis.
435 bool firstRequiresSmallCaps = (firstTraitsMask & FontVariantSmallCapsMask) && !(firstTraitsMask & FontVariantNormalMask);
436 bool secondRequiresSmallCaps = (secondTraitsMask & FontVariantSmallCapsMask) && !(secondTraitsMask & FontVariantNormalMask);
437 if (firstRequiresSmallCaps != secondRequiresSmallCaps)
438 return firstRequiresSmallCaps;
441 bool firstHasDesiredStyle = firstTraitsMask & desiredTraitsMaskForComparison & FontStyleMask;
442 bool secondHasDesiredStyle = secondTraitsMask & desiredTraitsMaskForComparison & FontStyleMask;
444 if (firstHasDesiredStyle != secondHasDesiredStyle)
445 return firstHasDesiredStyle;
447 if ((desiredTraitsMaskForComparison & FontStyleItalicMask) && !first->isLocalFallback() && !second->isLocalFallback()) {
448 // Prefer a font that has indicated that it can only support italics to a font that claims to support
449 // all styles. The specialized font is more likely to be the one the author wants used.
450 bool firstRequiresItalics = (firstTraitsMask & FontStyleItalicMask) && !(firstTraitsMask & FontStyleNormalMask);
451 bool secondRequiresItalics = (secondTraitsMask & FontStyleItalicMask) && !(secondTraitsMask & FontStyleNormalMask);
452 if (firstRequiresItalics != secondRequiresItalics)
453 return firstRequiresItalics;
456 if (secondTraitsMask & desiredTraitsMaskForComparison & FontWeightMask)
458 if (firstTraitsMask & desiredTraitsMaskForComparison & FontWeightMask)
461 // 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
462 // 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',
463 // '700', '800', or '900' remains unassigned, they are assigned to the same face as the next darker assigned keyword, if any, or the next
464 // lighter one otherwise. If any of '300', '200', or '100' remains unassigned, it is assigned to the next lighter assigned keyword, if any,
465 // or the next darker otherwise."
466 // For '400', we made up our own rule (which then '500' follows).
468 static const unsigned fallbackRuleSets = 9;
469 static const unsigned rulesPerSet = 8;
470 static const FontTraitsMask weightFallbackRuleSets[fallbackRuleSets][rulesPerSet] = {
471 { FontWeight200Mask, FontWeight300Mask, FontWeight400Mask, FontWeight500Mask, FontWeight600Mask, FontWeight700Mask, FontWeight800Mask, FontWeight900Mask },
472 { FontWeight100Mask, FontWeight300Mask, FontWeight400Mask, FontWeight500Mask, FontWeight600Mask, FontWeight700Mask, FontWeight800Mask, FontWeight900Mask },
473 { FontWeight200Mask, FontWeight100Mask, FontWeight400Mask, FontWeight500Mask, FontWeight600Mask, FontWeight700Mask, FontWeight800Mask, FontWeight900Mask },
474 { FontWeight500Mask, FontWeight300Mask, FontWeight600Mask, FontWeight200Mask, FontWeight700Mask, FontWeight100Mask, FontWeight800Mask, FontWeight900Mask },
475 { FontWeight400Mask, FontWeight300Mask, FontWeight600Mask, FontWeight200Mask, FontWeight700Mask, FontWeight100Mask, FontWeight800Mask, FontWeight900Mask },
476 { FontWeight700Mask, FontWeight800Mask, FontWeight900Mask, FontWeight500Mask, FontWeight400Mask, FontWeight300Mask, FontWeight200Mask, FontWeight100Mask },
477 { FontWeight800Mask, FontWeight900Mask, FontWeight600Mask, FontWeight500Mask, FontWeight400Mask, FontWeight300Mask, FontWeight200Mask, FontWeight100Mask },
478 { FontWeight900Mask, FontWeight700Mask, FontWeight600Mask, FontWeight500Mask, FontWeight400Mask, FontWeight300Mask, FontWeight200Mask, FontWeight100Mask },
479 { FontWeight800Mask, FontWeight700Mask, FontWeight600Mask, FontWeight500Mask, FontWeight400Mask, FontWeight300Mask, FontWeight200Mask, FontWeight100Mask }
482 unsigned ruleSetIndex = 0;
483 unsigned w = FontWeight100Bit;
484 while (!(desiredTraitsMaskForComparison & (1 << w))) {
489 ASSERT(ruleSetIndex < fallbackRuleSets);
490 const FontTraitsMask* weightFallbackRule = weightFallbackRuleSets[ruleSetIndex];
491 for (unsigned i = 0; i < rulesPerSet; ++i) {
492 if (secondTraitsMask & weightFallbackRule[i])
494 if (firstTraitsMask & weightFallbackRule[i])
501 FontData* CSSFontSelector::getFontData(const FontDescription& fontDescription, const AtomicString& familyName)
503 if (m_fontFaces.isEmpty()) {
504 if (familyName.startsWith("-webkit-"))
505 return fontDataForGenericFamily(m_document, fontDescription, familyName);
506 if (fontDescription.genericFamily() == FontDescription::StandardFamily && !fontDescription.isSpecifiedFont())
507 return fontDataForGenericFamily(m_document, fontDescription, "-webkit-standard");
511 String family = familyName.string();
513 Vector<RefPtr<CSSFontFace> >* familyFontFaces = m_fontFaces.get(family);
514 // If no face was found, then return 0 and let the OS come up with its best match for the name.
515 if (!familyFontFaces || familyFontFaces->isEmpty()) {
516 // If we were handed a generic family, but there was no match, go ahead and return the correct font based off our
518 if (fontDescription.genericFamily() == FontDescription::StandardFamily && !fontDescription.isSpecifiedFont())
519 return fontDataForGenericFamily(m_document, fontDescription, "-webkit-standard");
520 return fontDataForGenericFamily(m_document, fontDescription, familyName);
523 HashMap<unsigned, RefPtr<CSSSegmentedFontFace> >* segmentedFontFaceCache = m_fonts.get(family);
524 if (!segmentedFontFaceCache) {
525 segmentedFontFaceCache = new HashMap<unsigned, RefPtr<CSSSegmentedFontFace> >;
526 m_fonts.set(family, segmentedFontFaceCache);
529 FontTraitsMask traitsMask = fontDescription.traitsMask();
531 RefPtr<CSSSegmentedFontFace> face = segmentedFontFaceCache->get(traitsMask);
534 face = CSSSegmentedFontFace::create(this);
535 segmentedFontFaceCache->set(traitsMask, face);
536 // Collect all matching faces and sort them in order of preference.
537 Vector<CSSFontFace*, 32> candidateFontFaces;
538 for (int i = familyFontFaces->size() - 1; i >= 0; --i) {
539 CSSFontFace* candidate = familyFontFaces->at(i).get();
540 unsigned candidateTraitsMask = candidate->traitsMask();
541 if ((traitsMask & FontStyleNormalMask) && !(candidateTraitsMask & FontStyleNormalMask))
543 if ((traitsMask & FontVariantNormalMask) && !(candidateTraitsMask & FontVariantNormalMask))
545 #if ENABLE(SVG_FONTS)
546 // For SVG Fonts that specify that they only support the "normal" variant, we will assume they are incapable
547 // of small-caps synthesis and just ignore the font face as a candidate.
548 if (candidate->hasSVGFontFaceSource() && (traitsMask & FontVariantSmallCapsMask) && !(candidateTraitsMask & FontVariantSmallCapsMask))
551 candidateFontFaces.append(candidate);
554 if (Vector<RefPtr<CSSFontFace> >* familyLocallyInstalledFontFaces = m_locallyInstalledFontFaces.get(family)) {
555 unsigned numLocallyInstalledFontFaces = familyLocallyInstalledFontFaces->size();
556 for (unsigned i = 0; i < numLocallyInstalledFontFaces; ++i) {
557 CSSFontFace* candidate = familyLocallyInstalledFontFaces->at(i).get();
558 unsigned candidateTraitsMask = candidate->traitsMask();
559 if ((traitsMask & FontStyleNormalMask) && !(candidateTraitsMask & FontStyleNormalMask))
561 if ((traitsMask & FontVariantNormalMask) && !(candidateTraitsMask & FontVariantNormalMask))
563 candidateFontFaces.append(candidate);
567 desiredTraitsMaskForComparison = traitsMask;
568 std::stable_sort(candidateFontFaces.begin(), candidateFontFaces.end(), compareFontFaces);
569 unsigned numCandidates = candidateFontFaces.size();
570 for (unsigned i = 0; i < numCandidates; ++i)
571 face->appendFontFace(candidateFontFaces[i]);
574 // We have a face. Ask it for a font data. If it cannot produce one, it will fail, and the OS will take over.
575 return face->getFontData(fontDescription);