PassRefPtr<FontData> CSSSegmentedFontFace::getFontData(const FontDescription& fontDescription)
{
    if (!isValid())
        return nullptr;

    FontTraits desiredTraits = fontDescription.traits();
    FontCacheKey key = fontDescription.cacheKey(FontFaceCreationParams(), desiredTraits);

    RefPtr<SegmentedFontData>& fontData = m_fontDataTable.add(key.hash(), nullptr).storedValue->value;
    if (fontData && fontData->numFaces())
        return fontData; // No release, we have a reference to an object in the cache which should retain the ref count it has.

    if (!fontData)
        fontData = SegmentedFontData::create();

    FontDescription requestedFontDescription(fontDescription);
    requestedFontDescription.setTraits(m_traits);
    requestedFontDescription.setSyntheticBold(m_traits.weight() < FontWeight600 && desiredTraits.weight() >= FontWeight600);
    requestedFontDescription.setSyntheticItalic(m_traits.style() == FontStyleNormal && desiredTraits.style() == FontStyleItalic);

    for (FontFaceList::reverse_iterator it = m_fontFaces.rbegin(); it != m_fontFaces.rend(); ++it) {
        if (!(*it)->cssFontFace()->isValid())
            continue;
        if (RefPtr<SimpleFontData> faceFontData = (*it)->cssFontFace()->getFontData(requestedFontDescription)) {
            ASSERT(!faceFontData->isSegmented());
            fontData->appendFace(FontDataForRangeSet(faceFontData.release(), (*it)->cssFontFace()->ranges()));
        }
    }
    if (fontData->numFaces())
        return fontData; // No release, we have a reference to an object in the cache which should retain the ref count it has.

    return nullptr;
}
Beispiel #2
0
void FontDescription::setTraits(FontTraits traits)
{
    setStyle(traits.style());
    setVariant(traits.variant());
    setWeight(traits.weight());
    setStretch(traits.stretch());
}
Beispiel #3
0
FontCacheKey FontDescription::cacheKey(const FontFaceCreationParams& creationParams, FontTraits desiredTraits) const
{
    FontTraits fontTraits = desiredTraits.bitfield() ? desiredTraits : traits();

    unsigned options =
        static_cast<unsigned>(m_syntheticItalic) << 5 | // bit 6
        static_cast<unsigned>(m_syntheticBold) << 4 | // bit 5
        static_cast<unsigned>(m_textRendering) << 2 | // bits 3-4
        static_cast<unsigned>(m_orientation) << 1 | // bit 2
        static_cast<unsigned>(m_subpixelTextPosition); // bit 1

    return FontCacheKey(creationParams, effectiveFontSize(), options | fontTraits.bitfield() << 8);
}
Beispiel #4
0
FontCacheKey FontDescription::cacheKey(const AtomicString& familyName, FontTraits desiredTraits) const
{
    FontTraits fontTraits = desiredTraits.mask()
        ? desiredTraits
        : traits();

    unsigned options =
        static_cast<unsigned>(m_syntheticItalic) << 7 | // bit 8
        static_cast<unsigned>(m_syntheticBold) << 6 | // bit 7
        static_cast<unsigned>(m_fontSmoothing) << 4 | // bits 5-6
        static_cast<unsigned>(m_textRendering) << 2 | // bits 3-4
        static_cast<unsigned>(m_orientation) << 1 | // bit 2
        static_cast<unsigned>(m_subpixelTextPosition); // bit 1

    return FontCacheKey(familyName, effectiveFontSize(), options | fontTraits.mask() << 8);
}
Beispiel #5
0
CSSSegmentedFontFace* FontFaceCache::get(const FontDescription& fontDescription, const AtomicString& family)
{
    TraitsMap* familyFontFaces = m_fontFaces.get(family);
    if (!familyFontFaces || familyFontFaces->isEmpty())
        return 0;

    FamilyToTraitsMap::AddResult traitsResult = m_fonts.add(family, nullptr);
    if (!traitsResult.storedValue->value)
        traitsResult.storedValue->value = adoptPtr(new TraitsMap);

    FontTraits traits = fontDescription.traits();
    TraitsMap::AddResult faceResult = traitsResult.storedValue->value->add(traits.bitfield(), nullptr);
    if (!faceResult.storedValue->value) {
        for (TraitsMap::const_iterator i = familyFontFaces->begin(); i != familyFontFaces->end(); ++i) {
            CSSSegmentedFontFace* candidate = i->value.get();
            FontTraits candidateTraits = candidate->traits();
            if (traits.style() == FontStyleNormal && candidateTraits.style() != FontStyleNormal)
                continue;
            if (traits.variant() == FontVariantNormal && candidateTraits.variant() != FontVariantNormal)
                continue;
            if (!faceResult.storedValue->value || compareFontFaces(candidate, faceResult.storedValue->value.get(), traits))
                faceResult.storedValue->value = candidate;
        }
    }
    return faceResult.storedValue->value.get();
}
static inline unsigned styleScore(FontTraits desired, FontTraits candidate)
{
    static_assert(FontStyleNormal == 0 && FontStyleItalic == 2,
        "Enumeration values need to match lookup table.");
    unsigned styleScoreLookupTable[][FontStyleItalic + 1] = {
        // "If the value is normal, normal faces are checked first, then oblique faces, then italic faces."
        // i.e. normal has the highest score, then oblique, then italic.
        { 2, 1, 0 },
        // "If the value is oblique, oblique faces are checked first, then italic faces and then normal faces."
        // i.e. normal gets the lowest score, oblique gets the highest, italic second best.
        { 0, 2, 1 },
        // "If the value of font-style is italic, italic faces are checked first, then oblique, then normal faces"
        // i.e. normal gets the lowest score, oblique is second best, italic highest.
        { 0, 1, 2 }
    };

    ASSERT_WITH_SECURITY_IMPLICATION(desired.style() < FontStyleItalic + 1);
    ASSERT_WITH_SECURITY_IMPLICATION(candidate.style() < FontStyleItalic + 1);

    return styleScoreLookupTable[desired.style()][candidate.style()];
}
PassRefPtr<FontData> CSSSegmentedFontFace::getFontData(const FontDescription& fontDescription)
{
    if (!isValid())
        return nullptr;

    FontTraits desiredTraits = fontDescription.traits();
    AtomicString emptyFontFamily = "";
    FontCacheKey key = fontDescription.cacheKey(emptyFontFamily, desiredTraits);

    RefPtr<SegmentedFontData>& fontData = m_fontDataTable.add(key.hash(), nullptr).storedValue->value;
    if (fontData && fontData->numRanges())
        return fontData; // No release, we have a reference to an object in the cache which should retain the ref count it has.

    if (!fontData)
        fontData = SegmentedFontData::create();

    FontDescription requestedFontDescription(fontDescription);
    requestedFontDescription.setTraits(m_traits);
    requestedFontDescription.setSyntheticBold(m_traits.weight() < FontWeight600 && desiredTraits.weight() >= FontWeight600);
    requestedFontDescription.setSyntheticItalic(m_traits.style() == FontStyleNormal && desiredTraits.style() == FontStyleItalic);

    for (FontFaceList::reverse_iterator it = m_fontFaces.rbegin(); it != m_fontFaces.rend(); ++it) {
        if (!(*it)->cssFontFace()->isValid())
            continue;
        if (RefPtr<SimpleFontData> faceFontData = (*it)->cssFontFace()->getFontData(requestedFontDescription)) {
            ASSERT(!faceFontData->isSegmented());
#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.
            if (faceFontData->isSVGFont() && desiredTraits.variant() == FontVariantSmallCaps && m_traits.variant() == FontVariantNormal)
                continue;
#endif
            appendFontData(fontData.get(), faceFontData.release(), (*it)->cssFontFace()->ranges());
        }
    }
    if (fontData->numRanges())
        return fontData; // No release, we have a reference to an object in the cache which should retain the ref count it has.

    return nullptr;
}
CSSSegmentedFontFace* FontFaceCache::get(const FontDescription& fontDescription, const AtomicString& family)
{
    TraitsMap* familyFontFaces = m_fontFaces.get(family);
    if (!familyFontFaces || familyFontFaces->isEmpty())
        return nullptr;

    FamilyToTraitsMap::AddResult traitsResult = m_fonts.add(family, nullptr);
    if (!traitsResult.storedValue->value)
        traitsResult.storedValue->value = new TraitsMap;

    FontTraits traits = fontDescription.traits();
    TraitsMap::AddResult faceResult = traitsResult.storedValue->value->add(traits.bitfield(), nullptr);
    if (!faceResult.storedValue->value) {
        for (const auto& item : *familyFontFaces) {
            CSSSegmentedFontFace* candidate = item.value.get();
            FontStyleMatcher styleMatcher(traits);
            if (!faceResult.storedValue->value || styleMatcher.isCandidateBetter(candidate, faceResult.storedValue->value.get()))
                faceResult.storedValue->value = candidate;
        }
    }
    return faceResult.storedValue->value.get();
}
static inline unsigned weightScore(FontTraits desired, FontTraits candidate)
{
    static_assert(FontWeight100 == 0 && FontWeight900 - FontWeight100 == 8,
        "Enumeration values need to match lookup table.");
    static const unsigned scoreLookupSize = FontWeight900 + 1;
    // https://drafts.csswg.org/css-fonts/#font-style-matching
    // "..if the desired weight is available that face matches. "
    static const unsigned weightScoreLookup[scoreLookupSize][scoreLookupSize] = {
        // "If the desired weight is less than 400, weights below the desired
        // weight are checked in descending order followed by weights above the
        // desired weight in ascending order until a match is found."
        { 9, 8, 7, 6, 5, 4, 3, 2, 1 }, // FontWeight100 desired
        { 8, 9, 7, 6, 5, 4, 3, 2, 1 }, // FontWeight200 desired
        { 7, 8, 9, 6, 5, 4, 3, 2, 1 }, // FontWeight300 desired

        // "If the desired weight is 400, 500 is checked first and then the rule
        // for desired weights less than 400 is used."
        { 5, 6, 7, 9, 8, 4, 3, 2, 1 }, // FontWeight400 desired

        // "If the desired weight is 500, 400 is checked first and then the rule
        // for desired weights less than 400 is used."
        { 5, 6, 7, 8, 9, 4, 3, 2, 1 }, // FontWeight500 desired

        // "If the desired weight is greater than 500, weights above the desired
        // weight are checked in ascending order followed by weights below the
        // desired weight in descending order until a match is found."
        { 1, 2, 3, 4, 5, 9, 8, 7, 6 }, // FontWeight600 desired
        { 1, 2, 3, 4, 5, 6, 9, 8, 7 }, // FontWeight700 desired
        { 1, 2, 3, 4, 5, 6, 7, 9, 8 }, // FontWeight800 desired
        { 1, 2, 3, 4, 5, 6, 7, 8, 9 } // FontWeight900 desired
    };

    unsigned desiredScoresLookup = static_cast<unsigned>(desired.weight());
    unsigned candidateScoreLookup = static_cast<unsigned>(candidate.weight());
    ASSERT_WITH_SECURITY_IMPLICATION(desiredScoresLookup < scoreLookupSize);
    ASSERT_WITH_SECURITY_IMPLICATION(candidateScoreLookup < scoreLookupSize);

    return weightScoreLookup[desiredScoresLookup][candidateScoreLookup];
}
static inline unsigned stretchDistanceToDesired(FontTraits desired, FontTraits candidate)
{
    return abs(static_cast<int>(desired.stretch() - candidate.stretch()));
}
Beispiel #11
0
static inline bool compareFontFaces(CSSSegmentedFontFace* first, CSSSegmentedFontFace* second, FontTraits desiredTraits)
{
    const FontTraits& firstTraits = first->traits();
    const FontTraits& secondTraits = second->traits();

    bool firstHasDesiredVariant = firstTraits.variant() == desiredTraits.variant();
    bool secondHasDesiredVariant = secondTraits.variant() == desiredTraits.variant();

    if (firstHasDesiredVariant != secondHasDesiredVariant)
        return firstHasDesiredVariant;

    // We need to check font-variant css property for CSS2.1 compatibility.
    if (desiredTraits.variant() == FontVariantSmallCaps) {
        // 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 = firstTraits.variant() == FontVariantSmallCaps;
        bool secondRequiresSmallCaps = secondTraits.variant() == FontVariantSmallCaps;
        if (firstRequiresSmallCaps != secondRequiresSmallCaps)
            return firstRequiresSmallCaps;
    }

    bool firstHasDesiredStyle = firstTraits.style() == desiredTraits.style();
    bool secondHasDesiredStyle = secondTraits.style() == desiredTraits.style();

    if (firstHasDesiredStyle != secondHasDesiredStyle)
        return firstHasDesiredStyle;

    if (desiredTraits.style() == FontStyleItalic) {
        // 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 = firstTraits.style() == FontStyleItalic;
        bool secondRequiresItalics = secondTraits.style() == FontStyleItalic;
        if (firstRequiresItalics != secondRequiresItalics)
            return firstRequiresItalics;
    }
    if (secondTraits.weight() == desiredTraits.weight())
        return false;

    if (firstTraits.weight() == desiredTraits.weight())
        return true;

    // http://www.w3.org/TR/2011/WD-css3-fonts-20111004/#font-matching-algorithm says :
    //   - If the desired weight is less than 400, weights below the desired weight are checked in descending order followed by weights above the desired weight in ascending order until a match is found.
    //   - If the desired weight is greater than 500, weights above the desired weight are checked in ascending order followed by weights below the desired weight in descending order until a match is found.
    //   - If the desired weight is 400, 500 is checked first and then the rule for desired weights less than 400 is used.
    //   - If the desired weight is 500, 400 is checked first and then the rule for desired weights less than 400 is used.
    static const unsigned fallbackRuleSets = 9;
    static const unsigned rulesPerSet = 8;
    static const FontWeight weightFallbackRuleSets[fallbackRuleSets][rulesPerSet] = {
        { FontWeight200, FontWeight300, FontWeight400, FontWeight500, FontWeight600, FontWeight700, FontWeight800, FontWeight900 },
        { FontWeight100, FontWeight300, FontWeight400, FontWeight500, FontWeight600, FontWeight700, FontWeight800, FontWeight900 },
        { FontWeight200, FontWeight100, FontWeight400, FontWeight500, FontWeight600, FontWeight700, FontWeight800, FontWeight900 },
        { FontWeight500, FontWeight300, FontWeight200, FontWeight100, FontWeight600, FontWeight700, FontWeight800, FontWeight900 },
        { FontWeight400, FontWeight300, FontWeight200, FontWeight100, FontWeight600, FontWeight700, FontWeight800, FontWeight900 },
        { FontWeight700, FontWeight800, FontWeight900, FontWeight500, FontWeight400, FontWeight300, FontWeight200, FontWeight100 },
        { FontWeight800, FontWeight900, FontWeight600, FontWeight500, FontWeight400, FontWeight300, FontWeight200, FontWeight100 },
        { FontWeight900, FontWeight700, FontWeight600, FontWeight500, FontWeight400, FontWeight300, FontWeight200, FontWeight100 },
        { FontWeight800, FontWeight700, FontWeight600, FontWeight500, FontWeight400, FontWeight300, FontWeight200, FontWeight100 }
    };

    unsigned ruleSetIndex = static_cast<unsigned>(desiredTraits.weight());
    ASSERT(ruleSetIndex < fallbackRuleSets);
    const FontWeight* weightFallbackRule = weightFallbackRuleSets[ruleSetIndex];
    for (unsigned i = 0; i < rulesPerSet; ++i) {
        if (secondTraits.weight() == weightFallbackRule[i])
            return false;
        if (firstTraits.weight() == weightFallbackRule[i])
            return true;
    }

    return false;
}