diff options
author | Steve Block <steveblock@google.com> | 2011-05-06 11:45:16 +0100 |
---|---|---|
committer | Steve Block <steveblock@google.com> | 2011-05-12 13:44:10 +0100 |
commit | cad810f21b803229eb11403f9209855525a25d57 (patch) | |
tree | 29a6fd0279be608e0fe9ffe9841f722f0f4e4269 /Source/WebCore/css/CSSFontSelector.cpp | |
parent | 121b0cf4517156d0ac5111caf9830c51b69bae8f (diff) | |
download | external_webkit-cad810f21b803229eb11403f9209855525a25d57.zip external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.gz external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.bz2 |
Merge WebKit at r75315: Initial merge by git.
Change-Id: I570314b346ce101c935ed22a626b48c2af266b84
Diffstat (limited to 'Source/WebCore/css/CSSFontSelector.cpp')
-rw-r--r-- | Source/WebCore/css/CSSFontSelector.cpp | 546 |
1 files changed, 546 insertions, 0 deletions
diff --git a/Source/WebCore/css/CSSFontSelector.cpp b/Source/WebCore/css/CSSFontSelector.cpp new file mode 100644 index 0000000..9c9a844 --- /dev/null +++ b/Source/WebCore/css/CSSFontSelector.cpp @@ -0,0 +1,546 @@ +/* + * 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 "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 "CachedResourceLoader.h" +#include "Document.h" +#include "FontCache.h" +#include "FontFamilyValue.h" +#include "Frame.h" +#include "RenderObject.h" +#include "Settings.h" +#include "SimpleFontData.h" +#include <wtf/text/AtomicString.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(); +} + +CachedResourceLoader* CSSFontSelector::cachedResourceLoader() const +{ + return m_document ? m_document->cachedResourceLoader() : 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 |= FontVariantMask; + + // 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 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->cachedResourceLoader()->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()); + } + + 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; + + 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]), true); + 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->scheduleForcedStyleRecalc(); +} + +void CSSFontSelector::fontCacheInvalidated() +{ + if (!m_document || m_document->inPageCache() || !m_document->renderer()) + return; + m_document->scheduleForcedStyleRecalc(); +} + +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; + + if ((desiredTraitsMaskForComparison & FontVariantSmallCapsMask) && !first->isLocalFallback() && !second->isLocalFallback()) { + // Prefer a font that has indicated that it can only support small-caps to a font that claims to support + // all variants. The specialized font is more likely to be true small-caps and not require synthesis. + bool firstRequiresSmallCaps = (firstTraitsMask & FontVariantSmallCapsMask) && !(firstTraitsMask & FontVariantNormalMask); + bool secondRequiresSmallCaps = (secondTraitsMask & FontVariantSmallCapsMask) && !(secondTraitsMask & FontVariantNormalMask); + if (firstRequiresSmallCaps != secondRequiresSmallCaps) + return firstRequiresSmallCaps; + } + + bool firstHasDesiredStyle = firstTraitsMask & desiredTraitsMaskForComparison & FontStyleMask; + bool secondHasDesiredStyle = secondTraitsMask & desiredTraitsMaskForComparison & FontStyleMask; + + if (firstHasDesiredStyle != secondHasDesiredStyle) + return firstHasDesiredStyle; + + if ((desiredTraitsMaskForComparison & FontStyleItalicMask) && !first->isLocalFallback() && !second->isLocalFallback()) { + // Prefer a font that has indicated that it can only support italics to a font that claims to support + // all styles. The specialized font is more likely to be the one the author wants used. + bool firstRequiresItalics = (firstTraitsMask & FontStyleItalicMask) && !(firstTraitsMask & FontStyleNormalMask); + bool secondRequiresItalics = (secondTraitsMask & FontStyleItalicMask) && !(secondTraitsMask & FontStyleNormalMask); + if (firstRequiresItalics != secondRequiresItalics) + return firstRequiresItalics; + } + + 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(); + + 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; +#if ENABLE(SVG_FONTS) + // For SVG Fonts that specify that they only support the "normal" variant, we will assume they are incapable + // of small-caps synthesis and just ignore the font face as a candidate. + if (candidate->hasSVGFontFaceSource() && (traitsMask & FontVariantSmallCapsMask) && !(candidateTraitsMask & FontVariantSmallCapsMask)) + continue; +#endif + 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); +} + +} |