bool FontFallbackIterator::rangeSetContributesForHint(const Vector<UChar32> hintList, const FontDataForRangeSet& segmentedFace)
{
    for (auto it = hintList.begin(); it != hintList.end(); ++it) {
        if (segmentedFace.contains(*it)) {
            if (!alreadyLoadingRangeForHintChar(*it))
                return true;
        }
    }
    return false;
}
예제 #2
0
PassRefPtr<ShapeResult> HarfBuzzShaper::shapeResult()
{
    RefPtr<ShapeResult> result = ShapeResult::create(m_font,
        m_normalizedBufferLength, m_textRun.direction());
    HarfBuzzScopedPtr<hb_buffer_t> harfBuzzBuffer(hb_buffer_create(), hb_buffer_destroy);

    const FontDescription& fontDescription = m_font->getFontDescription();
    const String& localeString = fontDescription.locale();
    CString locale = localeString.latin1();
    const hb_language_t language = hb_language_from_string(locale.data(), locale.length());

    bool needsCapsHandling = fontDescription.variantCaps() != FontDescription::CapsNormal;
    OpenTypeCapsSupport capsSupport;

    RunSegmenter::RunSegmenterRange segmentRange = {
        0,
        0,
        USCRIPT_INVALID_CODE,
        OrientationIterator::OrientationInvalid,
        FontFallbackPriority::Invalid };
    RunSegmenter runSegmenter(
        m_normalizedBuffer.get(),
        m_normalizedBufferLength,
        m_font->getFontDescription().orientation());

    Vector<UChar32> fallbackCharsHint;

    // TODO: Check whether this treatAsZerowidthspace from the previous script
    // segmentation plays a role here, does the new scriptRuniterator handle that correctly?
    while (runSegmenter.consume(&segmentRange)) {
        RefPtr<FontFallbackIterator> fallbackIterator =
            m_font->createFontFallbackIterator(
            segmentRange.fontFallbackPriority);

        appendToHolesQueue(HolesQueueNextFont, 0, 0);
        appendToHolesQueue(HolesQueueRange, segmentRange.start, segmentRange.end - segmentRange.start);

        const SimpleFontData* currentFont = nullptr;
        RefPtr<UnicodeRangeSet> currentFontRangeSet;

        bool fontCycleQueued = false;
        while (m_holesQueue.size()) {
            HolesQueueItem currentQueueItem = m_holesQueue.takeFirst();

            if (currentQueueItem.m_action == HolesQueueNextFont) {
                // For now, we're building a character list with which we probe
                // for needed fonts depending on the declared unicode-range of a
                // segmented CSS font. Alternatively, we can build a fake font
                // for the shaper and check whether any glyphs were found, or
                // define a new API on the shaper which will give us coverage
                // information?
                if (!collectFallbackHintChars(fallbackCharsHint, fallbackIterator->needsHintList())) {
                    // Give up shaping since we cannot retrieve a font fallback
                    // font without a hintlist.
                    m_holesQueue.clear();
                    break;
                }

                FontDataForRangeSet nextFontDataForRangeSet = fallbackIterator->next(fallbackCharsHint);
                currentFont = nextFontDataForRangeSet.fontData();
                currentFontRangeSet = nextFontDataForRangeSet.ranges();

                if (!currentFont) {
                    ASSERT(!m_holesQueue.size());
                    break;
                }
                fontCycleQueued = false;
                continue;
            }

            SmallCapsIterator::SmallCapsBehavior smallCapsBehavior = SmallCapsIterator::SmallCapsSameCase;
            if (needsCapsHandling) {
                capsSupport = OpenTypeCapsSupport(currentFont->platformData().harfBuzzFace(),
                    fontDescription.variantCaps(),
                    ICUScriptToHBScript(segmentRange.script));
                if (capsSupport.needsRunCaseSplitting())
                    splitUntilNextCaseChange(currentQueueItem, smallCapsBehavior);
            }

            ASSERT(currentQueueItem.m_numCharacters);

            const SimpleFontData* smallcapsAdjustedFont = needsCapsHandling
                && capsSupport.needsSyntheticFont(smallCapsBehavior)
                ? currentFont->smallCapsFontData(fontDescription).get()
                : currentFont;

            // Compatibility with SimpleFontData approach of keeping a flag for overriding drawing direction.
            // TODO: crbug.com/506224 This should go away in favor of storing that information elsewhere, for example in
            // ShapeResult.
            const SimpleFontData* directionAndSmallCapsAdjustedFont = fontDataAdjustedForOrientation(smallcapsAdjustedFont,
                m_font->getFontDescription().orientation(),
                segmentRange.renderOrientation);

            CaseMapIntend caseMapIntend = CaseMapIntend::KeepSameCase;
            if (needsCapsHandling) {
                caseMapIntend = capsSupport.needsCaseChange(smallCapsBehavior);
            }

            CaseMappingHarfBuzzBufferFiller(
                caseMapIntend,
                harfBuzzBuffer.get(),
                m_normalizedBuffer.get(),
                m_normalizedBufferLength,
                currentQueueItem.m_startIndex,
                currentQueueItem.m_numCharacters);

            CapsFeatureSettingsScopedOverlay capsOverlay(m_features, capsSupport.fontFeatureToUse(smallCapsBehavior));

            if (!shapeRange(harfBuzzBuffer.get(),
                currentQueueItem.m_startIndex,
                currentQueueItem.m_numCharacters,
                directionAndSmallCapsAdjustedFont,
                currentFontRangeSet,
                segmentRange.script,
                language))
                DLOG(ERROR) << "Shaping range failed.";

            if (!extractShapeResults(harfBuzzBuffer.get(),
                result.get(),
                fontCycleQueued,
                currentQueueItem,
                directionAndSmallCapsAdjustedFont,
                segmentRange.script,
                !fallbackIterator->hasNext()))
                DLOG(ERROR) << "Shape result extraction failed.";

            hb_buffer_reset(harfBuzzBuffer.get());
        }
    }
    return result.release();
}
const FontDataForRangeSet FontFallbackIterator::next(const Vector<UChar32>& hintList)
{
    if (m_fallbackStage == OutOfLuck)
        return FontDataForRangeSet();

    if (m_fallbackStage == FallbackPriorityFonts) {
        // Only try one fallback priority font,
        // then proceed to regular system fallback.
        m_fallbackStage = SystemFonts;
        FontDataForRangeSet fallbackPriorityFontRange(fallbackPriorityFont(hintList[0]));
        if (fallbackPriorityFontRange.hasFontData())
            return fallbackPriorityFontRange;
        return next(hintList);
    }

    if (m_fallbackStage == SystemFonts) {
        // We've reached pref + system fallback.
        ASSERT(hintList.size());
        RefPtr<SimpleFontData> systemFont = uniqueSystemFontForHint(hintList[0]);
        if (systemFont)
            return FontDataForRangeSet(systemFont);

        // If we don't have options from the system fallback anymore or had
        // previously returned them, we only have the last resort font left.
        // TODO: crbug.com/42217 Improve this by doing the last run with a last
        // resort font that has glyphs for everything, for example the Unicode
        // LastResort font, not just Times or Arial.
        FontCache* fontCache = FontCache::fontCache();
        m_fallbackStage = OutOfLuck;
        RefPtr<SimpleFontData> lastResort = fontCache->getLastResortFallbackFont(m_fontDescription).get();
        RELEASE_ASSERT(lastResort);
        return FontDataForRangeSet(lastResort);
    }

    ASSERT(m_fallbackStage == FontGroupFonts
        || m_fallbackStage == SegmentedFace);
    const FontData* fontData = m_fontFallbackList->fontDataAt(
        m_fontDescription, m_currentFontDataIndex);

    if (!fontData) {
        // If there is no fontData coming from the fallback list, it means
        // we are now looking at system fonts, either for prioritized symbol
        // or emoji fonts or by calling system fallback API.
        m_fallbackStage = isNonTextFallbackPriority(m_fontFallbackPriority)
            ? FallbackPriorityFonts
            : SystemFonts;
        return next(hintList);
    }

    // Otherwise we've received a fontData from the font-family: set of fonts,
    // and a non-segmented one in this case.
    if (!fontData->isSegmented()) {
        // Skip forward to the next font family for the next call to next().
        m_currentFontDataIndex++;
        if (!fontData->isLoading()) {
            RefPtr<SimpleFontData> nonSegmented = const_cast<SimpleFontData*>(toSimpleFontData(fontData));
            return FontDataForRangeSet(nonSegmented);
        }
        return next(hintList);
    }

    // Iterate over ranges of a segmented font below.

    const SegmentedFontData* segmented = toSegmentedFontData(fontData);
    if (m_fallbackStage != SegmentedFace) {
        m_segmentedFaceIndex = 0;
        m_fallbackStage = SegmentedFace;
    }

    ASSERT(m_segmentedFaceIndex < segmented->numFaces());
    FontDataForRangeSet currentSegmentedFace = segmented->faceAt(m_segmentedFaceIndex);
    m_segmentedFaceIndex++;

    if (m_segmentedFaceIndex == segmented->numFaces()) {
        // Switch from iterating over a segmented face to the next family from
        // the font-family: group of fonts.
        m_fallbackStage = FontGroupFonts;
        m_currentFontDataIndex++;
    }

    if (rangeSetContributesForHint(hintList, currentSegmentedFace)) {
        if (currentSegmentedFace.fontData()->customFontData())
            currentSegmentedFace.fontData()->customFontData()->beginLoadIfNeeded();
        if (!currentSegmentedFace.fontData()->isLoading())
            return currentSegmentedFace;
        m_trackedLoadingRangeSets.append(currentSegmentedFace);
    }

    return next(hintList);
}
PassRefPtr<ShapeResult> HarfBuzzShaper::shapeResult()
{
    RefPtr<ShapeResult> result = ShapeResult::create(m_font,
        m_normalizedBufferLength, m_textRun.direction());
    HarfBuzzScopedPtr<hb_buffer_t> harfBuzzBuffer(hb_buffer_create(), hb_buffer_destroy);

    const FontDescription& fontDescription = m_font->getFontDescription();
    const String& localeString = fontDescription.locale();
    CString locale = localeString.latin1();
    const hb_language_t language = hb_language_from_string(locale.data(), locale.length());

    RunSegmenter::RunSegmenterRange segmentRange = {
        0,
        0,
        USCRIPT_INVALID_CODE,
        OrientationIterator::OrientationInvalid,
        SmallCapsIterator::SmallCapsSameCase,
        FontFallbackPriority::Invalid };
    RunSegmenter runSegmenter(
        m_normalizedBuffer.get(),
        m_normalizedBufferLength,
        m_font->getFontDescription().orientation(),
        fontDescription.variant());

    Vector<UChar32> fallbackCharsHint;

    // TODO: Check whether this treatAsZerowidthspace from the previous script
    // segmentation plays a role here, does the new scriptRuniterator handle that correctly?
    while (runSegmenter.consume(&segmentRange)) {
        RefPtr<FontFallbackIterator> fallbackIterator =
            m_font->createFontFallbackIterator(
            segmentRange.fontFallbackPriority);

        appendToHolesQueue(HolesQueueNextFont, 0, 0);
        appendToHolesQueue(HolesQueueRange, segmentRange.start, segmentRange.end - segmentRange.start);

        const SimpleFontData* currentFont = nullptr;
        RefPtr<UnicodeRangeSet> currentFontRangeSet;

        bool fontCycleQueued = false;
        while (m_holesQueue.size()) {
            HolesQueueItem currentQueueItem = m_holesQueue.takeFirst();

            if (currentQueueItem.m_action == HolesQueueNextFont) {
                // For now, we're building a character list with which we probe
                // for needed fonts depending on the declared unicode-range of a
                // segmented CSS font. Alternatively, we can build a fake font
                // for the shaper and check whether any glyphs were found, or
                // define a new API on the shaper which will give us coverage
                // information?
                if (!collectFallbackHintChars(fallbackCharsHint, fallbackIterator->needsHintList())) {
                    // Give up shaping since we cannot retrieve a font fallback
                    // font without a hintlist.
                    m_holesQueue.clear();
                    break;
                }

                FontDataForRangeSet nextFontDataForRangeSet = fallbackIterator->next(fallbackCharsHint);
                currentFont = nextFontDataForRangeSet.fontData().get();
                currentFontRangeSet = nextFontDataForRangeSet.ranges();

                if (!currentFont) {
                    ASSERT(!m_holesQueue.size());
                    break;
                }
                fontCycleQueued = false;
                continue;
            }

            // TODO crbug.com/522964: Only use smallCapsFontData when the font does not support true smcp.  The spec
            // says: "To match the surrounding text, a font may provide alternate glyphs for caseless characters when
            // these features are enabled but when a user agent simulates small capitals, it must not attempt to
            // simulate alternates for codepoints which are considered caseless."
            const SimpleFontData* smallcapsAdjustedFont = segmentRange.smallCapsBehavior == SmallCapsIterator::SmallCapsUppercaseNeeded
                ? currentFont->smallCapsFontData(fontDescription).get()
                : currentFont;

            // Compatibility with SimpleFontData approach of keeping a flag for overriding drawing direction.
            // TODO: crbug.com/506224 This should go away in favor of storing that information elsewhere, for example in
            // ShapeResult.
            const SimpleFontData* directionAndSmallCapsAdjustedFont = fontDataAdjustedForOrientation(smallcapsAdjustedFont,
                m_font->getFontDescription().orientation(),
                segmentRange.renderOrientation);

            if (!shapeRange(harfBuzzBuffer.get(),
                currentQueueItem.m_startIndex,
                currentQueueItem.m_numCharacters,
                directionAndSmallCapsAdjustedFont,
                currentFontRangeSet,
                segmentRange.script,
                language))
                DLOG(ERROR) << "Shaping range failed.";

            if (!extractShapeResults(harfBuzzBuffer.get(),
                result.get(),
                fontCycleQueued,
                currentQueueItem,
                directionAndSmallCapsAdjustedFont,
                segmentRange.script,
                !fallbackIterator->hasNext()))
                DLOG(ERROR) << "Shape result extraction failed.";

            hb_buffer_reset(harfBuzzBuffer.get());
        }
    }
    return result.release();
}