1/*
2 * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
3 * Copyright (C) 2012 Intel Corporation
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include "config.h"
28#include "FontCascade.h"
29
30#if USE(CAIRO)
31
32#include "CharacterProperties.h"
33#include "FontCache.h"
34#include "SurrogatePairAwareTextIterator.h"
35
36namespace WebCore {
37
38bool FontCascade::canReturnFallbackFontsForComplexText()
39{
40 return false;
41}
42
43bool FontCascade::canExpandAroundIdeographsInComplexText()
44{
45 return false;
46}
47
48static bool characterSequenceIsEmoji(SurrogatePairAwareTextIterator& iterator, UChar32 firstCharacter, unsigned firstClusterLength)
49{
50 UChar32 character = firstCharacter;
51 unsigned clusterLength = firstClusterLength;
52 if (!iterator.consume(character, clusterLength))
53 return false;
54
55 if (isEmojiKeycapBase(character)) {
56 iterator.advance(clusterLength);
57 UChar32 nextCharacter;
58 if (!iterator.consume(nextCharacter, clusterLength))
59 return false;
60
61 if (nextCharacter == combiningEnclosingKeycap)
62 return true;
63
64 // Variation selector 16.
65 if (nextCharacter == 0xFE0F) {
66 iterator.advance(clusterLength);
67 if (!iterator.consume(nextCharacter, clusterLength))
68 return false;
69
70 if (nextCharacter == combiningEnclosingKeycap)
71 return true;
72 }
73
74 return false;
75 }
76
77 // Regional indicator.
78 if (isEmojiRegionalIndicator(character)) {
79 iterator.advance(clusterLength);
80 UChar32 nextCharacter;
81 if (!iterator.consume(nextCharacter, clusterLength))
82 return false;
83
84 if (isEmojiRegionalIndicator(nextCharacter))
85 return true;
86
87 return false;
88 }
89
90 if (character == combiningEnclosingKeycap)
91 return true;
92
93 if (isEmojiWithPresentationByDefault(character)
94 || isEmojiModifierBase(character)
95 || isEmojiFitzpatrickModifier(character))
96 return true;
97
98 return false;
99}
100
101const Font* FontCascade::fontForCombiningCharacterSequence(const UChar* originalCharacters, size_t originalLength) const
102{
103 auto normalizedString = normalizedNFC(StringView { originalCharacters, static_cast<unsigned>(originalLength) });
104
105 // Code below relies on normalizedNFC never narrowing a 16-bit input string into an 8-bit output string.
106 // At the time of this writing, the function never does this, but in theory a future version could, and
107 // we would then need to add code paths here for the simpler 8-bit case.
108 auto characters = normalizedString.view.characters16();
109 auto length = normalizedString.view.length();
110
111 UChar32 character;
112 unsigned clusterLength = 0;
113 SurrogatePairAwareTextIterator iterator(characters, 0, length, length);
114 if (!iterator.consume(character, clusterLength))
115 return nullptr;
116
117 bool isEmoji = characterSequenceIsEmoji(iterator, character, clusterLength);
118 bool preferColoredFont = isEmoji;
119 // U+FE0E forces text style.
120 // U+FE0F forces emoji style.
121 if (characters[length - 1] == 0xFE0E)
122 preferColoredFont = false;
123 else if (characters[length - 1] == 0xFE0F)
124 preferColoredFont = true;
125
126 const Font* baseFont = glyphDataForCharacter(character, false, NormalVariant).font;
127 if (baseFont
128 && (clusterLength == length || baseFont->canRenderCombiningCharacterSequence(characters, length))
129 && (!preferColoredFont || baseFont->platformData().isColorBitmapFont()))
130 return baseFont;
131
132 for (unsigned i = 0; !fallbackRangesAt(i).isNull(); ++i) {
133 const Font* fallbackFont = fallbackRangesAt(i).fontForCharacter(character);
134 if (!fallbackFont || fallbackFont == baseFont)
135 continue;
136
137 if (fallbackFont->canRenderCombiningCharacterSequence(characters, length) && (!preferColoredFont || fallbackFont->platformData().isColorBitmapFont()))
138 return fallbackFont;
139 }
140
141 if (auto systemFallback = FontCache::singleton().systemFallbackForCharacters(m_fontDescription, baseFont, IsForPlatformFont::No, preferColoredFont ? FontCache::PreferColoredFont::Yes : FontCache::PreferColoredFont::No, characters, length)) {
142 if (systemFallback->canRenderCombiningCharacterSequence(characters, length) && (!preferColoredFont || systemFallback->platformData().isColorBitmapFont()))
143 return systemFallback.get();
144
145 // In case of emoji, if fallback font is colored try again without the variation selector character.
146 if (isEmoji && characters[length - 1] == 0xFE0F && systemFallback->platformData().isColorBitmapFont() && systemFallback->canRenderCombiningCharacterSequence(characters, length - 1))
147 return systemFallback.get();
148 }
149
150 return baseFont;
151}
152
153} // namespace WebCore
154
155#endif // USE(CAIRO)
156