#include "config.h"
#include "platform/fonts/Font.h"
+#include "platform/LayoutUnit.h"
+#include "platform/fonts/Character.h"
+#include "platform/fonts/FontCache.h"
+#include "platform/fonts/FontFallbackList.h"
+#include "platform/fonts/FontPlatformFeatures.h"
+#include "platform/fonts/GlyphBuffer.h"
+#include "platform/fonts/GlyphPageTreeNode.h"
+#include "platform/fonts/SimpleFontData.h"
#include "platform/fonts/WidthIterator.h"
#include "platform/geometry/FloatRect.h"
#include "platform/text/TextRun.h"
#include "wtf/MainThread.h"
#include "wtf/StdLibExtras.h"
-#include "wtf/text/StringBuilder.h"
+#include "wtf/unicode/CharacterNames.h"
+#include "wtf/unicode/Unicode.h"
using namespace WTF;
using namespace Unicode;
+using namespace std;
namespace WebCore {
-const uint8_t Font::s_roundingHackCharacterTable[256] = {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 /*\t*/, 1 /*\n*/, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 1 /*space*/, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 /*-*/, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 /*?*/,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 1 /*no-break space*/, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
-};
-
-static const UChar32 cjkIsolatedSymbolsArray[] = {
- // 0x2C7 Caron, Mandarin Chinese 3rd Tone
- 0x2C7,
- // 0x2CA Modifier Letter Acute Accent, Mandarin Chinese 2nd Tone
- 0x2CA,
- // 0x2CB Modifier Letter Grave Access, Mandarin Chinese 4th Tone
- 0x2CB,
- // 0x2D9 Dot Above, Mandarin Chinese 5th Tone
- 0x2D9,
- 0x2020, 0x2021, 0x2030, 0x203B, 0x203C, 0x2042, 0x2047, 0x2048, 0x2049, 0x2051,
- 0x20DD, 0x20DE, 0x2100, 0x2103, 0x2105, 0x2109, 0x210A, 0x2113, 0x2116, 0x2121,
- 0x212B, 0x213B, 0x2150, 0x2151, 0x2152, 0x217F, 0x2189, 0x2307, 0x2312, 0x23CE,
- 0x2423, 0x25A0, 0x25A1, 0x25A2, 0x25AA, 0x25AB, 0x25B1, 0x25B2, 0x25B3, 0x25B6,
- 0x25B7, 0x25BC, 0x25BD, 0x25C0, 0x25C1, 0x25C6, 0x25C7, 0x25C9, 0x25CB, 0x25CC,
- 0x25EF, 0x2605, 0x2606, 0x260E, 0x2616, 0x2617, 0x2640, 0x2642, 0x26A0, 0x26BD,
- 0x26BE, 0x2713, 0x271A, 0x273F, 0x2740, 0x2756, 0x2B1A, 0xFE10, 0xFE11, 0xFE12,
- 0xFE19, 0xFF1D,
- // Emoji.
- 0x1F100
-};
-
CodePath Font::s_codePath = AutoPath;
-TypesettingFeatures Font::s_defaultTypesettingFeatures = 0;
-
// ============================================================================================
// Font Implementation (Cross-Platform Portion)
// ============================================================================================
Font::Font()
- : m_letterSpacing(0)
- , m_wordSpacing(0)
- , m_typesettingFeatures(0)
{
}
-Font::Font(const FontDescription& fd, float letterSpacing, float wordSpacing)
+Font::Font(const FontDescription& fd)
: m_fontDescription(fd)
- , m_letterSpacing(letterSpacing)
- , m_wordSpacing(wordSpacing)
- , m_typesettingFeatures(computeTypesettingFeatures())
{
}
Font::Font(const Font& other)
: m_fontDescription(other.m_fontDescription)
, m_fontFallbackList(other.m_fontFallbackList)
- , m_letterSpacing(other.m_letterSpacing)
- , m_wordSpacing(other.m_wordSpacing)
- , m_typesettingFeatures(computeTypesettingFeatures())
{
}
{
m_fontDescription = other.m_fontDescription;
m_fontFallbackList = other.m_fontFallbackList;
- m_letterSpacing = other.m_letterSpacing;
- m_wordSpacing = other.m_wordSpacing;
- m_typesettingFeatures = other.m_typesettingFeatures;
return *this;
}
return first == second
&& m_fontDescription == other.m_fontDescription
- && m_letterSpacing == other.m_letterSpacing
- && m_wordSpacing == other.m_wordSpacing
&& (m_fontFallbackList ? m_fontFallbackList->fontSelectorVersion() : 0) == (other.m_fontFallbackList ? other.m_fontFallbackList->fontSelectorVersion() : 0)
&& (m_fontFallbackList ? m_fontFallbackList->generation() : 0) == (other.m_fontFallbackList ? other.m_fontFallbackList->generation() : 0);
}
if (!m_fontFallbackList)
m_fontFallbackList = FontFallbackList::create();
m_fontFallbackList->invalidate(fontSelector);
- m_typesettingFeatures = computeTypesettingFeatures();
}
void Font::drawText(GraphicsContext* context, const TextRunPaintInfo& runInfo, const FloatPoint& point, CustomFontNotReadyAction customFontNotReadyAction) const
CodePath codePathToUse = codePath(runInfo.run);
// FIXME: Use the fast code path once it handles partial runs with kerning and ligatures. See http://webkit.org/b/100050
- if (codePathToUse != ComplexPath && typesettingFeatures() && (runInfo.from || runInfo.to != runInfo.run.length()))
+ if (codePathToUse != ComplexPath && fontDescription().typesettingFeatures() && (runInfo.from || runInfo.to != runInfo.run.length()))
codePathToUse = ComplexPath;
if (codePathToUse != ComplexPath)
CodePath codePathToUse = codePath(runInfo.run);
// FIXME: Use the fast code path once it handles partial runs with kerning and ligatures. See http://webkit.org/b/100050
- if (codePathToUse != ComplexPath && typesettingFeatures() && (runInfo.from || runInfo.to != runInfo.run.length()))
+ if (codePathToUse != ComplexPath && fontDescription().typesettingFeatures() && (runInfo.from || runInfo.to != runInfo.run.length()))
codePathToUse = ComplexPath;
if (codePathToUse != ComplexPath)
CodePath codePathToUse = codePath(run);
if (codePathToUse != ComplexPath) {
// The complex path is more restrictive about returning fallback fonts than the simple path, so we need an explicit test to make their behaviors match.
- if (!canReturnFallbackFontsForComplexText())
+ if (!FontPlatformFeatures::canReturnFallbackFontsForComplexText())
fallbackFonts = 0;
// The simple path can optimize the case where glyph overflow is not observable.
if (codePathToUse != SimpleWithGlyphOverflowPath && (glyphOverflow && !glyphOverflow->computeBounds))
glyphOverflow = 0;
}
- bool hasKerningOrLigatures = typesettingFeatures() & (Kerning | Ligatures);
- bool hasWordSpacingOrLetterSpacing = wordSpacing() || letterSpacing();
+ bool hasKerningOrLigatures = fontDescription().typesettingFeatures() & (Kerning | Ligatures);
+ bool hasWordSpacingOrLetterSpacing = fontDescription().wordSpacing() || fontDescription().letterSpacing();
float* cacheEntry = m_fontFallbackList->widthCache().add(run, std::numeric_limits<float>::quiet_NaN(), hasKerningOrLigatures, hasWordSpacingOrLetterSpacing, glyphOverflow);
if (cacheEntry && !std::isnan(*cacheEntry))
return *cacheEntry;
CodePath codePathToUse = codePath(run);
// FIXME: Use the fast code path once it handles partial runs with kerning and ligatures. See http://webkit.org/b/100050
- if (codePathToUse != ComplexPath && typesettingFeatures() && (from || to != run.length()))
+ if (codePathToUse != ComplexPath && fontDescription().typesettingFeatures() && (from || to != run.length()))
codePathToUse = ComplexPath;
if (codePathToUse != ComplexPath)
int Font::offsetForPosition(const TextRun& run, float x, bool includePartialGlyphs) const
{
// FIXME: Use the fast code path once it handles partial runs with kerning and ligatures. See http://webkit.org/b/100050
- if (codePath(run) != ComplexPath && !typesettingFeatures())
+ if (codePath(run) != ComplexPath && !fontDescription().typesettingFeatures())
return offsetForPositionForSimpleText(run, x, includePartialGlyphs);
return offsetForPositionForComplexText(run, x, includePartialGlyphs);
}
-template <typename CharacterType>
-static inline String normalizeSpacesInternal(const CharacterType* characters, unsigned length)
-{
- StringBuilder normalized;
- normalized.reserveCapacity(length);
-
- for (unsigned i = 0; i < length; ++i)
- normalized.append(Font::normalizeSpaces(characters[i]));
-
- return normalized.toString();
-}
-
-String Font::normalizeSpaces(const LChar* characters, unsigned length)
-{
- return normalizeSpacesInternal(characters, length);
-}
-
-String Font::normalizeSpaces(const UChar* characters, unsigned length)
-{
- return normalizeSpacesInternal(characters, length);
-}
-
-static bool shouldUseFontSmoothing = true;
-
-void Font::setShouldUseSmoothing(bool shouldUseSmoothing)
-{
- ASSERT(isMainThread());
- shouldUseFontSmoothing = shouldUseSmoothing;
-}
-
-bool Font::shouldUseSmoothing()
-{
- return shouldUseFontSmoothing;
-}
-
void Font::setCodePath(CodePath p)
{
s_codePath = p;
return s_codePath;
}
-void Font::setDefaultTypesettingFeatures(TypesettingFeatures typesettingFeatures)
-{
- s_defaultTypesettingFeatures = typesettingFeatures;
-}
-
-TypesettingFeatures Font::defaultTypesettingFeatures()
-{
- return s_defaultTypesettingFeatures;
-}
-
CodePath Font::codePath(const TextRun& run) const
{
if (s_codePath != AutoPath)
return SimplePath;
// Start from 0 since drawing and highlighting also measure the characters before run->from.
- return characterRangeCodePath(run.characters16(), run.length());
+ return Character::characterRangeCodePath(run.characters16(), run.length());
}
-// Takes a flattened list of closed intervals
-template <class T, size_t size>
-bool valueInIntervalList(const T (&intervalList)[size], const T& value)
+void Font::willUseFontData() const
{
- const T* bound = std::upper_bound(&intervalList[0], &intervalList[size], value);
- if ((bound - intervalList) % 2 == 1)
+ const FontFamily& family = fontDescription().family();
+ if (m_fontFallbackList && m_fontFallbackList->fontSelector() && !family.familyIsEmpty())
+ m_fontFallbackList->fontSelector()->willUseFontData(fontDescription(), family.family());
+}
+
+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;
- return bound > intervalList && *(bound - 1) == value;
-}
-
-CodePath Font::characterRangeCodePath(const UChar* characters, unsigned len)
-{
- static const UChar complexCodePathRanges[] = {
- // U+02E5 through U+02E9 (Modifier Letters : Tone letters)
- 0x2E5, 0x2E9,
- // U+0300 through U+036F Combining diacritical marks
- 0x300, 0x36F,
- // U+0591 through U+05CF excluding U+05BE Hebrew combining marks, ...
- 0x0591, 0x05BD,
- // ... Hebrew punctuation Paseq, Sof Pasuq and Nun Hafukha
- 0x05BF, 0x05CF,
- // U+0600 through U+109F Arabic, Syriac, Thaana, NKo, Samaritan, Mandaic,
- // Devanagari, Bengali, Gurmukhi, Gujarati, Oriya, Tamil, Telugu, Kannada,
- // Malayalam, Sinhala, Thai, Lao, Tibetan, Myanmar
- 0x0600, 0x109F,
- // U+1100 through U+11FF Hangul Jamo (only Ancient Korean should be left
- // here if you precompose; Modern Korean will be precomposed as a result of step A)
- 0x1100, 0x11FF,
- // U+135D through U+135F Ethiopic combining marks
- 0x135D, 0x135F,
- // U+1780 through U+18AF Tagalog, Hanunoo, Buhid, Taghanwa,Khmer, Mongolian
- 0x1700, 0x18AF,
- // U+1900 through U+194F Limbu (Unicode 4.0)
- 0x1900, 0x194F,
- // U+1980 through U+19DF New Tai Lue
- 0x1980, 0x19DF,
- // U+1A00 through U+1CFF Buginese, Tai Tham, Balinese, Batak, Lepcha, Vedic
- 0x1A00, 0x1CFF,
- // U+1DC0 through U+1DFF Comining diacritical mark supplement
- 0x1DC0, 0x1DFF,
- // U+20D0 through U+20FF Combining marks for symbols
- 0x20D0, 0x20FF,
- // U+2CEF through U+2CF1 Combining marks for Coptic
- 0x2CEF, 0x2CF1,
- // U+302A through U+302F Ideographic and Hangul Tone marks
- 0x302A, 0x302F,
- // U+A67C through U+A67D Combining marks for old Cyrillic
- 0xA67C, 0xA67D,
- // U+A6F0 through U+A6F1 Combining mark for Bamum
- 0xA6F0, 0xA6F1,
- // U+A800 through U+ABFF Nagri, Phags-pa, Saurashtra, Devanagari Extended,
- // Hangul Jamo Ext. A, Javanese, Myanmar Extended A, Tai Viet, Meetei Mayek
- 0xA800, 0xABFF,
- // U+D7B0 through U+D7FF Hangul Jamo Ext. B
- 0xD7B0, 0xD7FF,
- // U+FE00 through U+FE0F Unicode variation selectors
- 0xFE00, 0xFE0F,
- // U+FE20 through U+FE2F Combining half marks
- 0xFE20, 0xFE2F
- };
-
- CodePath result = SimplePath;
- for (unsigned i = 0; i < len; i++) {
- const UChar c = characters[i];
-
- // Shortcut for common case
- if (c < 0x2E5)
- continue;
-
- // U+1E00 through U+2000 characters with diacritics and stacked diacritics
- if (c >= 0x1E00 && c <= 0x2000) {
- result = SimpleWithGlyphOverflowPath;
- continue;
- }
- // Surrogate pairs
- if (c > 0xD7FF && c <= 0xDBFF) {
- if (i == len - 1)
- continue;
+ if (character == 0x000B6 || character == 0x000BC || character == 0x000BD || character == 0x000BE)
+ return true;
- UChar next = characters[++i];
- if (!U16_IS_TRAIL(next))
- continue;
+ if (isInRange(character, 0x002E5, 0x002EB))
+ return true;
- UChar32 supplementaryCharacter = U16_GET_SUPPLEMENTARY(c, next);
+ if (isInRange(character, 0x01100, 0x011FF) || isInRange(character, 0x01401, 0x0167F) || isInRange(character, 0x01800, 0x018FF))
+ return true;
- if (supplementaryCharacter < 0x1F1E6) // U+1F1E6 through U+1F1FF Regional Indicator Symbols
- continue;
- if (supplementaryCharacter <= 0x1F1FF)
- return ComplexPath;
+ if (character == 0x02016 || character == 0x02018 || character == 0x02019 || character == 0x02020 || character == 0x02021
+ || character == 0x2030 || character == 0x02031)
+ return true;
- if (supplementaryCharacter < 0xE0100) // U+E0100 through U+E01EF Unicode variation selectors.
- continue;
- if (supplementaryCharacter <= 0xE01EF)
- return ComplexPath;
+ if (isInRange(character, 0x0203B, 0x0203D) || character == 0x02042 || character == 0x02044 || character == 0x02047
+ || character == 0x02048 || character == 0x02049 || character == 0x2051)
+ return true;
- // FIXME: Check for Brahmi (U+11000 block), Kaithi (U+11080 block) and other complex scripts
- // in plane 1 or higher.
+ 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;
- continue;
- }
+ if (isInRange(character, 0x02145, 0x0214A) || character == 0x0214C || character == 0x0214D
+ || isInRange(character, 0x0214F, 0x0218F))
+ return true;
- // Search for other Complex cases
- if (valueInIntervalList(complexCodePathRanges, c))
- return ComplexPath;
- }
+ 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;
- return result;
-}
+ 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;
-bool Font::isCJKIdeograph(UChar32 c)
-{
- static const UChar32 cjkIdeographRanges[] = {
- // CJK Radicals Supplement and Kangxi Radicals.
- 0x2E80, 0x2FDF,
- // CJK Strokes.
- 0x31C0, 0x31EF,
- // CJK Unified Ideographs Extension A.
- 0x3400, 0x4DBF,
- // The basic CJK Unified Ideographs block.
- 0x4E00, 0x9FFF,
- // CJK Compatibility Ideographs.
- 0xF900, 0xFAFF,
- // CJK Unified Ideographs Extension B.
- 0x20000, 0x2A6DF,
- // CJK Unified Ideographs Extension C.
- // CJK Unified Ideographs Extension D.
- 0x2A700, 0x2B81F,
- // CJK Compatibility Ideographs Supplement.
- 0x2F800, 0x2FA1F
- };
- static size_t cjkIdeographRangesCount = WTF_ARRAY_LENGTH(cjkIdeographRanges);
-
- // Early out
- if (c < cjkIdeographRanges[0] || c > cjkIdeographRanges[cjkIdeographRangesCount - 1])
- return false;
+ if (character == 0x03012 || character == 0x03013 || isInRange(character, 0x03020, 0x0302F)
+ || isInRange(character, 0x03031, 0x0309F) || isInRange(character, 0x030A1, 0x030FB)
+ || isInRange(character, 0x030FD, 0x0A4CF))
+ return true;
- return valueInIntervalList(cjkIdeographRanges, c);
-}
+ if (isInRange(character, 0x0A840, 0x0A87F) || isInRange(character, 0x0A960, 0x0A97F)
+ || isInRange(character, 0x0AC00, 0x0D7FF) || isInRange(character, 0x0E000, 0x0FAFF))
+ return true;
-bool Font::isCJKIdeographOrSymbol(UChar32 c)
-{
- // Likely common case
- if (c < 0x2C7)
- return false;
+ if (isInRange(character, 0x0FE10, 0x0FE1F) || isInRange(character, 0x0FE30, 0x0FE48)
+ || isInRange(character, 0x0FE50, 0x0FE57) || isInRange(character, 0x0FE5F, 0x0FE62)
+ || isInRange(character, 0x0FE67, 0x0FE6F))
+ return true;
- // Hash lookup for isolated symbols (those not part of a contiguous range)
- static HashSet<UChar32>* cjkIsolatedSymbols = 0;
- if (!cjkIsolatedSymbols) {
- cjkIsolatedSymbols = new HashSet<UChar32>();
- for (size_t i = 0; i < WTF_ARRAY_LENGTH(cjkIsolatedSymbolsArray); ++i)
- cjkIsolatedSymbols->add(cjkIsolatedSymbolsArray[i]);
- }
- if (cjkIsolatedSymbols->contains(c))
+ if (isInRange(character, 0x0FF01, 0x0FF07) || isInRange(character, 0x0FF0A, 0x0FF0C)
+ || isInRange(character, 0x0FF0E, 0x0FF19) || 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 (isCJKIdeograph(c))
+ 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;
- static const UChar32 cjkSymbolRanges[] = {
- 0x2156, 0x215A,
- 0x2160, 0x216B,
- 0x2170, 0x217B,
- 0x23BE, 0x23CC,
- 0x2460, 0x2492,
- 0x249C, 0x24FF,
- 0x25CE, 0x25D3,
- 0x25E2, 0x25E6,
- 0x2600, 0x2603,
- 0x2660, 0x266F,
- 0x2672, 0x267D,
- 0x2776, 0x277F,
- // Ideographic Description Characters, with CJK Symbols and Punctuation, excluding 0x3030.
- // Then Hiragana 0x3040 .. 0x309F, Katakana 0x30A0 .. 0x30FF, Bopomofo 0x3100 .. 0x312F
- 0x2FF0, 0x302F,
- 0x3031, 0x312F,
- // More Bopomofo and Bopomofo Extended 0x31A0 .. 0x31BF
- 0x3190, 0x31BF,
- // Enclosed CJK Letters and Months (0x3200 .. 0x32FF).
- // CJK Compatibility (0x3300 .. 0x33FF).
- 0x3200, 0x33FF,
- 0xF860, 0xF862,
- // CJK Compatibility Forms.
- 0xFE30, 0xFE4F,
- // Halfwidth and Fullwidth Forms
- // Usually only used in CJK
- 0xFF00, 0xFF0C,
- 0xFF0E, 0xFF1A,
- 0xFF1F, 0xFFEF,
- // Emoji.
- 0x1F110, 0x1F129,
- 0x1F130, 0x1F149,
- 0x1F150, 0x1F169,
- 0x1F170, 0x1F189,
- 0x1F200, 0x1F6FF
- };
-
- return valueInIntervalList(cjkSymbolRanges, c);
-}
-
-unsigned Font::expansionOpportunityCount(const LChar* characters, size_t length, TextDirection direction, bool& isAfterExpansion)
-{
- unsigned count = 0;
- if (direction == LTR) {
- for (size_t i = 0; i < length; ++i) {
- if (treatAsSpace(characters[i])) {
- count++;
- isAfterExpansion = true;
- } else
- isAfterExpansion = false;
+ if (isInRange(character, 0x20000, 0x2FFFD) || isInRange(character, 0x30000, 0x3FFFD))
+ return true;
+
+ return false;
+}
+
+static inline std::pair<GlyphData, GlyphPage*> glyphDataAndPageForNonCJKCharacterWithGlyphOrientation(UChar32 character, NonCJKGlyphOrientation orientation, GlyphData& data, GlyphPage* page, unsigned pageNumber)
+{
+ if (orientation == NonCJKGlyphOrientationUpright || shouldIgnoreRotation(character)) {
+ RefPtr<SimpleFontData> uprightFontData = data.fontData->uprightOrientationFontData();
+ GlyphPageTreeNode* uprightNode = GlyphPageTreeNode::getRootChild(uprightFontData.get(), pageNumber);
+ GlyphPage* uprightPage = uprightNode->page();
+ if (uprightPage) {
+ GlyphData uprightData = uprightPage->glyphDataForCharacter(character);
+ // If the glyphs are the same, then we know we can just use the horizontal glyph rotated vertically to be upright.
+ if (data.glyph == uprightData.glyph)
+ return make_pair(data, page);
+ // 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 make_pair(uprightData, uprightPage);
}
- } else {
- for (size_t i = length; i > 0; --i) {
- if (treatAsSpace(characters[i - 1])) {
- count++;
- isAfterExpansion = true;
- } else
- isAfterExpansion = false;
+ } else if (orientation == NonCJKGlyphOrientationVerticalRight) {
+ RefPtr<SimpleFontData> verticalRightFontData = data.fontData->verticalRightOrientationFontData();
+ GlyphPageTreeNode* verticalRightNode = GlyphPageTreeNode::getRootChild(verticalRightFontData.get(), pageNumber);
+ GlyphPage* verticalRightPage = verticalRightNode->page();
+ if (verticalRightPage) {
+ GlyphData verticalRightData = verticalRightPage->glyphDataForCharacter(character);
+ // If the glyphs are distinct, we will make the assumption that the font has a vertical-right glyph baked
+ // into it.
+ if (data.glyph != verticalRightData.glyph)
+ return make_pair(data, page);
+ // The glyphs are identical, meaning that we should just use the horizontal glyph.
+ if (verticalRightData.fontData)
+ return make_pair(verticalRightData, verticalRightPage);
}
}
- return count;
+ return make_pair(data, page);
}
-unsigned Font::expansionOpportunityCount(const UChar* characters, size_t length, TextDirection direction, bool& isAfterExpansion)
+std::pair<GlyphData, GlyphPage*> Font::glyphDataAndPageForCharacter(UChar32 c, bool mirror, FontDataVariant variant) const
{
- static bool expandAroundIdeographs = canExpandAroundIdeographsInComplexText();
- unsigned count = 0;
- if (direction == LTR) {
- for (size_t i = 0; i < length; ++i) {
- UChar32 character = characters[i];
- if (treatAsSpace(character)) {
- count++;
- isAfterExpansion = true;
- continue;
- }
- if (U16_IS_LEAD(character) && i + 1 < length && U16_IS_TRAIL(characters[i + 1])) {
- character = U16_GET_SUPPLEMENTARY(character, characters[i + 1]);
- i++;
- }
- if (expandAroundIdeographs && isCJKIdeographOrSymbol(character)) {
- if (!isAfterExpansion)
- count++;
- count++;
- isAfterExpansion = true;
- continue;
+ ASSERT(isMainThread());
+
+ if (variant == AutoVariant) {
+ if (m_fontDescription.smallCaps() && !primaryFont()->isSVGFont()) {
+ UChar32 upperC = toUpper(c);
+ if (upperC != c) {
+ c = upperC;
+ variant = SmallCapsVariant;
+ } else {
+ variant = NormalVariant;
}
- isAfterExpansion = false;
+ } else {
+ variant = NormalVariant;
}
- } else {
- for (size_t i = length; i > 0; --i) {
- UChar32 character = characters[i - 1];
- if (treatAsSpace(character)) {
- count++;
- isAfterExpansion = true;
- continue;
- }
- if (U16_IS_TRAIL(character) && i > 1 && U16_IS_LEAD(characters[i - 2])) {
- character = U16_GET_SUPPLEMENTARY(characters[i - 2], character);
- i--;
+ }
+
+ if (mirror)
+ c = mirroredChar(c);
+
+ unsigned pageNumber = (c / GlyphPage::size);
+
+ GlyphPageTreeNode* node = pageNumber ? m_fontFallbackList->m_pages.get(pageNumber) : m_fontFallbackList->m_pageZero;
+ if (!node) {
+ node = GlyphPageTreeNode::getRootChild(fontDataAt(0), pageNumber);
+ if (pageNumber)
+ m_fontFallbackList->m_pages.set(pageNumber, node);
+ else
+ m_fontFallbackList->m_pageZero = node;
+ }
+
+ GlyphPage* page = 0;
+ if (variant == NormalVariant) {
+ // Fastest loop, for the common case (normal variant).
+ while (true) {
+ page = node->page();
+ if (page) {
+ GlyphData data = page->glyphDataForCharacter(c);
+ if (data.fontData && (data.fontData->platformData().orientation() == Horizontal || data.fontData->isTextOrientationFallback()))
+ return make_pair(data, page);
+
+ if (data.fontData) {
+ if (Character::isCJKIdeographOrSymbol(c)) {
+ 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).
+ variant = BrokenIdeographVariant;
+ break;
+ }
+ } else {
+ return glyphDataAndPageForNonCJKCharacterWithGlyphOrientation(c, m_fontDescription.nonCJKGlyphOrientation(), data, page, pageNumber);
+ }
+
+ return make_pair(data, page);
+ }
+
+ if (node->isSystemFallback())
+ break;
}
- if (expandAroundIdeographs && isCJKIdeographOrSymbol(character)) {
- if (!isAfterExpansion)
- count++;
- count++;
- isAfterExpansion = true;
- continue;
+
+ // Proceed with the fallback list.
+ node = node->getChild(fontDataAt(node->level()), pageNumber);
+ if (pageNumber)
+ m_fontFallbackList->m_pages.set(pageNumber, node);
+ else
+ m_fontFallbackList->m_pageZero = node;
+ }
+ }
+ if (variant != NormalVariant) {
+ while (true) {
+ page = node->page();
+ if (page) {
+ GlyphData data = page->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(m_fontDescription, variant);
+ if (!variantFontData)
+ return make_pair(data, page);
+
+ GlyphPageTreeNode* variantNode = GlyphPageTreeNode::getRootChild(variantFontData.get(), pageNumber);
+ GlyphPage* variantPage = variantNode->page();
+ if (variantPage) {
+ GlyphData data = variantPage->glyphDataForCharacter(c);
+ if (data.fontData)
+ return make_pair(data, variantPage);
+ }
+
+ // Do not attempt system fallback off the variantFontData. This is the very unlikely case that
+ // a font has the lowercase character but the small caps font does not have its uppercase version.
+ return make_pair(variantFontData->missingGlyphData(), page);
+ }
+
+ if (node->isSystemFallback())
+ break;
}
- isAfterExpansion = false;
+
+ // Proceed with the fallback list.
+ node = node->getChild(fontDataAt(node->level()), pageNumber);
+ if (pageNumber)
+ m_fontFallbackList->m_pages.set(pageNumber, node);
+ else
+ m_fontFallbackList->m_pageZero = node;
}
}
- return count;
+
+ ASSERT(page);
+ ASSERT(node->isSystemFallback());
+
+ // System fallback is character-dependent. When we get here, we
+ // know that the character in question isn't in the system fallback
+ // font's glyph page. Try to lazily create it here.
+
+ // FIXME: Unclear if this should normalizeSpaces above 0xFFFF.
+ // Doing so changes fast/text/international/plane2-diffs.html
+ UChar32 characterToRender = c;
+ if (characterToRender <= 0xFFFF)
+ characterToRender = Character::normalizeSpaces(characterToRender);
+ const SimpleFontData* fontDataToSubstitute = fontDataAt(0)->fontDataForCharacter(characterToRender);
+ RefPtr<SimpleFontData> characterFontData = FontCache::fontCache()->platformFallbackForCharacter(m_fontDescription, characterToRender, fontDataToSubstitute);
+ if (characterFontData) {
+ if (characterFontData->platformData().orientation() == Vertical && !characterFontData->hasVerticalGlyphs() && Character::isCJKIdeographOrSymbol(c))
+ variant = BrokenIdeographVariant;
+ if (variant != NormalVariant)
+ characterFontData = characterFontData->variantFontData(m_fontDescription, variant);
+ }
+ if (characterFontData) {
+ // Got the fallback glyph and font.
+ GlyphPage* fallbackPage = GlyphPageTreeNode::getRootChild(characterFontData.get(), pageNumber)->page();
+ GlyphData data = fallbackPage && fallbackPage->fontDataForCharacter(c) ? fallbackPage->glyphDataForCharacter(c) : characterFontData->missingGlyphData();
+ // Cache it so we don't have to do system fallback again next time.
+ if (variant == NormalVariant) {
+ page->setGlyphDataForCharacter(c, data.glyph, data.fontData);
+ data.fontData->setMaxGlyphPageTreeLevel(max(data.fontData->maxGlyphPageTreeLevel(), node->level()));
+ if (!Character::isCJKIdeographOrSymbol(c) && data.fontData->platformData().orientation() != Horizontal && !data.fontData->isTextOrientationFallback())
+ return glyphDataAndPageForNonCJKCharacterWithGlyphOrientation(c, m_fontDescription.nonCJKGlyphOrientation(), data, fallbackPage, pageNumber);
+ }
+ return make_pair(data, page);
+ }
+
+ // Even system fallback can fail; use the missing glyph in that case.
+ // FIXME: It would be nicer to use the missing glyph from the last resort font instead.
+ GlyphData data = primaryFont()->missingGlyphData();
+ if (variant == NormalVariant) {
+ page->setGlyphDataForCharacter(c, data.glyph, data.fontData);
+ data.fontData->setMaxGlyphPageTreeLevel(max(data.fontData->maxGlyphPageTreeLevel(), node->level()));
+ }
+ return make_pair(data, page);
}
-bool Font::canReceiveTextEmphasis(UChar32 c)
+bool Font::primaryFontHasGlyphForCharacter(UChar32 character) const
{
- CharCategory category = Unicode::category(c);
- if (category & (Separator_Space | Separator_Line | Separator_Paragraph | Other_NotAssigned | Other_Control | Other_Format))
- return false;
+ unsigned pageNumber = (character / GlyphPage::size);
+
+ GlyphPageTreeNode* node = GlyphPageTreeNode::getRootChild(primaryFont(), pageNumber);
+ GlyphPage* page = node->page();
+
+ return page && page->fontDataForCharacter(character);
+}
- // Additional word-separator characters listed in CSS Text Level 3 Editor's Draft 3 November 2010.
- if (c == ethiopicWordspace || c == aegeanWordSeparatorLine || c == aegeanWordSeparatorDot
- || c == ugariticWordDivider || c == tibetanMarkIntersyllabicTsheg || c == tibetanMarkDelimiterTshegBstar)
+// FIXME: This function may not work if the emphasis mark uses a complex script, but none of the
+// standard emphasis marks do so.
+bool Font::getEmphasisMarkGlyphData(const AtomicString& mark, GlyphData& glyphData) const
+{
+ if (mark.isEmpty())
return false;
+ UChar32 character = mark[0];
+
+ if (U16_IS_SURROGATE(character)) {
+ if (!U16_IS_SURROGATE_LEAD(character))
+ return false;
+
+ if (mark.length() < 2)
+ return false;
+
+ UChar low = mark[1];
+ if (!U16_IS_TRAIL(low))
+ return false;
+
+ character = U16_GET_SUPPLEMENTARY(character, low);
+ }
+
+ glyphData = glyphDataForCharacter(character, false, EmphasisMarkVariant);
return true;
}
-void Font::willUseFontData() const
+int Font::emphasisMarkAscent(const AtomicString& mark) const
{
- const FontFamily& family = fontDescription().family();
- if (m_fontFallbackList && m_fontFallbackList->fontSelector() && !family.familyIsEmpty())
- m_fontFallbackList->fontSelector()->willUseFontData(fontDescription(), family.family());
+ FontCachePurgePreventer purgePreventer;
+
+ GlyphData markGlyphData;
+ if (!getEmphasisMarkGlyphData(mark, markGlyphData))
+ return 0;
+
+ const SimpleFontData* markFontData = markGlyphData.fontData;
+ ASSERT(markFontData);
+ if (!markFontData)
+ return 0;
+
+ return markFontData->fontMetrics().ascent();
+}
+
+int Font::emphasisMarkDescent(const AtomicString& mark) const
+{
+ FontCachePurgePreventer purgePreventer;
+
+ GlyphData markGlyphData;
+ if (!getEmphasisMarkGlyphData(mark, markGlyphData))
+ return 0;
+
+ const SimpleFontData* markFontData = markGlyphData.fontData;
+ ASSERT(markFontData);
+ if (!markFontData)
+ return 0;
+
+ return markFontData->fontMetrics().descent();
+}
+
+int Font::emphasisMarkHeight(const AtomicString& mark) const
+{
+ FontCachePurgePreventer purgePreventer;
+
+ GlyphData markGlyphData;
+ if (!getEmphasisMarkGlyphData(mark, markGlyphData))
+ return 0;
+
+ const SimpleFontData* markFontData = markGlyphData.fontData;
+ ASSERT(markFontData);
+ if (!markFontData)
+ return 0;
+
+ return markFontData->fontMetrics().height();
+}
+
+float Font::getGlyphsAndAdvancesForSimpleText(const TextRun& run, int from, int to, GlyphBuffer& glyphBuffer, ForTextEmphasisOrNot forTextEmphasis) const
+{
+ float initialAdvance;
+
+ WidthIterator it(this, run, 0, false, forTextEmphasis);
+ // FIXME: Using separate glyph buffers for the prefix and the suffix is incorrect when kerning or
+ // ligatures are enabled.
+ GlyphBuffer localGlyphBuffer;
+ it.advance(from, &localGlyphBuffer);
+ float beforeWidth = it.m_runWidthSoFar;
+ it.advance(to, &glyphBuffer);
+
+ if (glyphBuffer.isEmpty())
+ return 0;
+
+ float afterWidth = it.m_runWidthSoFar;
+
+ if (run.rtl()) {
+ float finalRoundingWidth = it.m_finalRoundingWidth;
+ it.advance(run.length(), &localGlyphBuffer);
+ initialAdvance = finalRoundingWidth + it.m_runWidthSoFar - afterWidth;
+ } else {
+ initialAdvance = beforeWidth;
+ }
+
+ if (run.rtl())
+ glyphBuffer.reverse(0, glyphBuffer.size());
+
+ return initialAdvance;
+}
+
+void Font::drawSimpleText(GraphicsContext* context, const TextRunPaintInfo& runInfo, const FloatPoint& point) const
+{
+ // This glyph buffer holds our glyphs+advances+font data for each glyph.
+ GlyphBuffer glyphBuffer;
+
+ float startX = point.x() + getGlyphsAndAdvancesForSimpleText(runInfo.run, runInfo.from, runInfo.to, glyphBuffer);
+
+ if (glyphBuffer.isEmpty())
+ return;
+
+ FloatPoint startPoint(startX, point.y());
+ drawGlyphBuffer(context, runInfo, glyphBuffer, startPoint);
+}
+
+void Font::drawEmphasisMarksForSimpleText(GraphicsContext* context, const TextRunPaintInfo& runInfo, const AtomicString& mark, const FloatPoint& point) const
+{
+ GlyphBuffer glyphBuffer;
+ float initialAdvance = getGlyphsAndAdvancesForSimpleText(runInfo.run, runInfo.from, runInfo.to, glyphBuffer, ForTextEmphasis);
+
+ if (glyphBuffer.isEmpty())
+ return;
+
+ drawEmphasisMarks(context, runInfo, glyphBuffer, mark, FloatPoint(point.x() + initialAdvance, point.y()));
+}
+
+void Font::drawGlyphBuffer(GraphicsContext* context, const TextRunPaintInfo& runInfo, const GlyphBuffer& glyphBuffer, const FloatPoint& point) const
+{
+ // Draw each contiguous run of glyphs that use the same font data.
+ const SimpleFontData* fontData = glyphBuffer.fontDataAt(0);
+ FloatPoint startPoint(point);
+ float nextX = startPoint.x() + glyphBuffer.advanceAt(0);
+ unsigned lastFrom = 0;
+ unsigned nextGlyph = 1;
+#if ENABLE(SVG_FONTS)
+ TextRun::RenderingContext* renderingContext = runInfo.run.renderingContext();
+#endif
+ while (nextGlyph < glyphBuffer.size()) {
+ const SimpleFontData* nextFontData = glyphBuffer.fontDataAt(nextGlyph);
+
+ if (nextFontData != fontData) {
+#if ENABLE(SVG_FONTS)
+ if (renderingContext && fontData->isSVGFont())
+ renderingContext->drawSVGGlyphs(context, runInfo.run, fontData, glyphBuffer, lastFrom, nextGlyph - lastFrom, startPoint);
+ else
+#endif
+ drawGlyphs(context, fontData, glyphBuffer, lastFrom, nextGlyph - lastFrom, startPoint, runInfo.bounds);
+
+ lastFrom = nextGlyph;
+ fontData = nextFontData;
+ startPoint.setX(nextX);
+ }
+ nextX += glyphBuffer.advanceAt(nextGlyph);
+ nextGlyph++;
+ }
+
+#if ENABLE(SVG_FONTS)
+ if (renderingContext && fontData->isSVGFont())
+ renderingContext->drawSVGGlyphs(context, runInfo.run, fontData, glyphBuffer, lastFrom, nextGlyph - lastFrom, startPoint);
+ else
+#endif
+ drawGlyphs(context, fontData, glyphBuffer, lastFrom, nextGlyph - lastFrom, startPoint, runInfo.bounds);
+}
+
+inline static float offsetToMiddleOfGlyph(const SimpleFontData* fontData, Glyph glyph)
+{
+ if (fontData->platformData().orientation() == Horizontal) {
+ FloatRect bounds = fontData->boundsForGlyph(glyph);
+ return bounds.x() + bounds.width() / 2;
+ }
+ // FIXME: Use glyph bounds once they make sense for vertical fonts.
+ return fontData->widthForGlyph(glyph) / 2;
+}
+
+inline static float offsetToMiddleOfAdvanceAtIndex(const GlyphBuffer& glyphBuffer, size_t i)
+{
+ return glyphBuffer.advanceAt(i) / 2;
+}
+
+void Font::drawEmphasisMarks(GraphicsContext* context, const TextRunPaintInfo& runInfo, const GlyphBuffer& glyphBuffer, const AtomicString& mark, const FloatPoint& point) const
+{
+ FontCachePurgePreventer purgePreventer;
+
+ GlyphData markGlyphData;
+ if (!getEmphasisMarkGlyphData(mark, markGlyphData))
+ return;
+
+ const SimpleFontData* markFontData = markGlyphData.fontData;
+ ASSERT(markFontData);
+ if (!markFontData)
+ return;
+
+ Glyph markGlyph = markGlyphData.glyph;
+ Glyph spaceGlyph = markFontData->spaceGlyph();
+
+ float middleOfLastGlyph = offsetToMiddleOfAdvanceAtIndex(glyphBuffer, 0);
+ FloatPoint startPoint(point.x() + middleOfLastGlyph - offsetToMiddleOfGlyph(markFontData, markGlyph), point.y());
+
+ GlyphBuffer markBuffer;
+ for (unsigned i = 0; i + 1 < glyphBuffer.size(); ++i) {
+ float middleOfNextGlyph = offsetToMiddleOfAdvanceAtIndex(glyphBuffer, i + 1);
+ float advance = glyphBuffer.advanceAt(i) - middleOfLastGlyph + middleOfNextGlyph;
+ markBuffer.add(glyphBuffer.glyphAt(i) ? markGlyph : spaceGlyph, markFontData, advance);
+ middleOfLastGlyph = middleOfNextGlyph;
+ }
+ markBuffer.add(glyphBuffer.glyphAt(glyphBuffer.size() - 1) ? markGlyph : spaceGlyph, markFontData, 0);
+
+ drawGlyphBuffer(context, runInfo, markBuffer, startPoint);
+}
+
+float Font::floatWidthForSimpleText(const TextRun& run, HashSet<const SimpleFontData*>* fallbackFonts, GlyphOverflow* glyphOverflow) const
+{
+ WidthIterator it(this, run, fallbackFonts, glyphOverflow);
+ GlyphBuffer glyphBuffer;
+ it.advance(run.length(), (fontDescription().typesettingFeatures() & (Kerning | Ligatures)) ? &glyphBuffer : 0);
+
+ if (glyphOverflow) {
+ glyphOverflow->top = max<int>(glyphOverflow->top, ceilf(-it.minGlyphBoundingBoxY()) - (glyphOverflow->computeBounds ? 0 : fontMetrics().ascent()));
+ glyphOverflow->bottom = max<int>(glyphOverflow->bottom, ceilf(it.maxGlyphBoundingBoxY()) - (glyphOverflow->computeBounds ? 0 : fontMetrics().descent()));
+ glyphOverflow->left = ceilf(it.firstGlyphOverflow());
+ glyphOverflow->right = ceilf(it.lastGlyphOverflow());
+ }
+
+ return it.m_runWidthSoFar;
+}
+
+FloatRect Font::selectionRectForSimpleText(const TextRun& run, const FloatPoint& point, int h, int from, int to) const
+{
+ GlyphBuffer glyphBuffer;
+ WidthIterator it(this, run);
+ it.advance(from, &glyphBuffer);
+ float beforeWidth = it.m_runWidthSoFar;
+ it.advance(to, &glyphBuffer);
+ float afterWidth = it.m_runWidthSoFar;
+
+ // Using roundf() rather than ceilf() for the right edge as a compromise to
+ // ensure correct caret positioning.
+ // Use LayoutUnit::epsilon() to ensure that values that cannot be stored as
+ // an integer are floored to n and not n-1 due to floating point imprecision.
+ if (run.rtl()) {
+ it.advance(run.length(), &glyphBuffer);
+ float totalWidth = it.m_runWidthSoFar;
+ float pixelAlignedX = floorf(point.x() + totalWidth - afterWidth + LayoutUnit::epsilon());
+ return FloatRect(pixelAlignedX, point.y(),
+ roundf(point.x() + totalWidth - beforeWidth) - pixelAlignedX, h);
+ }
+
+ float pixelAlignedX = floorf(point.x() + beforeWidth + LayoutUnit::epsilon());
+ return FloatRect(pixelAlignedX, point.y(),
+ roundf(point.x() + afterWidth) - pixelAlignedX, h);
+}
+
+int Font::offsetForPositionForSimpleText(const TextRun& run, float x, bool includePartialGlyphs) const
+{
+ float delta = x;
+
+ WidthIterator it(this, run);
+ GlyphBuffer localGlyphBuffer;
+ unsigned offset;
+ if (run.rtl()) {
+ delta -= floatWidthForSimpleText(run);
+ while (1) {
+ offset = it.m_currentCharacter;
+ float w;
+ if (!it.advanceOneCharacter(w, localGlyphBuffer))
+ break;
+ delta += w;
+ if (includePartialGlyphs) {
+ if (delta - w / 2 >= 0)
+ break;
+ } else {
+ if (delta >= 0)
+ break;
+ }
+ }
+ } else {
+ while (1) {
+ offset = it.m_currentCharacter;
+ float w;
+ if (!it.advanceOneCharacter(w, localGlyphBuffer))
+ break;
+ delta -= w;
+ if (includePartialGlyphs) {
+ if (delta + w / 2 <= 0)
+ break;
+ } else {
+ if (delta <= 0)
+ break;
+ }
+ }
+ }
+
+ return offset;
}
}