/*
 * Copyright (C) 2006 Apple Computer, Inc.  All rights reserved.
 *
 * 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. 
 * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
 *     its contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission. 
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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 "FontGlyphs.h"

#include "Font.h"
#include "FontCache.h"
#include "GlyphPage.h"
#include <wtf/HexNumber.h>
#include <wtf/text/StringBuilder.h>
#include <wtf/unicode/Unicode.h>

namespace WebCore {

class MixedFontGlyphPage : public RefCounted<MixedFontGlyphPage> {
public:
    MixedFontGlyphPage(const GlyphPage* initialPage)
    {
        if (initialPage) {
            for (unsigned i = 0; i < GlyphPage::size; ++i)
                setGlyphDataForIndex(i, initialPage->glyphDataForIndex(i));
        }
        else {
            memset(m_glyphs, 0, sizeof(m_glyphs));
            memset(m_fonts, 0, sizeof(m_fonts));
        }
    }

    ALWAYS_INLINE GlyphData glyphDataForCharacter(UChar32 c) const
    {
        unsigned index = GlyphPage::indexForCodePoint(c);
        ASSERT_WITH_SECURITY_IMPLICATION(index < GlyphPage::size);
        return GlyphData(m_glyphs[index], m_fonts[index]);
    }

    void setGlyphDataForCharacter(UChar32 c, GlyphData glyphData)
    {
        setGlyphDataForIndex(GlyphPage::indexForCodePoint(c), glyphData);
    }

private:
    void setGlyphDataForIndex(unsigned index, const GlyphData& glyphData)
    {
        ASSERT_WITH_SECURITY_IMPLICATION(index < GlyphPage::size);
        m_glyphs[index] = glyphData.glyph;
        m_fonts[index] = glyphData.fontData;
    }

    Glyph m_glyphs[GlyphPage::size];
    const SimpleFontData* m_fonts[GlyphPage::size];
};

GlyphData FontGlyphs::GlyphPageCacheEntry::glyphDataForCharacter(UChar32 character)
{
    ASSERT(!(m_singleFont && m_mixedFont));
    if (m_singleFont)
        return m_singleFont->glyphDataForCharacter(character);
    if (m_mixedFont)
        return m_mixedFont->glyphDataForCharacter(character);
    return 0;
}

void FontGlyphs::GlyphPageCacheEntry::setGlyphDataForCharacter(UChar32 character, GlyphData glyphData)
{
    ASSERT(!glyphDataForCharacter(character).glyph);
    if (!m_mixedFont) {
        m_mixedFont = adoptRef(new MixedFontGlyphPage(m_singleFont.get()));
        m_singleFont = 0;
    }
    m_mixedFont->setGlyphDataForCharacter(character, glyphData);
}

void FontGlyphs::GlyphPageCacheEntry::setSingleFontPage(PassRefPtr<GlyphPage> page)
{
    ASSERT(isNull());
    m_singleFont = page;
}

FontGlyphs::FontGlyphs(PassRefPtr<FontSelector> fontSelector)
: m_fontCache(fontCache())
, m_lastRealizedFallbackIndex(0)
, m_cachedPrimarySimpleFontData(0)
, m_fontSelector(fontSelector)
, m_fontSelectorVersion(m_fontSelector ? m_fontSelector->version() : 0)
, m_generation(m_fontCache.generation())
, m_pitch(UnknownPitch)
, m_isForPlatformFont(false)
{
}

FontGlyphs::FontGlyphs(const FontPlatformData& platformData)
: m_fontCache(fontCache())
, m_lastRealizedFallbackIndex(0)
, m_cachedPrimarySimpleFontData(0)
, m_fontSelectorVersion(0)
, m_generation(m_fontCache.generation())
, m_pitch(UnknownPitch)
, m_isForPlatformFont(true)
{
	m_realizedFallbackRanges.append(FontRanges(fontCache().fontForPlatformData(platformData)));
}

FontGlyphs::~FontGlyphs()
{
	fontCache().schedulePurgeInactiveFontData();
}

void FontGlyphs::determinePitch(const FontDescription& description)
{
	auto& primaryRanges = realizeFallbackRangesAt(description, 0);
	unsigned numRanges = primaryRanges.size();
	if (numRanges == 1)
		m_pitch = primaryRanges.rangeAt(0).fontData().pitch();
	else
		m_pitch = VariablePitch;
}

bool FontGlyphs::isLoadingCustomFonts() const
{
	for(int i = 0; i < m_realizedFallbackRanges.size(); ++i) {
		const FontRanges& fontRanges = m_realizedFallbackRanges[i];
        if (fontRanges.isLoading())
            return true;
    }
    return false;
}

static FontRanges realizeNextFallback(const FontDescription& description, unsigned& index, FontSelector* fontSelector)
{
    ASSERT(index < description.familyCount());

    while (index < description.familyCount()) {
        const AtomicString& family = description.familyAt(index++);
        if (family.isEmpty())
            continue;
        if (fontSelector) {
            auto ranges = fontSelector->fontRangesForFamily(description, family);
            if (!ranges.isNull())
                return ranges;
        }
        if (auto font = fontCache().fontForFamily(description, family))
            return FontRanges(font);
    }
    // We didn't find a font. Try to find a similar font using our own specific knowledge about our platform.
    // For example on OS X, we know to map any families containing the words Arabic, Pashto, or Urdu to the
    // Geeza Pro font.
    return FontRanges(fontCache().similarFontPlatformData(description));
}

const FontRanges& FontGlyphs::realizeFallbackRangesAt(const FontDescription& description, unsigned index)
{
    if (index < m_realizedFallbackRanges.size())
        return m_realizedFallbackRanges[index];

    ASSERT(index == m_realizedFallbackRanges.size());
    ASSERT(fontCache().generation() == m_generation);

    m_realizedFallbackRanges.append(FontRanges());
    auto& fontRanges = m_realizedFallbackRanges.last();

    if (!index) {
        fontRanges = realizeNextFallback(description, m_lastRealizedFallbackIndex, m_fontSelector.get());
        if (fontRanges.isNull() && m_fontSelector)
            fontRanges = m_fontSelector->fontRangesForFamily(description, standardFamily);
        if (fontRanges.isNull())
            fontRanges = FontRanges(fontCache().lastResortFallbackFont(description));
        return fontRanges;
    }

    if (m_lastRealizedFallbackIndex < description.familyCount())
        fontRanges = realizeNextFallback(description, m_lastRealizedFallbackIndex, m_fontSelector.get());

    if (fontRanges.isNull() && m_fontSelector) {
        ASSERT(m_lastRealizedFallbackIndex >= description.familyCount());

        unsigned fontSelectorFallbackIndex = m_lastRealizedFallbackIndex - description.familyCount();
        if (fontSelectorFallbackIndex == m_fontSelector->fallbackFontDataCount())
            return fontRanges;
        ++m_lastRealizedFallbackIndex;
        fontRanges = FontRanges(m_fontSelector->fallbackFontDataAt(description, fontSelectorFallbackIndex));
    }

    return fontRanges;
}

/**
 * Finds a visually similar character to use in place of the given character.
 * Returns 0 if no character could be found.
 * The tryAgain flag will be set to true if we should search again for
 * another fallback character if the given fallback is not found.
 */
