blob: a0882f7c2cf8b23423bc7048a3896a7cca4ea048 [file] [log] [blame]
/*
* Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
* Copyright (C) 2012 Intel Corporation
*
* 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 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 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 "FontCascade.h"
#if USE(CAIRO)
#include "CharacterProperties.h"
#include "FontCache.h"
#include "SurrogatePairAwareTextIterator.h"
namespace WebCore {
bool FontCascade::canReturnFallbackFontsForComplexText()
{
return false;
}
bool FontCascade::canExpandAroundIdeographsInComplexText()
{
return false;
}
static bool characterSequenceIsEmoji(SurrogatePairAwareTextIterator& iterator, UChar32 firstCharacter, unsigned firstClusterLength)
{
UChar32 character = firstCharacter;
unsigned clusterLength = firstClusterLength;
if (!iterator.consume(character, clusterLength))
return false;
if (isEmojiKeycapBase(character)) {
iterator.advance(clusterLength);
UChar32 nextCharacter;
if (!iterator.consume(nextCharacter, clusterLength))
return false;
if (nextCharacter == combiningEnclosingKeycap)
return true;
// Variation selector 16.
if (nextCharacter == 0xFE0F) {
iterator.advance(clusterLength);
if (!iterator.consume(nextCharacter, clusterLength))
return false;
if (nextCharacter == combiningEnclosingKeycap)
return true;
}
return false;
}
// Regional indicator.
if (isEmojiRegionalIndicator(character)) {
iterator.advance(clusterLength);
UChar32 nextCharacter;
if (!iterator.consume(nextCharacter, clusterLength))
return false;
if (isEmojiRegionalIndicator(nextCharacter))
return true;
return false;
}
if (character == combiningEnclosingKeycap)
return true;
if (isEmojiWithPresentationByDefault(character)
|| isEmojiModifierBase(character)
|| isEmojiFitzpatrickModifier(character))
return true;
return false;
}
const Font* FontCascade::fontForCombiningCharacterSequence(const UChar* originalCharacters, size_t originalLength) const
{
auto normalizedString = normalizedNFC(StringView { originalCharacters, static_cast<unsigned>(originalLength) });
// Code below relies on normalizedNFC never narrowing a 16-bit input string into an 8-bit output string.
// At the time of this writing, the function never does this, but in theory a future version could, and
// we would then need to add code paths here for the simpler 8-bit case.
auto characters = normalizedString.view.characters16();
auto length = normalizedString.view.length();
UChar32 character;
unsigned clusterLength = 0;
SurrogatePairAwareTextIterator iterator(characters, 0, length, length);
if (!iterator.consume(character, clusterLength))
return nullptr;
bool isEmoji = characterSequenceIsEmoji(iterator, character, clusterLength);
bool preferColoredFont = isEmoji;
// U+FE0E forces text style.
// U+FE0F forces emoji style.
if (characters[length - 1] == 0xFE0E)
preferColoredFont = false;
else if (characters[length - 1] == 0xFE0F)
preferColoredFont = true;
const Font* baseFont = glyphDataForCharacter(character, false, NormalVariant).font;
if (baseFont
&& (clusterLength == length || baseFont->canRenderCombiningCharacterSequence(characters, length))
&& (!preferColoredFont || baseFont->platformData().isColorBitmapFont()))
return baseFont;
for (unsigned i = 0; !fallbackRangesAt(i).isNull(); ++i) {
const Font* fallbackFont = fallbackRangesAt(i).fontForCharacter(character);
if (!fallbackFont || fallbackFont == baseFont)
continue;
if (fallbackFont->canRenderCombiningCharacterSequence(characters, length) && (!preferColoredFont || fallbackFont->platformData().isColorBitmapFont()))
return fallbackFont;
}
if (auto systemFallback = FontCache::singleton().systemFallbackForCharacters(m_fontDescription, baseFont, IsForPlatformFont::No, preferColoredFont ? FontCache::PreferColoredFont::Yes : FontCache::PreferColoredFont::No, characters, length)) {
if (systemFallback->canRenderCombiningCharacterSequence(characters, length) && (!preferColoredFont || systemFallback->platformData().isColorBitmapFont()))
return systemFallback.get();
// In case of emoji, if fallback font is colored try again without the variation selector character.
if (isEmoji && characters[length - 1] == 0xFE0F && systemFallback->platformData().isColorBitmapFont() && systemFallback->canRenderCombiningCharacterSequence(characters, length - 1))
return systemFallback.get();
}
return baseFont;
}
} // namespace WebCore
#endif // USE(CAIRO)