| /* |
| * Copyright (C) 1999 Lars Knoll (knoll@kde.org) |
| * (C) 1999 Antti Koivisto (koivisto@kde.org) |
| * (C) 2000 Dirk Mueller (mueller@kde.org) |
| * Copyright (C) 2003, 2006, 2010, 2011 Apple Inc. All rights reserved. |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Library General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Library General Public License for more details. |
| * |
| * You should have received a copy of the GNU Library General Public License |
| * along with this library; see the file COPYING.LIB. If not, write to |
| * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
| * Boston, MA 02110-1301, USA. |
| * |
| */ |
| |
| #include "config.h" |
| #include "core/platform/graphics/Font.h" |
| |
| #include "core/platform/graphics/FloatRect.h" |
| #include "core/platform/graphics/TextRun.h" |
| #include "core/platform/graphics/WidthIterator.h" |
| #include "core/platform/text/transcoder/FontTranscoder.h" |
| #include <wtf/MainThread.h> |
| #include <wtf/MathExtras.h> |
| #include <wtf/text/StringBuilder.h> |
| #include <wtf/UnusedParam.h> |
| |
| using namespace WTF; |
| using namespace Unicode; |
| |
| namespace WTF { |
| |
| // allow compilation of OwnPtr<TextLayout> in source files that don't have access to the TextLayout class definition |
| template <> void deleteOwnedPtr<WebCore::TextLayout>(WebCore::TextLayout* ptr) |
| { |
| WebCore::Font::deleteLayout(ptr); |
| } |
| |
| } |
| |
| 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 |
| }; |
| |
| Font::CodePath Font::s_codePath = Auto; |
| |
| TypesettingFeatures Font::s_defaultTypesettingFeatures = 0; |
| |
| // ============================================================================================ |
| // Font Implementation (Cross-Platform Portion) |
| // ============================================================================================ |
| |
| Font::Font() |
| : m_letterSpacing(0) |
| , m_wordSpacing(0) |
| , m_isPlatformFont(false) |
| , m_needsTranscoding(false) |
| , m_typesettingFeatures(0) |
| { |
| } |
| |
| Font::Font(const FontDescription& fd, short letterSpacing, short wordSpacing) |
| : m_fontDescription(fd) |
| , m_letterSpacing(letterSpacing) |
| , m_wordSpacing(wordSpacing) |
| , m_isPlatformFont(false) |
| , m_needsTranscoding(fontTranscoder().needsTranscoding(fd)) |
| , m_typesettingFeatures(computeTypesettingFeatures()) |
| { |
| } |
| |
| Font::Font(const FontPlatformData& fontData, bool isPrinterFont, FontSmoothingMode fontSmoothingMode) |
| : m_fontFallbackList(FontFallbackList::create()) |
| , m_letterSpacing(0) |
| , m_wordSpacing(0) |
| , m_isPlatformFont(true) |
| , m_typesettingFeatures(computeTypesettingFeatures()) |
| { |
| m_fontDescription.setUsePrinterFont(isPrinterFont); |
| m_fontDescription.setFontSmoothing(fontSmoothingMode); |
| m_needsTranscoding = fontTranscoder().needsTranscoding(fontDescription()); |
| m_fontFallbackList->setPlatformFont(fontData); |
| } |
| |
| 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_isPlatformFont(other.m_isPlatformFont) |
| , m_needsTranscoding(other.m_needsTranscoding) |
| , m_typesettingFeatures(computeTypesettingFeatures()) |
| { |
| } |
| |
| Font& Font::operator=(const Font& other) |
| { |
| m_fontDescription = other.m_fontDescription; |
| m_fontFallbackList = other.m_fontFallbackList; |
| m_letterSpacing = other.m_letterSpacing; |
| m_wordSpacing = other.m_wordSpacing; |
| m_isPlatformFont = other.m_isPlatformFont; |
| m_needsTranscoding = other.m_needsTranscoding; |
| m_typesettingFeatures = other.m_typesettingFeatures; |
| return *this; |
| } |
| |
| bool Font::operator==(const Font& other) const |
| { |
| // Our FontData don't have to be checked, since checking the font description will be fine. |
| // FIXME: This does not work if the font was made with the FontPlatformData constructor. |
| if (loadingCustomFonts() || other.loadingCustomFonts()) |
| return false; |
| |
| FontSelector* first = m_fontFallbackList ? m_fontFallbackList->fontSelector() : 0; |
| FontSelector* second = other.m_fontFallbackList ? other.m_fontFallbackList->fontSelector() : 0; |
| |
| 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); |
| } |
| |
| void Font::update(PassRefPtr<FontSelector> fontSelector) const |
| { |
| // FIXME: It is pretty crazy that we are willing to just poke into a RefPtr, but it ends up |
| // being reasonably safe (because inherited fonts in the render tree pick up the new |
| // style anyway. Other copies are transient, e.g., the state in the GraphicsContext, and |
| // won't stick around long enough to get you in trouble). Still, this is pretty disgusting, |
| // and could eventually be rectified by using RefPtrs for Fonts themselves. |
| 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 |
| { |
| // Don't draw anything while we are using custom fonts that are in the process of loading, |
| // except if the 'force' argument is set to true (in which case it will use a fallback |
| // font). |
| if (loadingCustomFonts() && customFontNotReadyAction == DoNotPaintIfFontNotReady) |
| return; |
| |
| 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 != Complex && typesettingFeatures() && (runInfo.from || runInfo.to != runInfo.run.length())) |
| codePathToUse = Complex; |
| |
| if (codePathToUse != Complex) |
| return drawSimpleText(context, runInfo, point); |
| |
| return drawComplexText(context, runInfo, point); |
| } |
| |
| void Font::drawEmphasisMarks(GraphicsContext* context, const TextRunPaintInfo& runInfo, const AtomicString& mark, const FloatPoint& point) const |
| { |
| if (loadingCustomFonts()) |
| return; |
| |
| 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 != Complex && typesettingFeatures() && (runInfo.from || runInfo.to != runInfo.run.length())) |
| codePathToUse = Complex; |
| |
| if (codePathToUse != Complex) |
| drawEmphasisMarksForSimpleText(context, runInfo, mark, point); |
| else |
| drawEmphasisMarksForComplexText(context, runInfo, mark, point); |
| } |
| |
| float Font::width(const TextRun& run, HashSet<const SimpleFontData*>* fallbackFonts, GlyphOverflow* glyphOverflow) const |
| { |
| CodePath codePathToUse = codePath(run); |
| if (codePathToUse != Complex) { |
| // 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()) |
| fallbackFonts = 0; |
| // The simple path can optimize the case where glyph overflow is not observable. |
| if (codePathToUse != SimpleWithGlyphOverflow && (glyphOverflow && !glyphOverflow->computeBounds)) |
| glyphOverflow = 0; |
| } |
| |
| bool hasKerningOrLigatures = typesettingFeatures() & (Kerning | Ligatures); |
| bool hasWordSpacingOrLetterSpacing = wordSpacing() | letterSpacing(); |
| float* cacheEntry = m_fontFallbackList->widthCache().add(run, std::numeric_limits<float>::quiet_NaN(), hasKerningOrLigatures, hasWordSpacingOrLetterSpacing, glyphOverflow); |
| if (cacheEntry && !std::isnan(*cacheEntry)) |
| return *cacheEntry; |
| |
| float result; |
| if (codePathToUse == Complex) |
| result = floatWidthForComplexText(run, fallbackFonts, glyphOverflow); |
| else |
| result = floatWidthForSimpleText(run, fallbackFonts, glyphOverflow); |
| |
| if (cacheEntry && (!fallbackFonts || fallbackFonts->isEmpty())) |
| *cacheEntry = result; |
| return result; |
| } |
| |
| float Font::width(const TextRun& run, int& charsConsumed, String& glyphName) const |
| { |
| #if ENABLE(SVG_FONTS) |
| if (TextRun::RenderingContext* renderingContext = run.renderingContext()) |
| return renderingContext->floatWidthUsingSVGFont(*this, run, charsConsumed, glyphName); |
| #endif |
| |
| charsConsumed = run.length(); |
| glyphName = ""; |
| return width(run); |
| } |
| |
| #if !OS(DARWIN) |
| |
| PassOwnPtr<TextLayout> Font::createLayout(RenderText*, float, bool) const |
| { |
| return nullptr; |
| } |
| |
| void Font::deleteLayout(TextLayout*) |
| { |
| } |
| |
| float Font::width(TextLayout&, unsigned, unsigned, HashSet<const SimpleFontData*>*) |
| { |
| ASSERT_NOT_REACHED(); |
| return 0; |
| } |
| |
| #endif |
| |
| FloatRect Font::selectionRectForText(const TextRun& run, const FloatPoint& point, int h, int from, int to) const |
| { |
| to = (to == -1 ? run.length() : to); |
| |
| 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 != Complex && typesettingFeatures() && (from || to != run.length())) |
| codePathToUse = Complex; |
| |
| if (codePathToUse != Complex) |
| return selectionRectForSimpleText(run, point, h, from, to); |
| |
| return selectionRectForComplexText(run, point, h, from, to); |
| } |
| |
| 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) != Complex && !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; |
| } |
| |
| Font::CodePath Font::codePath() |
| { |
| return s_codePath; |
| } |
| |
| void Font::setDefaultTypesettingFeatures(TypesettingFeatures typesettingFeatures) |
| { |
| s_defaultTypesettingFeatures = typesettingFeatures; |
| } |
| |
| TypesettingFeatures Font::defaultTypesettingFeatures() |
| { |
| return s_defaultTypesettingFeatures; |
| } |
| |
| Font::CodePath Font::codePath(const TextRun& run) const |
| { |
| if (s_codePath != Auto) |
| return s_codePath; |
| |
| #if ENABLE(SVG_FONTS) |
| if (run.renderingContext()) |
| return Simple; |
| #endif |
| |
| if (m_fontDescription.featureSettings() && m_fontDescription.featureSettings()->size() > 0) |
| return Complex; |
| |
| if (run.length() > 1 && !WidthIterator::supportsTypesettingFeatures(*this)) |
| return Complex; |
| |
| if (!run.characterScanForCodePath()) |
| return Simple; |
| |
| if (run.is8Bit()) |
| return Simple; |
| |
| // Start from 0 since drawing and highlighting also measure the characters before run->from. |
| return characterRangeCodePath(run.characters16(), run.length()); |
| } |
| |
| Font::CodePath Font::characterRangeCodePath(const UChar* characters, unsigned len) |
| { |
| // FIXME: Should use a UnicodeSet in ports where ICU is used. Note that we |
| // can't simply use UnicodeCharacter Property/class because some characters |
| // are not 'combining', but still need to go to the complex path. |
| // Alternatively, we may as well consider binary search over a sorted |
| // list of ranges. |
| CodePath result = Simple; |
| for (unsigned i = 0; i < len; i++) { |
| const UChar c = characters[i]; |
| if (c < 0x2E5) // U+02E5 through U+02E9 (Modifier Letters : Tone letters) |
| continue; |
| if (c <= 0x2E9) |
| return Complex; |
| |
| if (c < 0x300) // U+0300 through U+036F Combining diacritical marks |
| continue; |
| if (c <= 0x36F) |
| return Complex; |
| |
| if (c < 0x0591 || c == 0x05BE) // U+0591 through U+05CF excluding U+05BE Hebrew combining marks, Hebrew punctuation Paseq, Sof Pasuq and Nun Hafukha |
| continue; |
| if (c <= 0x05CF) |
| return Complex; |
| |
| // 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 |
| if (c < 0x0600) |
| continue; |
| if (c <= 0x109F) |
| return Complex; |
| |
| // 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) |
| if (c < 0x1100) |
| continue; |
| if (c <= 0x11FF) |
| return Complex; |
| |
| if (c < 0x135D) // U+135D through U+135F Ethiopic combining marks |
| continue; |
| if (c <= 0x135F) |
| return Complex; |
| |
| if (c < 0x1700) // U+1780 through U+18AF Tagalog, Hanunoo, Buhid, Taghanwa,Khmer, Mongolian |
| continue; |
| if (c <= 0x18AF) |
| return Complex; |
| |
| if (c < 0x1900) // U+1900 through U+194F Limbu (Unicode 4.0) |
| continue; |
| if (c <= 0x194F) |
| return Complex; |
| |
| if (c < 0x1980) // U+1980 through U+19DF New Tai Lue |
| continue; |
| if (c <= 0x19DF) |
| return Complex; |
| |
| if (c < 0x1A00) // U+1A00 through U+1CFF Buginese, Tai Tham, Balinese, Batak, Lepcha, Vedic |
| continue; |
| if (c <= 0x1CFF) |
| return Complex; |
| |
| if (c < 0x1DC0) // U+1DC0 through U+1DFF Comining diacritical mark supplement |
| continue; |
| if (c <= 0x1DFF) |
| return Complex; |
| |
| // U+1E00 through U+2000 characters with diacritics and stacked diacritics |
| if (c <= 0x2000) { |
| result = SimpleWithGlyphOverflow; |
| continue; |
| } |
| |
| if (c < 0x20D0) // U+20D0 through U+20FF Combining marks for symbols |
| continue; |
| if (c <= 0x20FF) |
| return Complex; |
| |
| if (c < 0x2CEF) // U+2CEF through U+2CF1 Combining marks for Coptic |
| continue; |
| if (c <= 0x2CF1) |
| return Complex; |
| |
| if (c < 0x302A) // U+302A through U+302F Ideographic and Hangul Tone marks |
| continue; |
| if (c <= 0x302F) |
| return Complex; |
| |
| if (c < 0xA67C) // U+A67C through U+A67D Combining marks for old Cyrillic |
| continue; |
| if (c <= 0xA67D) |
| return Complex; |
| |
| if (c < 0xA6F0) // U+A6F0 through U+A6F1 Combining mark for Bamum |
| continue; |
| if (c <= 0xA6F1) |
| return Complex; |
| |
| // U+A800 through U+ABFF Nagri, Phags-pa, Saurashtra, Devanagari Extended, |
| // Hangul Jamo Ext. A, Javanese, Myanmar Extended A, Tai Viet, Meetei Mayek, |
| if (c < 0xA800) |
| continue; |
| if (c <= 0xABFF) |
| return Complex; |
| |
| if (c < 0xD7B0) // U+D7B0 through U+D7FF Hangul Jamo Ext. B |
| continue; |
| if (c <= 0xD7FF) |
| return Complex; |
| |
| if (c <= 0xDBFF) { |
| // High surrogate |
| |
| if (i == len - 1) |
| continue; |
| |
| UChar next = characters[++i]; |
| if (!U16_IS_TRAIL(next)) |
| continue; |
| |
| UChar32 supplementaryCharacter = U16_GET_SUPPLEMENTARY(c, next); |
| |
| if (supplementaryCharacter < 0x1F1E6) // U+1F1E6 through U+1F1FF Regional Indicator Symbols |
| continue; |
| if (supplementaryCharacter <= 0x1F1FF) |
| return Complex; |
| |
| if (supplementaryCharacter < 0xE0100) // U+E0100 through U+E01EF Unicode variation selectors. |
| continue; |
| if (supplementaryCharacter <= 0xE01EF) |
| return Complex; |
| |
| // FIXME: Check for Brahmi (U+11000 block), Kaithi (U+11080 block) and other complex scripts |
| // in plane 1 or higher. |
| |
| continue; |
| } |
| |
| if (c < 0xFE00) // U+FE00 through U+FE0F Unicode variation selectors |
| continue; |
| if (c <= 0xFE0F) |
| return Complex; |
| |
| if (c < 0xFE20) // U+FE20 through U+FE2F Combining half marks |
| continue; |
| if (c <= 0xFE2F) |
| return Complex; |
| } |
| return result; |
| } |
| |
| bool Font::isCJKIdeograph(UChar32 c) |
| { |
| // The basic CJK Unified Ideographs block. |
| if (c >= 0x4E00 && c <= 0x9FFF) |
| return true; |
| |
| // CJK Unified Ideographs Extension A. |
| if (c >= 0x3400 && c <= 0x4DBF) |
| return true; |
| |
| // CJK Radicals Supplement. |
| if (c >= 0x2E80 && c <= 0x2EFF) |
| return true; |
| |
| // Kangxi Radicals. |
| if (c >= 0x2F00 && c <= 0x2FDF) |
| return true; |
| |
| // CJK Strokes. |
| if (c >= 0x31C0 && c <= 0x31EF) |
| return true; |
| |
| // CJK Compatibility Ideographs. |
| if (c >= 0xF900 && c <= 0xFAFF) |
| return true; |
| |
| // CJK Unified Ideographs Extension B. |
| if (c >= 0x20000 && c <= 0x2A6DF) |
| return true; |
| |
| // CJK Unified Ideographs Extension C. |
| if (c >= 0x2A700 && c <= 0x2B73F) |
| return true; |
| |
| // CJK Unified Ideographs Extension D. |
| if (c >= 0x2B740 && c <= 0x2B81F) |
| return true; |
| |
| // CJK Compatibility Ideographs Supplement. |
| if (c >= 0x2F800 && c <= 0x2FA1F) |
| return true; |
| |
| return false; |
| } |
| |
| bool Font::isCJKIdeographOrSymbol(UChar32 c) |
| { |
| // 0x2C7 Caron, Mandarin Chinese 3rd Tone |
| // 0x2CA Modifier Letter Acute Accent, Mandarin Chinese 2nd Tone |
| // 0x2CB Modifier Letter Grave Access, Mandarin Chinese 4th Tone |
| // 0x2D9 Dot Above, Mandarin Chinese 5th Tone |
| if ((c == 0x2C7) || (c == 0x2CA) || (c == 0x2CB) || (c == 0x2D9)) |
| return true; |
| |
| if ((c == 0x2020) || (c == 0x2021) || (c == 0x2030) || (c == 0x203B) || (c == 0x203C) |
| || (c == 0x2042) || (c == 0x2047) || (c == 0x2048) || (c == 0x2049) || (c == 0x2051) |
| || (c == 0x20DD) || (c == 0x20DE) || (c == 0x2100) || (c == 0x2103) || (c == 0x2105) |
| || (c == 0x2109) || (c == 0x210A) || (c == 0x2113) || (c == 0x2116) || (c == 0x2121) |
| || (c == 0x212B) || (c == 0x213B) || (c == 0x2150) || (c == 0x2151) || (c == 0x2152)) |
| return true; |
| |
| if (c >= 0x2156 && c <= 0x215A) |
| return true; |
| |
| if (c >= 0x2160 && c <= 0x216B) |
| return true; |
| |
| if (c >= 0x2170 && c <= 0x217B) |
| return true; |
| |
| if ((c == 0x217F) || (c == 0x2189) || (c == 0x2307) || (c == 0x2312) || (c == 0x23BE) || (c == 0x23BF)) |
| return true; |
| |
| if (c >= 0x23C0 && c <= 0x23CC) |
| return true; |
| |
| if ((c == 0x23CE) || (c == 0x2423)) |
| return true; |
| |
| if (c >= 0x2460 && c <= 0x2492) |
| return true; |
| |
| if (c >= 0x249C && c <= 0x24FF) |
| return true; |
| |
| if ((c == 0x25A0) || (c == 0x25A1) || (c == 0x25A2) || (c == 0x25AA) || (c == 0x25AB)) |
| return true; |
| |
| if ((c == 0x25B1) || (c == 0x25B2) || (c == 0x25B3) || (c == 0x25B6) || (c == 0x25B7) || (c == 0x25BC) || (c == 0x25BD)) |
| return true; |
| |
| if ((c == 0x25C0) || (c == 0x25C1) || (c == 0x25C6) || (c == 0x25C7) || (c == 0x25C9) || (c == 0x25CB) || (c == 0x25CC)) |
| return true; |
| |
| if (c >= 0x25CE && c <= 0x25D3) |
| return true; |
| |
| if (c >= 0x25E2 && c <= 0x25E6) |
| return true; |
| |
| if (c == 0x25EF) |
| return true; |
| |
| if (c >= 0x2600 && c <= 0x2603) |
| return true; |
| |
| if ((c == 0x2605) || (c == 0x2606) || (c == 0x260E) || (c == 0x2616) || (c == 0x2617) || (c == 0x2640) || (c == 0x2642)) |
| return true; |
| |
| if (c >= 0x2660 && c <= 0x266F) |
| return true; |
| |
| if (c >= 0x2672 && c <= 0x267D) |
| return true; |
| |
| if ((c == 0x26A0) || (c == 0x26BD) || (c == 0x26BE) || (c == 0x2713) || (c == 0x271A) || (c == 0x273F) || (c == 0x2740) || (c == 0x2756)) |
| return true; |
| |
| if (c >= 0x2776 && c <= 0x277F) |
| return true; |
| |
| if (c == 0x2B1A) |
| return true; |
| |
| // Ideographic Description Characters. |
| if (c >= 0x2FF0 && c <= 0x2FFF) |
| return true; |
| |
| // CJK Symbols and Punctuation, excluding 0x3030. |
| if (c >= 0x3000 && c < 0x3030) |
| return true; |
| |
| if (c > 0x3030 && c <= 0x303F) |
| return true; |
| |
| // Hiragana |
| if (c >= 0x3040 && c <= 0x309F) |
| return true; |
| |
| // Katakana |
| if (c >= 0x30A0 && c <= 0x30FF) |
| return true; |
| |
| // Bopomofo |
| if (c >= 0x3100 && c <= 0x312F) |
| return true; |
| |
| if (c >= 0x3190 && c <= 0x319F) |
| return true; |
| |
| // Bopomofo Extended |
| if (c >= 0x31A0 && c <= 0x31BF) |
| return true; |
| |
| // Enclosed CJK Letters and Months. |
| if (c >= 0x3200 && c <= 0x32FF) |
| return true; |
| |
| // CJK Compatibility. |
| if (c >= 0x3300 && c <= 0x33FF) |
| return true; |
| |
| if (c >= 0xF860 && c <= 0xF862) |
| return true; |
| |
| // CJK Compatibility Forms. |
| if (c >= 0xFE30 && c <= 0xFE4F) |
| return true; |
| |
| if ((c == 0xFE10) || (c == 0xFE11) || (c == 0xFE12) || (c == 0xFE19)) |
| return true; |
| |
| if ((c == 0xFF0D) || (c == 0xFF1B) || (c == 0xFF1C) || (c == 0xFF1E)) |
| return false; |
| |
| // Halfwidth and Fullwidth Forms |
| // Usually only used in CJK |
| if (c >= 0xFF00 && c <= 0xFFEF) |
| return true; |
| |
| // Emoji. |
| if (c == 0x1F100) |
| return true; |
| |
| if (c >= 0x1F110 && c <= 0x1F129) |
| return true; |
| |
| if (c >= 0x1F130 && c <= 0x1F149) |
| return true; |
| |
| if (c >= 0x1F150 && c <= 0x1F169) |
| return true; |
| |
| if (c >= 0x1F170 && c <= 0x1F189) |
| return true; |
| |
| if (c >= 0x1F200 && c <= 0x1F6F) |
| return true; |
| |
| return isCJKIdeograph(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; |
| } |
| } else { |
| for (size_t i = length; i > 0; --i) { |
| if (treatAsSpace(characters[i - 1])) { |
| count++; |
| isAfterExpansion = true; |
| } else |
| isAfterExpansion = false; |
| } |
| } |
| return count; |
| } |
| |
| unsigned Font::expansionOpportunityCount(const UChar* characters, size_t length, TextDirection direction, bool& isAfterExpansion) |
| { |
| 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; |
| } |
| isAfterExpansion = false; |
| } |
| } 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 (expandAroundIdeographs && isCJKIdeographOrSymbol(character)) { |
| if (!isAfterExpansion) |
| count++; |
| count++; |
| isAfterExpansion = true; |
| continue; |
| } |
| isAfterExpansion = false; |
| } |
| } |
| return count; |
| } |
| |
| bool Font::canReceiveTextEmphasis(UChar32 c) |
| { |
| CharCategory category = Unicode::category(c); |
| if (category & (Separator_Space | Separator_Line | Separator_Paragraph | Other_NotAssigned | Other_Control | Other_Format)) |
| return false; |
| |
| // 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) |
| return false; |
| |
| return true; |
| } |
| |
| } |