static UChar32 getFallbackCharacter(UChar32 c)
{
    // map full-width punctuation and latin characters to their
    // ascii counterparts
    if (c >= 0xff01 && c <= 0xff5e)
        return c - 0xfee0;

    // if we lack support for a combining character, better to leave it
    // off than to show a box
    if (c >= 0x0300 && c <= 0x036f)
        return zeroWidthSpace;
    if (c >= 0x1dc0 && c <= 0x1dff)
        return zeroWidthSpace;
    if (c >= 0x20d0 && c <= 0x20ff)
        return zeroWidthSpace;

    switch (c) {
        // note: many of these fallbacks come from the unicode standard
        // version 6.1 "general punctuation" range 0x2000-0x206f

        // all non-rendering control characters should map to zero width space
        case 0:
        case 0xfffe: // byte-order marker, should not occur in text!
        case 0x2028: // line separator
        case 0x2029: // paragraph separator
        case 0x2060: // word joiner
        case 0x2061: // function application
        case 0x2062: // invisible times
        case 0x2063: // invisible separator
        case 0x2064: // invisible plus
        case 0x206a: // inhibit symmetric swapping
        case 0x206b: // activate symmetric swapping
        case 0x206c: // inhibit arabic form shaping
        case 0x206d: // activate arabic form shaping
        case 0x206e: // national digit shapes
        case 0x206f: // nominal digit shapes
            return zeroWidthSpace;

        case 0x30fb: // katakana middle dot
        case 0x180e: // mongolian vowel separator
        case 0x2022: // bullet
        case 0x2219: // bullet operator
        case 0x2027: // hyphenation point
            return 0x00b7; // middle dot

        case 0x00ad: // soft hyphen
        case 0x2010: // hyphen
        case 0x2011: // non-breaking hyphen
        case 0x2012: // figure dash
        case 0x2013: // en dash
        case 0x2014: // em dash
        case 0x2015: // horizontal bar
        case 0x30fc: // katakana-hiragana prolonged sound mark
        case 0x1680: // ogham space mark
        case 0xfe58: // small em dash
        case 0xfe63: // small hyphen-minus
            return '-'; // hyphen-minus

        case 0x30a0: // katakana-hiragana double hyphen
        case 0x1400: // canadian syllabics hyphen
            return '='; // equal sign

        case 0x00a0: // non-break space
        case 0x2000: // en quad
        case 0x2001: // em quad
        case 0x2002: // en space
        case 0x2003: // em space
        case 0x2004: // three-per-em space
        case 0x2005: // four-per-em space
        case 0x2006: // six-per-em space
        case 0x2007: // figure space
        case 0x2008: // punctuation space
        case 0x2009: // thin space
        case 0x200a: // hair space
        case 0x202f: // narrow no-break space
        case 0x205f: // medium mathematical space
        case 0x3000: // ideographic space
        case 0xffa0: // halfwidth hangul filler
        case 0xfff9: // interlinear annotation anchor
        case 0xfffa: // interlinear annotation separator
        case 0xfffb: // interlinear annotation terminator
            return ' '; // space

        case 0x00b4: // acute accent
        case 0x02b9: // modifier letter prime
        case 0x02bd: // modifier letter reversed comma
        case 0x2032: // prime
        case 0x2035: // reversed prime
        case 0x2018: // left single quotation mark
        case 0x2019: // right single quotation mark
        case 0x201b: // single high-reversed-9 quotation mark
            return '\''; // apostrophe

        case 0x201a: // single low-9 quotation mark
            return ','; // comma

        case 0x201c: // left double quotation mark
        case 0x201d: // right double quotation mark
        case 0x201e: // double low-9 quotation mark
        case 0x201f: // double high-reversed-9 quotation mark
        case 0x2033: // double prime
        case 0x2036: // reversed double prime
        case 0x275d: // heavy double turned comma quotation mark ornament
        case 0x275e: // heavy double comma quotation mark ornament
        case 0x301e: // double prime quotation mark
            return '"'; // quotation mark

        case 0x301c: // WAVE DASH
        	return 0xFF5E; // FULLWIDTH TILDE

        case 0x2024: // one dot leader
        case 0x3002: // ideographic full stop
        case 0xff61: // halfwidth ideographic full stop
            return '.'; // full stop

        case 0x2038: // caret
        case 0x2303: // up arrowhead
            return '^'; // circumflex accent

        case 0x2053: // swung dash
            return '~'; // tilde

        case 0x2039: // single left-pointing angle quotation mark
        case 0x2329: // left-pointing angle bracket
        case 0x3008: // left angle bracket
            return '<'; // less-than sign

        case 0x203a: // single right-pointing angle quotation mark
        case 0x232a: // right-pointing angle bracket
        case 0x3009: // right angle bracket
            return '>'; // greater-than sign

        case 0x2044: // fraction slash
        case 0x2215: // division slash
        case 0x29f8: // big solidus
            return '/'; // solidus

        case 0x2017: // double low line
            return '_'; // low line

        case 0x204f: // reversed semicolon
            return ';'; // semicolon

        case 0x02d0: // modifier letter triangular colon
        case 0x05c3: // hebrew punctuation sof pasuq
        case 0x2236: // ratio
            return ':'; // colon

        case 0x2e18: // inverted interrobang
            return 0x00bf; // inverted question mark

        case 0x203d: // interrobang
        case 0x00bf: // inverted question mark
            return '?'; // question mark

        case 0x2052: // commercial minus sign
        case 0x066a: // arabic percent sign
            return '%'; // percent sign

        case 0x204a: // tironian sign et
            return '&'; // ampersand

        case 0x204b: // reversed pilcrow sign
            return 0x00b6; // pilcrow sign

        case 0x204e: // low asterisk
        case 0x2055: // flower punctuation mark
        case 0x274b: // heavy eight teardrop-spoked propeller asterisk
            return '*'; // asterisk

        case 0x01c3: // latin letter retroflex click
            return '!'; // exclamation mark

        case 0xffe0: // fullwidth cent sign
            return 0x00a2; // cent sign

        case 0xffe1: // fullwidth pound sign
            return 0x00a3; // pound sign

        case 0xffe5: // fullwidth yen sign
            return 0x00a5; // yen sign

        case 0xffe6: // fullwidth won sign
            return 0x20a9; // won sign

        default:
            // no fallback character found
            return 0;
    }
}

