/platform/external/webkit/WebCore/css/CSSFontSelector.cpp
https://github.com/aharish/totoro-gb-opensource-update2 · C++ · 538 lines · 419 code · 72 blank · 47 comment · 115 complexity · f76d2c39e9ff94626d213c77b1576c30 MD5 · raw file
- /*
- * Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
- * (C) 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org>
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- #include "config.h"
- #include "CSSFontSelector.h"
- #include "AtomicString.h"
- #include "CachedFont.h"
- #include "CSSFontFace.h"
- #include "CSSFontFaceRule.h"
- #include "CSSFontFaceSource.h"
- #include "CSSFontFaceSrcValue.h"
- #include "CSSMutableStyleDeclaration.h"
- #include "CSSPrimitiveValue.h"
- #include "CSSPropertyNames.h"
- #include "CSSSegmentedFontFace.h"
- #include "CSSUnicodeRangeValue.h"
- #include "CSSValueKeywords.h"
- #include "CSSValueList.h"
- #include "DocLoader.h"
- #include "Document.h"
- #include "FontCache.h"
- #include "FontFamilyValue.h"
- #include "Frame.h"
- #include "RenderObject.h"
- #include "Settings.h"
- #include "SimpleFontData.h"
- #if ENABLE(SVG)
- #include "SVGFontFaceElement.h"
- #include "SVGNames.h"
- #endif
- namespace WebCore {
- CSSFontSelector::CSSFontSelector(Document* document)
- : m_document(document)
- {
- // FIXME: An old comment used to say there was no need to hold a reference to m_document
- // because "we are guaranteed to be destroyed before the document". But there does not
- // seem to be any such guarantee.
- ASSERT(m_document);
- fontCache()->addClient(this);
- }
- CSSFontSelector::~CSSFontSelector()
- {
- fontCache()->removeClient(this);
- deleteAllValues(m_fontFaces);
- deleteAllValues(m_locallyInstalledFontFaces);
- deleteAllValues(m_fonts);
- }
- bool CSSFontSelector::isEmpty() const
- {
- return m_fonts.isEmpty();
- }
- DocLoader* CSSFontSelector::docLoader() const
- {
- return m_document ? m_document->docLoader() : 0;
- }
- void CSSFontSelector::addFontFaceRule(const CSSFontFaceRule* fontFaceRule)
- {
- // Obtain the font-family property and the src property. Both must be defined.
- const CSSMutableStyleDeclaration* style = fontFaceRule->style();
- RefPtr<CSSValue> fontFamily = style->getPropertyCSSValue(CSSPropertyFontFamily);
- RefPtr<CSSValue> src = style->getPropertyCSSValue(CSSPropertySrc);
- RefPtr<CSSValue> unicodeRange = style->getPropertyCSSValue(CSSPropertyUnicodeRange);
- if (!fontFamily || !src || !fontFamily->isValueList() || !src->isValueList() || (unicodeRange && !unicodeRange->isValueList()))
- return;
- CSSValueList* familyList = static_cast<CSSValueList*>(fontFamily.get());
- if (!familyList->length())
- return;
- CSSValueList* srcList = static_cast<CSSValueList*>(src.get());
- if (!srcList->length())
- return;
- CSSValueList* rangeList = static_cast<CSSValueList*>(unicodeRange.get());
- unsigned traitsMask = 0;
- if (RefPtr<CSSValue> fontStyle = style->getPropertyCSSValue(CSSPropertyFontStyle)) {
- if (fontStyle->isPrimitiveValue()) {
- RefPtr<CSSValueList> list = CSSValueList::createCommaSeparated();
- list->append(fontStyle);
- fontStyle = list;
- } else if (!fontStyle->isValueList())
- return;
- CSSValueList* styleList = static_cast<CSSValueList*>(fontStyle.get());
- unsigned numStyles = styleList->length();
- if (!numStyles)
- return;
- for (unsigned i = 0; i < numStyles; ++i) {
- switch (static_cast<CSSPrimitiveValue*>(styleList->itemWithoutBoundsCheck(i))->getIdent()) {
- case CSSValueAll:
- traitsMask |= FontStyleMask;
- break;
- case CSSValueNormal:
- traitsMask |= FontStyleNormalMask;
- break;
- case CSSValueItalic:
- case CSSValueOblique:
- traitsMask |= FontStyleItalicMask;
- break;
- default:
- break;
- }
- }
- } else
- traitsMask |= FontStyleMask;
- if (RefPtr<CSSValue> fontWeight = style->getPropertyCSSValue(CSSPropertyFontWeight)) {
- if (fontWeight->isPrimitiveValue()) {
- RefPtr<CSSValueList> list = CSSValueList::createCommaSeparated();
- list->append(fontWeight);
- fontWeight = list;
- } else if (!fontWeight->isValueList())
- return;
- CSSValueList* weightList = static_cast<CSSValueList*>(fontWeight.get());
- unsigned numWeights = weightList->length();
- if (!numWeights)
- return;
- for (unsigned i = 0; i < numWeights; ++i) {
- switch (static_cast<CSSPrimitiveValue*>(weightList->itemWithoutBoundsCheck(i))->getIdent()) {
- case CSSValueAll:
- traitsMask |= FontWeightMask;
- break;
- case CSSValueBolder:
- case CSSValueBold:
- case CSSValue700:
- traitsMask |= FontWeight700Mask;
- break;
- case CSSValueNormal:
- case CSSValue400:
- traitsMask |= FontWeight400Mask;
- break;
- case CSSValue900:
- traitsMask |= FontWeight900Mask;
- break;
- case CSSValue800:
- traitsMask |= FontWeight800Mask;
- break;
- case CSSValue600:
- traitsMask |= FontWeight600Mask;
- break;
- case CSSValue500:
- traitsMask |= FontWeight500Mask;
- break;
- case CSSValue300:
- traitsMask |= FontWeight300Mask;
- break;
- case CSSValueLighter:
- case CSSValue200:
- traitsMask |= FontWeight200Mask;
- break;
- case CSSValue100:
- traitsMask |= FontWeight100Mask;
- break;
- default:
- break;
- }
- }
- } else
- traitsMask |= FontWeightMask;
- if (RefPtr<CSSValue> fontVariant = style->getPropertyCSSValue(CSSPropertyFontVariant)) {
- if (fontVariant->isPrimitiveValue()) {
- RefPtr<CSSValueList> list = CSSValueList::createCommaSeparated();
- list->append(fontVariant);
- fontVariant = list;
- } else if (!fontVariant->isValueList())
- return;
- CSSValueList* variantList = static_cast<CSSValueList*>(fontVariant.get());
- unsigned numVariants = variantList->length();
- if (!numVariants)
- return;
- for (unsigned i = 0; i < numVariants; ++i) {
- switch (static_cast<CSSPrimitiveValue*>(variantList->itemWithoutBoundsCheck(i))->getIdent()) {
- case CSSValueAll:
- traitsMask |= FontVariantMask;
- break;
- case CSSValueNormal:
- traitsMask |= FontVariantNormalMask;
- break;
- case CSSValueSmallCaps:
- traitsMask |= FontVariantSmallCapsMask;
- break;
- default:
- break;
- }
- }
- } else
- traitsMask |= FontVariantNormalMask;
- // Each item in the src property's list is a single CSSFontFaceSource. Put them all into a CSSFontFace.
- RefPtr<CSSFontFace> fontFace;
- int srcLength = srcList->length();
- bool foundLocal = false;
- bool foundSVGFont = false;
- for (int i = 0; i < srcLength; i++) {
- // An item in the list either specifies a string (local font name) or a URL (remote font to download).
- CSSFontFaceSrcValue* item = static_cast<CSSFontFaceSrcValue*>(srcList->itemWithoutBoundsCheck(i));
- CSSFontFaceSource* source = 0;
- #if ENABLE(SVG_FONTS)
- foundSVGFont = item->isSVGFontFaceSrc() || item->svgFontFaceElement();
- #endif
- if (!item->isLocal()) {
- Settings* settings = m_document ? m_document->frame() ? m_document->frame()->settings() : 0 : 0;
- bool allowDownloading = foundSVGFont || (settings && settings->downloadableBinaryFontsEnabled());
- if (allowDownloading && item->isSupportedFormat() && m_document) {
- CachedFont* cachedFont = m_document->docLoader()->requestFont(item->resource());
- if (cachedFont) {
- #if ENABLE(SVG_FONTS)
- if (foundSVGFont)
- cachedFont->setSVGFont(true);
- #endif
- source = new CSSFontFaceSource(item->resource(), cachedFont);
- }
- }
- } else {
- source = new CSSFontFaceSource(item->resource());
- foundLocal = true;
- }
- if (!fontFace)
- fontFace = CSSFontFace::create(static_cast<FontTraitsMask>(traitsMask));
- if (source) {
- #if ENABLE(SVG_FONTS)
- source->setSVGFontFaceElement(item->svgFontFaceElement());
- #endif
- fontFace->addSource(source);
- }
- }
- ASSERT(fontFace);
- if (fontFace && !fontFace->isValid())
- return;
- if (rangeList) {
- unsigned numRanges = rangeList->length();
- for (unsigned i = 0; i < numRanges; i++) {
- CSSUnicodeRangeValue* range = static_cast<CSSUnicodeRangeValue*>(rangeList->itemWithoutBoundsCheck(i));
- fontFace->addRange(range->from(), range->to());
- }
- }
- // Hash under every single family name.
- int familyLength = familyList->length();
- for (int i = 0; i < familyLength; i++) {
- CSSPrimitiveValue* item = static_cast<CSSPrimitiveValue*>(familyList->itemWithoutBoundsCheck(i));
- String familyName;
- if (item->primitiveType() == CSSPrimitiveValue::CSS_STRING)
- familyName = static_cast<FontFamilyValue*>(item)->familyName();
- else if (item->primitiveType() == CSSPrimitiveValue::CSS_IDENT) {
- // We need to use the raw text for all the generic family types, since @font-face is a way of actually
- // defining what font to use for those types.
- String familyName;
- switch (item->getIdent()) {
- case CSSValueSerif:
- familyName = "-webkit-serif";
- break;
- case CSSValueSansSerif:
- familyName = "-webkit-sans-serif";
- break;
- case CSSValueCursive:
- familyName = "-webkit-cursive";
- break;
- case CSSValueFantasy:
- familyName = "-webkit-fantasy";
- break;
- case CSSValueMonospace:
- familyName = "-webkit-monospace";
- break;
- default:
- break;
- }
- }
- if (familyName.isEmpty())
- continue;
- #if ENABLE(SVG_FONTS)
- // SVG allows several <font> elements with the same font-family, differing only
- // in ie. font-variant. Be sure to pick up the right one - in getFontData below.
- if (foundSVGFont && (traitsMask & FontVariantSmallCapsMask))
- familyName += "-webkit-svg-small-caps";
- #endif
- Vector<RefPtr<CSSFontFace> >* familyFontFaces = m_fontFaces.get(familyName);
- if (!familyFontFaces) {
- familyFontFaces = new Vector<RefPtr<CSSFontFace> >;
- m_fontFaces.set(familyName, familyFontFaces);
- ASSERT(!m_locallyInstalledFontFaces.contains(familyName));
- Vector<RefPtr<CSSFontFace> >* familyLocallyInstalledFaces;
- Vector<unsigned> locallyInstalledFontsTraitsMasks;
- fontCache()->getTraitsInFamily(familyName, locallyInstalledFontsTraitsMasks);
- unsigned numLocallyInstalledFaces = locallyInstalledFontsTraitsMasks.size();
- if (numLocallyInstalledFaces) {
- familyLocallyInstalledFaces = new Vector<RefPtr<CSSFontFace> >;
- m_locallyInstalledFontFaces.set(familyName, familyLocallyInstalledFaces);
- for (unsigned i = 0; i < numLocallyInstalledFaces; ++i) {
- RefPtr<CSSFontFace> locallyInstalledFontFace = CSSFontFace::create(static_cast<FontTraitsMask>(locallyInstalledFontsTraitsMasks[i]));
- locallyInstalledFontFace->addSource(new CSSFontFaceSource(familyName));
- ASSERT(locallyInstalledFontFace->isValid());
- familyLocallyInstalledFaces->append(locallyInstalledFontFace);
- }
- }
- }
- familyFontFaces->append(fontFace);
- }
- }
- void CSSFontSelector::fontLoaded()
- {
- if (!m_document || m_document->inPageCache() || !m_document->renderer())
- return;
- m_document->recalcStyle(Document::Force);
- m_document->renderer()->setNeedsLayoutAndPrefWidthsRecalc();
- }
- void CSSFontSelector::fontCacheInvalidated()
- {
- if (!m_document || m_document->inPageCache() || !m_document->renderer())
- return;
- m_document->recalcStyle(Document::Force);
- m_document->renderer()->setNeedsLayoutAndPrefWidthsRecalc();
- }
- static FontData* fontDataForGenericFamily(Document* document, const FontDescription& fontDescription, const AtomicString& familyName)
- {
- if (!document || !document->frame())
- return 0;
- const Settings* settings = document->frame()->settings();
- if (!settings)
- return 0;
-
- AtomicString genericFamily;
- if (familyName == "-webkit-serif")
- genericFamily = settings->serifFontFamily();
- else if (familyName == "-webkit-sans-serif")
- genericFamily = settings->sansSerifFontFamily();
- else if (familyName == "-webkit-cursive")
- genericFamily = settings->cursiveFontFamily();
- else if (familyName == "-webkit-fantasy")
- genericFamily = settings->fantasyFontFamily();
- else if (familyName == "-webkit-monospace")
- genericFamily = settings->fixedFontFamily();
- else if (familyName == "-webkit-standard")
- genericFamily = settings->standardFontFamily();
- if (!genericFamily.isEmpty())
- return fontCache()->getCachedFontData(fontDescription, genericFamily);
- return 0;
- }
- static FontTraitsMask desiredTraitsMaskForComparison;
- static inline bool compareFontFaces(CSSFontFace* first, CSSFontFace* second)
- {
- FontTraitsMask firstTraitsMask = first->traitsMask();
- FontTraitsMask secondTraitsMask = second->traitsMask();
- bool firstHasDesiredVariant = firstTraitsMask & desiredTraitsMaskForComparison & FontVariantMask;
- bool secondHasDesiredVariant = secondTraitsMask & desiredTraitsMaskForComparison & FontVariantMask;
- if (firstHasDesiredVariant != secondHasDesiredVariant)
- return firstHasDesiredVariant;
- bool firstHasDesiredStyle = firstTraitsMask & desiredTraitsMaskForComparison & FontStyleMask;
- bool secondHasDesiredStyle = secondTraitsMask & desiredTraitsMaskForComparison & FontStyleMask;
- if (firstHasDesiredStyle != secondHasDesiredStyle)
- return firstHasDesiredStyle;
- if (secondTraitsMask & desiredTraitsMaskForComparison & FontWeightMask)
- return false;
- if (firstTraitsMask & desiredTraitsMaskForComparison & FontWeightMask)
- return true;
- // 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
- // 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',
- // '700', '800', or '900' remains unassigned, they are assigned to the same face as the next darker assigned keyword, if any, or the next
- // lighter one otherwise. If any of '300', '200', or '100' remains unassigned, it is assigned to the next lighter assigned keyword, if any,
- // or the next darker otherwise."
- // For '400', we made up our own rule (which then '500' follows).
- static const unsigned fallbackRuleSets = 9;
- static const unsigned rulesPerSet = 8;
- static const FontTraitsMask weightFallbackRuleSets[fallbackRuleSets][rulesPerSet] = {
- { FontWeight200Mask, FontWeight300Mask, FontWeight400Mask, FontWeight500Mask, FontWeight600Mask, FontWeight700Mask, FontWeight800Mask, FontWeight900Mask },
- { FontWeight100Mask, FontWeight300Mask, FontWeight400Mask, FontWeight500Mask, FontWeight600Mask, FontWeight700Mask, FontWeight800Mask, FontWeight900Mask },
- { FontWeight200Mask, FontWeight100Mask, FontWeight400Mask, FontWeight500Mask, FontWeight600Mask, FontWeight700Mask, FontWeight800Mask, FontWeight900Mask },
- { FontWeight500Mask, FontWeight300Mask, FontWeight600Mask, FontWeight200Mask, FontWeight700Mask, FontWeight100Mask, FontWeight800Mask, FontWeight900Mask },
- { FontWeight400Mask, FontWeight300Mask, FontWeight600Mask, FontWeight200Mask, FontWeight700Mask, FontWeight100Mask, FontWeight800Mask, FontWeight900Mask },
- { FontWeight700Mask, FontWeight800Mask, FontWeight900Mask, FontWeight500Mask, FontWeight400Mask, FontWeight300Mask, FontWeight200Mask, FontWeight100Mask },
- { FontWeight800Mask, FontWeight900Mask, FontWeight600Mask, FontWeight500Mask, FontWeight400Mask, FontWeight300Mask, FontWeight200Mask, FontWeight100Mask },
- { FontWeight900Mask, FontWeight700Mask, FontWeight600Mask, FontWeight500Mask, FontWeight400Mask, FontWeight300Mask, FontWeight200Mask, FontWeight100Mask },
- { FontWeight800Mask, FontWeight700Mask, FontWeight600Mask, FontWeight500Mask, FontWeight400Mask, FontWeight300Mask, FontWeight200Mask, FontWeight100Mask }
- };
- unsigned ruleSetIndex = 0;
- unsigned w = FontWeight100Bit;
- while (!(desiredTraitsMaskForComparison & (1 << w))) {
- w++;
- ruleSetIndex++;
- }
- ASSERT(ruleSetIndex < fallbackRuleSets);
- const FontTraitsMask* weightFallbackRule = weightFallbackRuleSets[ruleSetIndex];
- for (unsigned i = 0; i < rulesPerSet; ++i) {
- if (secondTraitsMask & weightFallbackRule[i])
- return false;
- if (firstTraitsMask & weightFallbackRule[i])
- return true;
- }
- return false;
- }
- FontData* CSSFontSelector::getFontData(const FontDescription& fontDescription, const AtomicString& familyName)
- {
- if (m_fontFaces.isEmpty()) {
- if (familyName.startsWith("-webkit-"))
- return fontDataForGenericFamily(m_document, fontDescription, familyName);
- return 0;
- }
- String family = familyName.string();
- #if ENABLE(SVG_FONTS)
- if (fontDescription.smallCaps())
- family += "-webkit-svg-small-caps";
- #endif
- Vector<RefPtr<CSSFontFace> >* familyFontFaces = m_fontFaces.get(family);
- // If no face was found, then return 0 and let the OS come up with its best match for the name.
- if (!familyFontFaces || familyFontFaces->isEmpty()) {
- // If we were handed a generic family, but there was no match, go ahead and return the correct font based off our
- // settings.
- return fontDataForGenericFamily(m_document, fontDescription, familyName);
- }
- HashMap<unsigned, RefPtr<CSSSegmentedFontFace> >* segmentedFontFaceCache = m_fonts.get(family);
- if (!segmentedFontFaceCache) {
- segmentedFontFaceCache = new HashMap<unsigned, RefPtr<CSSSegmentedFontFace> >;
- m_fonts.set(family, segmentedFontFaceCache);
- }
- FontTraitsMask traitsMask = fontDescription.traitsMask();
- RefPtr<CSSSegmentedFontFace> face = segmentedFontFaceCache->get(traitsMask);
- if (!face) {
- face = CSSSegmentedFontFace::create(this);
- segmentedFontFaceCache->set(traitsMask, face);
- // Collect all matching faces and sort them in order of preference.
- Vector<CSSFontFace*, 32> candidateFontFaces;
- for (int i = familyFontFaces->size() - 1; i >= 0; --i) {
- CSSFontFace* candidate = familyFontFaces->at(i).get();
- unsigned candidateTraitsMask = candidate->traitsMask();
- if ((traitsMask & FontStyleNormalMask) && !(candidateTraitsMask & FontStyleNormalMask))
- continue;
- if ((traitsMask & FontVariantNormalMask) && !(candidateTraitsMask & FontVariantNormalMask))
- continue;
- candidateFontFaces.append(candidate);
- }
- if (Vector<RefPtr<CSSFontFace> >* familyLocallyInstalledFontFaces = m_locallyInstalledFontFaces.get(family)) {
- unsigned numLocallyInstalledFontFaces = familyLocallyInstalledFontFaces->size();
- for (unsigned i = 0; i < numLocallyInstalledFontFaces; ++i) {
- CSSFontFace* candidate = familyLocallyInstalledFontFaces->at(i).get();
- unsigned candidateTraitsMask = candidate->traitsMask();
- if ((traitsMask & FontStyleNormalMask) && !(candidateTraitsMask & FontStyleNormalMask))
- continue;
- if ((traitsMask & FontVariantNormalMask) && !(candidateTraitsMask & FontVariantNormalMask))
- continue;
- candidateFontFaces.append(candidate);
- }
- }
- desiredTraitsMaskForComparison = traitsMask;
- std::stable_sort(candidateFontFaces.begin(), candidateFontFaces.end(), compareFontFaces);
- unsigned numCandidates = candidateFontFaces.size();
- for (unsigned i = 0; i < numCandidates; ++i)
- face->appendFontFace(candidateFontFaces[i]);
- }
- // We have a face. Ask it for a font data. If it cannot produce one, it will fail, and the OS will take over.
- return face->getFontData(fontDescription);
- }
- }