static inline bool isInRange(UChar32 character, UChar32 lowerBound, UChar32 upperBound)
{
    return character >= lowerBound && character <= upperBound;
}

static bool shouldIgnoreRotation(UChar32 character)
{
    if (character == 0x000A7 || character == 0x000A9 || character == 0x000AE)
        return true;

    if (character == 0x000B6 || character == 0x000BC || character == 0x000BD || character == 0x000BE)
        return true;

    if (isInRange(character, 0x002E5, 0x002EB))
        return true;

    if (isInRange(character, 0x01100, 0x011FF) || isInRange(character, 0x01401, 0x0167F) || isInRange(character, 0x01800, 0x018FF))
        return true;

    if (character == 0x02016 || character == 0x02020 || character == 0x02021 || character == 0x2030 || character == 0x02031)
        return true;

    if (isInRange(character, 0x0203B, 0x0203D) || character == 0x02042 || character == 0x02044 || character == 0x02047
        || character == 0x02048 || character == 0x02049 || character == 0x2051)
        return true;

    if (isInRange(character, 0x02065, 0x02069) || isInRange(character, 0x020DD, 0x020E0)
        || isInRange(character, 0x020E2, 0x020E4) || isInRange(character, 0x02100, 0x02117)
        || isInRange(character, 0x02119, 0x02131) || isInRange(character, 0x02133, 0x0213F))
        return true;

    if (isInRange(character, 0x02145, 0x0214A) || character == 0x0214C || character == 0x0214D
        || isInRange(character, 0x0214F, 0x0218F))
        return true;

    if (isInRange(character, 0x02300, 0x02307) || isInRange(character, 0x0230C, 0x0231F)
        || isInRange(character, 0x02322, 0x0232B) || isInRange(character, 0x0237D, 0x0239A)
        || isInRange(character, 0x023B4, 0x023B6) || isInRange(character, 0x023BA, 0x023CF)
        || isInRange(character, 0x023D1, 0x023DB) || isInRange(character, 0x023E2, 0x024FF))
        return true;

    if (isInRange(character, 0x025A0, 0x02619) || isInRange(character, 0x02620, 0x02767)
        || isInRange(character, 0x02776, 0x02793) || isInRange(character, 0x02B12, 0x02B2F)
        || isInRange(character, 0x02B4D, 0x02BFF) || isInRange(character, 0x02E80, 0x03007))
        return true;

    if (character == 0x03012 || character == 0x03013 || isInRange(character, 0x03020, 0x0302F)
        || isInRange(character, 0x03031, 0x0309F) || isInRange(character, 0x030A1, 0x030FB)
        || isInRange(character, 0x030FD, 0x0A4CF))
        return true;

    if (isInRange(character, 0x0A840, 0x0A87F) || isInRange(character, 0x0A960, 0x0A97F)
        || isInRange(character, 0x0AC00, 0x0D7FF) || isInRange(character, 0x0E000, 0x0FAFF))
        return true;

    if (isInRange(character, 0x0FE10, 0x0FE1F) || isInRange(character, 0x0FE30, 0x0FE48)
        || isInRange(character, 0x0FE50, 0x0FE57) || isInRange(character, 0x0FE5F, 0x0FE62)
        || isInRange(character, 0x0FE67, 0x0FE6F))
        return true;

    if (isInRange(character, 0x0FF01, 0x0FF07) || isInRange(character, 0x0FF0A, 0x0FF0C)
        || isInRange(character, 0x0FF0E, 0x0FF19) || character == 0x0FF1B || isInRange(character, 0x0FF1F, 0x0FF3A))
        return true;

    if (character == 0x0FF3C || character == 0x0FF3E)
        return true;

    if (isInRange(character, 0x0FF40, 0x0FF5A) || isInRange(character, 0x0FFE0, 0x0FFE2)
        || isInRange(character, 0x0FFE4, 0x0FFE7) || isInRange(character, 0x0FFF0, 0x0FFF8)
        || character == 0x0FFFD)
        return true;

    if (isInRange(character, 0x13000, 0x1342F) || isInRange(character, 0x1B000, 0x1B0FF)
        || isInRange(character, 0x1D000, 0x1D1FF) || isInRange(character, 0x1D300, 0x1D37F)
        || isInRange(character, 0x1F000, 0x1F64F) || isInRange(character, 0x1F680, 0x1F77F))
        return true;

    if (isInRange(character, 0x20000, 0x2FFFD) || isInRange(character, 0x30000, 0x3FFFD))
        return true;

    return false;
}

static inline GlyphData glyphDataForNonCJKCharacterWithGlyphOrientation(UChar32 character, NonCJKGlyphOrientation orientation, GlyphData& data)
{
    bool syntheticOblique = data.fontData->platformData().syntheticOblique();
    if (orientation == NonCJKGlyphOrientation::Upright || shouldIgnoreRotation(character)) {
    	GlyphData uprightData = data.fontData->uprightOrientationFontData()->glyphDataForCharacter(character);
    	// If the glyphs are the same, then we know we can just use the horizontal glyph rotated vertically
		// to be upright. For synthetic oblique, however, we will always return the uprightData to ensure
		// that non-CJK and CJK runs are broken up. This guarantees that vertical
		// fonts without isTextOrientationFallback() set contain CJK characters only and thus we can get
		// the oblique slant correct.
		if (data.glyph == uprightData.glyph && !syntheticOblique)
			return data;
		// The glyphs are distinct, meaning that the font has a vertical-right glyph baked into it. We can't use that
		// glyph, so we fall back to the upright data and use the horizontal glyph.
		if (uprightData.fontData)
			return uprightData;
    }
    else if (orientation == NonCJKGlyphOrientation::Mixed) {
    	GlyphData verticalRightData = data.fontData->verticalRightOrientationFontData()->glyphDataForCharacter(character);

        // If there is a baked-in rotated glyph, we will use it unless syntheticOblique is set. If
        // synthetic oblique is set, we fall back to the horizontal glyph. This guarantees that vertical
        // fonts without isTextOrientationFallback() set contain CJK characters only and thus we can get
        // the oblique slant correct.
        if (data.glyph != verticalRightData.glyph && !syntheticOblique)
            return data;

		// The glyphs are identical, meaning that we should just use the horizontal glyph.
		if (verticalRightData.fontData)
			return verticalRightData;
    }
    return data;
}

GlyphData FontGlyphs::glyphDataForSystemFallback(UChar32 c, const FontDescription& description, FontDataVariant variant, bool useSimilarChar)
{
    if(knownMissingGlyphMap().contains(c)) {
        if(UChar32 missingCharacter = m_fontCache.missingCharacter(c))
        	return glyphDataForCharacter(missingCharacter, description, variant, false);
    	return GlyphData();
    }

#if 0
    {
		StringBuilder msg;
		msg.append("FontGlyphs::glyphDataForSystemFallback ");
		appendUnsignedAsHexFixedSize(c, msg, 4);
		WTF::logMessage(msg.toString());
	}
#endif

    // System fallback is character-dependent.
    auto& primaryRanges = realizeFallbackRangesAt(description, 0);
    auto* originalFontData = primaryRanges.fontDataForCharacter(c);
    if (!originalFontData)
        originalFontData = &primaryRanges.fontDataForFirstRange();

    if(RefPtr<SimpleFontData> systemFallbackFontData = originalFontData->systemFallbackFontDataForCharacter(c, description, m_isForPlatformFont)) {
		if (systemFallbackFontData->platformData().orientation() == Vertical && !systemFallbackFontData->hasVerticalGlyphs() && Font::isCJKIdeographOrSymbol(c))
			variant = BrokenIdeographVariant;

		GlyphData fallbackGlyphData;
		if (variant == NormalVariant)
			fallbackGlyphData = systemFallbackFontData->glyphDataForCharacter(c);
		else
			fallbackGlyphData = systemFallbackFontData->variantFontData(description, variant)->glyphDataForCharacter(c);

	    if (fallbackGlyphData.fontData && fallbackGlyphData.fontData->platformData().orientation() == Vertical && !fallbackGlyphData.fontData->isTextOrientationFallback()) {
	        if (variant == NormalVariant && !Font::isCJKIdeographOrSymbol(c))
	            fallbackGlyphData = glyphDataForNonCJKCharacterWithGlyphOrientation(c, description.nonCJKGlyphOrientation(), fallbackGlyphData);
	    }

		// Keep the system fallback fonts we use alive.
		if (fallbackGlyphData.glyph) {
			m_systemFallbackFontDataSet.add(systemFallbackFontData.release());
			return fallbackGlyphData;
		}
    }

    // Glyph was not found in any font. Try to replace it with a visually similar character.
    if(useSimilarChar) {
    	// user defined mapping should check first
        FontCharReplaceMap::const_iterator it = m_fontCache.similarCharacters().find(c);
		if(it != m_fontCache.similarCharacters().end()) {
			GlyphData data = glyphDataForCharacter(it->second, description, variant, false);
            if(data.glyph)
            	return data;
		}

		// try default substitute
        UChar32 replacement = getFallbackCharacter(c);
        if (replacement == zeroWidthSpace)
            return GlyphData(); // non-rendering

        if (replacement) {
        	GlyphData data = glyphDataForCharacter(replacement, description, variant, false);
            if(data.glyph)
            	return data;
        }
    }

    // do not grow too big
    if(knownMissingGlyphMap().size() >= 1024)
    	knownMissingGlyphMap().clear();

    knownMissingGlyphMap().add(c);

    if(UChar32 missingCharacter = m_fontCache.missingCharacter(c))
    	return glyphDataForCharacter(missingCharacter, description, variant, false);

    return GlyphData();
}

GlyphData FontGlyphs::glyphDataForVariant(UChar32 c, const FontDescription& description, FontDataVariant variant, bool useSimilarChar, unsigned fallbackIndex)
{
    while (true) {
        auto& fontRanges = realizeFallbackRangesAt(description, fallbackIndex++);
        if (fontRanges.isNull())
            break;
        GlyphData data = fontRanges.glyphDataForCharacter(c);
		if (data.fontData) {
			// The variantFontData function should not normally return 0.
			// But if it does, we will just render the capital letter big.
			RefPtr<SimpleFontData> variantFontData = data.fontData->variantFontData(description, variant);
			if (!variantFontData)
				return data;

			return variantFontData->glyphDataForCharacter(c);
		}
	}

    return glyphDataForSystemFallback(c, description, variant, useSimilarChar);
}

GlyphData FontGlyphs::glyphDataForNormalVariant(UChar32 c, const FontDescription& description, bool useSimilarChar)
{
    for (unsigned fallbackIndex = 0; ; ++fallbackIndex) {
        auto& fontRanges = realizeFallbackRangesAt(description, fallbackIndex);
        if (fontRanges.isNull())
            break;
        GlyphData data = fontRanges.glyphDataForCharacter(c);
        if (data.fontData) {
            if (data.fontData->platformData().orientation() == Vertical && !data.fontData->isTextOrientationFallback()) {
                if (!Font::isCJKIdeographOrSymbol(c))
                    return glyphDataForNonCJKCharacterWithGlyphOrientation(c, description.nonCJKGlyphOrientation(), data);

                if (!data.fontData->hasVerticalGlyphs()) {
                    // Use the broken ideograph font data. The broken ideograph font will use the horizontal width of glyphs
                    // to make sure you get a square (even for broken glyphs like symbols used for punctuation).
                    return glyphDataForVariant(c, description, BrokenIdeographVariant, useSimilarChar, fallbackIndex);
                }
            }
            return data;
        }
    }

    return glyphDataForSystemFallback(c, description, NormalVariant, useSimilarChar);
}

static RefPtr<GlyphPage> glyphPageFromFontRanges(unsigned pageNumber, const FontRanges& fontRanges)
{
    const SimpleFontData* simpleFontData = 0;
	UChar32 pageRangeFrom = pageNumber * GlyphPage::size;
	UChar32 pageRangeTo = pageRangeFrom + GlyphPage::size - 1;
	for (unsigned i = 0; i < fontRanges.size(); ++i) {
		auto& range = fontRanges.rangeAt(i);
		if (range.to()) {
			if (range.from() <= pageRangeFrom && pageRangeTo <= range.to())
				simpleFontData = &range.fontData();
			break;
		}
	}
	if (!simpleFontData)
		return nullptr;

    if (simpleFontData->platformData().orientation() == Vertical)
        return nullptr;

    return const_cast<GlyphPage*>(simpleFontData->glyphPage(pageNumber));
}

GlyphData FontGlyphs::glyphDataForCharacter(UChar32 c, const FontDescription& description, FontDataVariant variant, bool useSimilarChar)
{
    ASSERT(isMainThread());
    ASSERT(variant != AutoVariant);

    if (variant != NormalVariant)
        return glyphDataForVariant(c, description, variant, useSimilarChar, 0);

    const unsigned pageNumber = GlyphPage::pageNumberForCodePoint(c);

    GlyphPageCacheEntry& cacheEntry = pageNumber ? m_cachedPages.add(pageNumber, GlyphPageCacheEntry()).iterator->second : m_cachedPageZero;

    // Initialize cache with a full page of glyph mappings from a single font.
    if (cacheEntry.isNull())
        cacheEntry.setSingleFontPage(glyphPageFromFontRanges(pageNumber, realizeFallbackRangesAt(description, 0)));

    GlyphData glyphData = cacheEntry.glyphDataForCharacter(c);
    if (!glyphData.glyph) {
        // No glyph, resolve per-character.
        glyphData = glyphDataForNormalVariant(c, description, useSimilarChar);
        // Cache the results.
        cacheEntry.setGlyphDataForCharacter(c, glyphData);
    }

    return glyphData;
}

#if 0
void FontGlyphs::pruneSystemFallbacks()
{
    if (m_systemFallbackFontSet.isEmpty())
        return;
    // Mutable glyph pages may reference fallback fonts.
    if (m_cachedPageZero.isMixedFont())
        m_cachedPageZero = { };
    m_cachedPages.removeIf([](decltype(m_cachedPages)::KeyValuePairType& keyAndValue) {
        return keyAndValue.value.isMixedFont();
    });
    m_systemFallbackFontSet.clear();
}
#endif

}
