| Index: webkit/pending/Font.cpp
|
| ===================================================================
|
| --- webkit/pending/Font.cpp (revision 2884)
|
| +++ webkit/pending/Font.cpp (working copy)
|
| @@ -1,839 +0,0 @@
|
| -/**
|
| - * This file is part of the html renderer for KDE.
|
| - *
|
| - * 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 Apple Computer, Inc.
|
| - *
|
| - * 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 "Font.h"
|
| -
|
| -#include "CharacterNames.h"
|
| -#include "FloatRect.h"
|
| -#include "FontCache.h"
|
| -#include "FontFallbackList.h"
|
| -#include "IntPoint.h"
|
| -#include "GlyphBuffer.h"
|
| -#include <wtf/unicode/Unicode.h>
|
| -#include <wtf/MathExtras.h>
|
| -
|
| -#if USE(ICU_UNICODE)
|
| -#include <unicode/unorm.h>
|
| -#endif
|
| -
|
| -using namespace WTF;
|
| -using namespace Unicode;
|
| -
|
| -namespace WebCore {
|
| -
|
| -// According to http://www.unicode.org/Public/UNIDATA/UCD.html#Canonical_Combining_Class_Values
|
| -const uint8_t hiraganaKatakanaVoicingMarksCombiningClass = 8;
|
| -
|
| -const uint8_t Font::gRoundingHackCharacterTable[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::codePath = Auto;
|
| -
|
| -struct WidthIterator {
|
| - WidthIterator(const Font* font, const TextRun& run);
|
| -
|
| - void advance(int to, GlyphBuffer* glyphBuffer = 0);
|
| - bool advanceOneCharacter(float& width, GlyphBuffer* glyphBuffer = 0);
|
| -
|
| - const Font* m_font;
|
| -
|
| - const TextRun& m_run;
|
| - int m_end;
|
| -
|
| - unsigned m_currentCharacter;
|
| - float m_runWidthSoFar;
|
| - float m_padding;
|
| - float m_padPerSpace;
|
| - float m_finalRoundingWidth;
|
| -
|
| -private:
|
| - UChar32 normalizeVoicingMarks(int currentCharacter);
|
| -};
|
| -
|
| -WidthIterator::WidthIterator(const Font* font, const TextRun& run)
|
| - : m_font(font)
|
| - , m_run(run)
|
| - , m_end(run.length())
|
| - , m_currentCharacter(0)
|
| - , m_runWidthSoFar(0)
|
| - , m_finalRoundingWidth(0)
|
| -{
|
| - // If the padding is non-zero, count the number of spaces in the run
|
| - // and divide that by the padding for per space addition.
|
| - m_padding = m_run.padding();
|
| - if (!m_padding)
|
| - m_padPerSpace = 0;
|
| - else {
|
| - float numSpaces = 0;
|
| - for (int i = 0; i < run.length(); i++)
|
| - if (Font::treatAsSpace(m_run[i]))
|
| - numSpaces++;
|
| -
|
| - if (numSpaces == 0)
|
| - m_padPerSpace = 0;
|
| - else
|
| - m_padPerSpace = ceilf(m_run.padding() / numSpaces);
|
| - }
|
| -}
|
| -
|
| -void WidthIterator::advance(int offset, GlyphBuffer* glyphBuffer)
|
| -{
|
| - if (offset > m_end)
|
| - offset = m_end;
|
| -
|
| - int currentCharacter = m_currentCharacter;
|
| - const UChar* cp = m_run.data(currentCharacter);
|
| -
|
| - bool rtl = m_run.rtl();
|
| - bool hasExtraSpacing = m_font->letterSpacing() || m_font->wordSpacing() || m_padding;
|
| -
|
| - float runWidthSoFar = m_runWidthSoFar;
|
| - float lastRoundingWidth = m_finalRoundingWidth;
|
| -
|
| - while (currentCharacter < offset) {
|
| - UChar32 c = *cp;
|
| - unsigned clusterLength = 1;
|
| - if (c >= 0x3041) {
|
| - if (c <= 0x30FE) {
|
| - // Deal with Hiragana and Katakana voiced and semi-voiced syllables.
|
| - // Normalize into composed form, and then look for glyph with base + combined mark.
|
| - // Check above for character range to minimize performance impact.
|
| - UChar32 normalized = normalizeVoicingMarks(currentCharacter);
|
| - if (normalized) {
|
| - c = normalized;
|
| - clusterLength = 2;
|
| - }
|
| - } else if (U16_IS_SURROGATE(c)) {
|
| - if (!U16_IS_SURROGATE_LEAD(c))
|
| - break;
|
| -
|
| - // Do we have a surrogate pair? If so, determine the full Unicode (32 bit)
|
| - // code point before glyph lookup.
|
| - // Make sure we have another character and it's a low surrogate.
|
| - if (currentCharacter + 1 >= m_run.length())
|
| - break;
|
| - UChar low = cp[1];
|
| - if (!U16_IS_TRAIL(low))
|
| - break;
|
| - c = U16_GET_SUPPLEMENTARY(c, low);
|
| - clusterLength = 2;
|
| - }
|
| - }
|
| -
|
| - const GlyphData& glyphData = m_font->glyphDataForCharacter(c, rtl);
|
| - Glyph glyph = glyphData.glyph;
|
| - const SimpleFontData* fontData = glyphData.fontData;
|
| -
|
| - ASSERT(fontData);
|
| -
|
| - // Now that we have a glyph and font data, get its width.
|
| - float width;
|
| - if (c == '\t' && m_run.allowTabs()) {
|
| - float tabWidth = m_font->tabWidth();
|
| - width = tabWidth - fmodf(m_run.xPos() + runWidthSoFar, tabWidth);
|
| - } else {
|
| - width = fontData->widthForGlyph(glyph);
|
| - // We special case spaces in two ways when applying word rounding.
|
| - // First, we round spaces to an adjusted width in all fonts.
|
| - // Second, in fixed-pitch fonts we ensure that all characters that
|
| - // match the width of the space character have the same width as the space character.
|
| - if (width == fontData->m_spaceWidth && (fontData->m_treatAsFixedPitch || glyph == fontData->m_spaceGlyph) && m_run.applyWordRounding())
|
| - width = fontData->m_adjustedSpaceWidth;
|
| - }
|
| -
|
| - if (hasExtraSpacing && !m_run.spacingDisabled()) {
|
| - // Account for letter-spacing.
|
| - if (width && m_font->letterSpacing())
|
| - width += m_font->letterSpacing();
|
| -
|
| - if (Font::treatAsSpace(c)) {
|
| - // Account for padding. WebCore uses space padding to justify text.
|
| - // We distribute the specified padding over the available spaces in the run.
|
| - if (m_padding) {
|
| - // Use left over padding if not evenly divisible by number of spaces.
|
| - if (m_padding < m_padPerSpace) {
|
| - width += m_padding;
|
| - m_padding = 0;
|
| - } else {
|
| - width += m_padPerSpace;
|
| - m_padding -= m_padPerSpace;
|
| - }
|
| - }
|
| -
|
| - // Account for word spacing.
|
| - // We apply additional space between "words" by adding width to the space character.
|
| - if (currentCharacter != 0 && !Font::treatAsSpace(cp[-1]) && m_font->wordSpacing())
|
| - width += m_font->wordSpacing();
|
| - }
|
| - }
|
| -
|
| - // Advance past the character we just dealt with.
|
| - cp += clusterLength;
|
| - currentCharacter += clusterLength;
|
| -
|
| - // Account for float/integer impedance mismatch between CG and KHTML. "Words" (characters
|
| - // followed by a character defined by isRoundingHackCharacter()) are always an integer width.
|
| - // We adjust the width of the last character of a "word" to ensure an integer width.
|
| - // If we move KHTML to floats we can remove this (and related) hacks.
|
| -
|
| - float oldWidth = width;
|
| -
|
| - // Force characters that are used to determine word boundaries for the rounding hack
|
| - // to be integer width, so following words will start on an integer boundary.
|
| - if (m_run.applyWordRounding() && Font::isRoundingHackCharacter(c))
|
| - width = ceilf(width);
|
| -
|
| - // Check to see if the next character is a "rounding hack character", if so, adjust
|
| - // width so that the total run width will be on an integer boundary.
|
| - if ((m_run.applyWordRounding() && currentCharacter < m_run.length() && Font::isRoundingHackCharacter(*cp))
|
| - || (m_run.applyRunRounding() && currentCharacter >= m_end)) {
|
| - float totalWidth = runWidthSoFar + width;
|
| - width += ceilf(totalWidth) - totalWidth;
|
| - }
|
| -
|
| - runWidthSoFar += width;
|
| -
|
| - if (glyphBuffer)
|
| - glyphBuffer->add(glyph, fontData, (rtl ? oldWidth + lastRoundingWidth : width));
|
| -
|
| - lastRoundingWidth = width - oldWidth;
|
| - }
|
| -
|
| - m_currentCharacter = currentCharacter;
|
| - m_runWidthSoFar = runWidthSoFar;
|
| - m_finalRoundingWidth = lastRoundingWidth;
|
| -}
|
| -
|
| -bool WidthIterator::advanceOneCharacter(float& width, GlyphBuffer* glyphBuffer)
|
| -{
|
| - glyphBuffer->clear();
|
| - advance(m_currentCharacter + 1, glyphBuffer);
|
| - float w = 0;
|
| - for (int i = 0; i < glyphBuffer->size(); ++i)
|
| - w += glyphBuffer->advanceAt(i);
|
| - width = w;
|
| - return !glyphBuffer->isEmpty();
|
| -}
|
| -
|
| -UChar32 WidthIterator::normalizeVoicingMarks(int currentCharacter)
|
| -{
|
| - if (currentCharacter + 1 < m_end) {
|
| - if (combiningClass(m_run[currentCharacter + 1]) == hiraganaKatakanaVoicingMarksCombiningClass) {
|
| -#if USE(ICU_UNICODE)
|
| - // Normalize into composed form using 3.2 rules.
|
| - UChar normalizedCharacters[2] = { 0, 0 };
|
| - UErrorCode uStatus = U_ZERO_ERROR;
|
| - int32_t resultLength = unorm_normalize(m_run.data(currentCharacter), 2,
|
| - UNORM_NFC, UNORM_UNICODE_3_2, &normalizedCharacters[0], 2, &uStatus);
|
| - if (resultLength == 1 && uStatus == 0)
|
| - return normalizedCharacters[0];
|
| -#elif USE(QT4_UNICODE)
|
| - QString tmp(reinterpret_cast<const QChar*>(m_run.data(currentCharacter)), 2);
|
| - QString res = tmp.normalized(QString::NormalizationForm_C, QChar::Unicode_3_2);
|
| - if (res.length() == 1)
|
| - return res.at(0).unicode();
|
| -#endif
|
| - }
|
| - }
|
| - return 0;
|
| -}
|
| -
|
| -// ============================================================================================
|
| -// Font Implementation (Cross-Platform Portion)
|
| -// ============================================================================================
|
| -
|
| -Font::Font()
|
| - : m_pageZero(0)
|
| - , m_cachedPrimaryFont(0)
|
| - , m_letterSpacing(0)
|
| - , m_wordSpacing(0)
|
| - , m_isPlatformFont(false)
|
| -{
|
| -}
|
| -
|
| -Font::Font(const FontDescription& fd, short letterSpacing, short wordSpacing)
|
| - : m_fontDescription(fd)
|
| - , m_pageZero(0)
|
| - , m_cachedPrimaryFont(0)
|
| - , m_letterSpacing(letterSpacing)
|
| - , m_wordSpacing(wordSpacing)
|
| - , m_isPlatformFont(false)
|
| -{
|
| -}
|
| -
|
| -Font::Font(const FontPlatformData& fontData, bool isPrinterFont)
|
| - : m_fontList(FontFallbackList::create())
|
| - , m_pageZero(0)
|
| - , m_cachedPrimaryFont(0)
|
| - , m_letterSpacing(0)
|
| - , m_wordSpacing(0)
|
| - , m_isPlatformFont(true)
|
| -{
|
| - m_fontDescription.setUsePrinterFont(isPrinterFont);
|
| - m_fontList->setPlatformFont(fontData);
|
| -}
|
| -
|
| -Font::Font(const Font& other)
|
| - : m_fontDescription(other.m_fontDescription)
|
| - , m_fontList(other.m_fontList)
|
| - , m_pages(other.m_pages)
|
| - , m_pageZero(other.m_pageZero)
|
| - , m_cachedPrimaryFont(other.m_cachedPrimaryFont)
|
| - , m_letterSpacing(other.m_letterSpacing)
|
| - , m_wordSpacing(other.m_wordSpacing)
|
| - , m_isPlatformFont(other.m_isPlatformFont)
|
| -{
|
| -}
|
| -
|
| -Font& Font::operator=(const Font& other)
|
| -{
|
| - m_fontDescription = other.m_fontDescription;
|
| - m_fontList = other.m_fontList;
|
| - m_pages = other.m_pages;
|
| - m_pageZero = other.m_pageZero;
|
| - m_cachedPrimaryFont = other.m_cachedPrimaryFont;
|
| - m_letterSpacing = other.m_letterSpacing;
|
| - m_wordSpacing = other.m_wordSpacing;
|
| - m_isPlatformFont = other.m_isPlatformFont;
|
| - return *this;
|
| -}
|
| -
|
| -Font::~Font()
|
| -{
|
| -}
|
| -
|
| -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 ((m_fontList && m_fontList->loadingCustomFonts()) ||
|
| - (other.m_fontList && other.m_fontList->loadingCustomFonts()))
|
| - return false;
|
| -
|
| - FontSelector* first = m_fontList ? m_fontList->fontSelector() : 0;
|
| - FontSelector* second = other.m_fontList ? other.m_fontList->fontSelector() : 0;
|
| -
|
| - return first == second
|
| - && m_fontDescription == other.m_fontDescription
|
| - && m_letterSpacing == other.m_letterSpacing
|
| - && m_wordSpacing == other.m_wordSpacing
|
| - && (m_fontList ? m_fontList->generation() : 0) == (other.m_fontList ? other.m_fontList->generation() : 0);
|
| -}
|
| -
|
| -const GlyphData& Font::glyphDataForCharacter(UChar32 c, bool mirror, bool forceSmallCaps) const
|
| -{
|
| - bool useSmallCapsFont = forceSmallCaps;
|
| - if (m_fontDescription.smallCaps()) {
|
| - UChar32 upperC = Unicode::toUpper(c);
|
| - if (upperC != c) {
|
| - c = upperC;
|
| - useSmallCapsFont = true;
|
| - }
|
| - }
|
| -
|
| - if (mirror)
|
| - c = mirroredChar(c);
|
| -
|
| - unsigned pageNumber = (c / GlyphPage::size);
|
| -
|
| - GlyphPageTreeNode* node = pageNumber ? m_pages.get(pageNumber) : m_pageZero;
|
| - if (!node) {
|
| - node = GlyphPageTreeNode::getRootChild(fontDataAt(0), pageNumber);
|
| - if (pageNumber)
|
| - m_pages.set(pageNumber, node);
|
| - else
|
| - m_pageZero = node;
|
| - }
|
| -
|
| - GlyphPage* page;
|
| - if (!useSmallCapsFont) {
|
| - // Fastest loop, for the common case (not small caps).
|
| - while (true) {
|
| - page = node->page();
|
| - if (page) {
|
| - const GlyphData& data = page->glyphDataForCharacter(c);
|
| - if (data.fontData)
|
| - return data;
|
| - if (node->isSystemFallback())
|
| - break;
|
| - }
|
| -
|
| - // Proceed with the fallback list.
|
| - node = node->getChild(fontDataAt(node->level()), pageNumber);
|
| - if (pageNumber)
|
| - m_pages.set(pageNumber, node);
|
| - else
|
| - m_pageZero = node;
|
| - }
|
| - } else {
|
| - while (true) {
|
| - page = node->page();
|
| - if (page) {
|
| - const GlyphData& data = page->glyphDataForCharacter(c);
|
| - if (data.fontData) {
|
| - // The smallCapsFontData function should not normally return 0.
|
| - // But if it does, we will just render the capital letter big.
|
| - const SimpleFontData* smallCapsFontData = data.fontData->smallCapsFontData(m_fontDescription);
|
| - if (!smallCapsFontData)
|
| - return data;
|
| -
|
| - GlyphPageTreeNode* smallCapsNode = GlyphPageTreeNode::getRootChild(smallCapsFontData, pageNumber);
|
| - const GlyphPage* smallCapsPage = smallCapsNode->page();
|
| - if (smallCapsPage) {
|
| - const GlyphData& data = smallCapsPage->glyphDataForCharacter(c);
|
| - if (data.fontData)
|
| - return data;
|
| - }
|
| -
|
| - // Do not attempt system fallback off the smallCapsFontData. 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 smallCapsFontData->missingGlyphData();
|
| - }
|
| -
|
| - if (node->isSystemFallback())
|
| - break;
|
| - }
|
| -
|
| - // Proceed with the fallback list.
|
| - node = node->getChild(fontDataAt(node->level()), pageNumber);
|
| - if (pageNumber)
|
| - m_pages.set(pageNumber, node);
|
| - else
|
| - m_pageZero = node;
|
| - }
|
| - }
|
| -
|
| - 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.
|
| - UChar codeUnits[2];
|
| - int codeUnitsLength;
|
| - if (c <= 0xFFFF) {
|
| - UChar c16 = c;
|
| - if (Font::treatAsSpace(c16))
|
| - codeUnits[0] = ' ';
|
| - else if (Font::treatAsZeroWidthSpace(c16))
|
| - codeUnits[0] = zeroWidthSpace;
|
| - else
|
| - codeUnits[0] = c16;
|
| - codeUnitsLength = 1;
|
| - } else {
|
| - codeUnits[0] = U16_LEAD(c);
|
| - codeUnits[1] = U16_TRAIL(c);
|
| - codeUnitsLength = 2;
|
| - }
|
| - const SimpleFontData* characterFontData = FontCache::getFontDataForCharacters(*this, codeUnits, codeUnitsLength);
|
| - if (useSmallCapsFont)
|
| - characterFontData = characterFontData->smallCapsFontData(m_fontDescription);
|
| - if (characterFontData) {
|
| - // Got the fallback glyph and font.
|
| - GlyphPage* fallbackPage = GlyphPageTreeNode::getRootChild(characterFontData, pageNumber)->page();
|
| - const GlyphData& data = fallbackPage && fallbackPage->glyphDataForCharacter(c).fontData ? fallbackPage->glyphDataForCharacter(c) : characterFontData->missingGlyphData();
|
| - // Cache it so we don't have to do system fallback again next time.
|
| - if (!useSmallCapsFont)
|
| - page->setGlyphDataForCharacter(c, data.glyph, data.fontData);
|
| - return data;
|
| - }
|
| -
|
| - // 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.
|
| - const GlyphData& data = primaryFont()->missingGlyphData();
|
| - if (!useSmallCapsFont)
|
| - page->setGlyphDataForCharacter(c, data.glyph, data.fontData);
|
| - return data;
|
| -}
|
| -
|
| -void Font::cachePrimaryFont() const
|
| -{
|
| - ASSERT(m_fontList);
|
| - ASSERT(!m_cachedPrimaryFont);
|
| - m_cachedPrimaryFont = m_fontList->primaryFont(this)->fontDataForCharacter(' ');
|
| -}
|
| -
|
| -const FontData* Font::fontDataAt(unsigned index) const
|
| -{
|
| - ASSERT(m_fontList);
|
| - return m_fontList->fontDataAt(this, index);
|
| -}
|
| -
|
| -const FontData* Font::fontDataForCharacters(const UChar* characters, int length) const
|
| -{
|
| - ASSERT(m_fontList);
|
| - return m_fontList->fontDataForCharacters(this, characters, length);
|
| -}
|
| -
|
| -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_fontList)
|
| - m_fontList = FontFallbackList::create();
|
| - m_fontList->invalidate(fontSelector);
|
| - m_cachedPrimaryFont = 0;
|
| - m_pageZero = 0;
|
| - m_pages.clear();
|
| -}
|
| -
|
| -int Font::width(const TextRun& run) const
|
| -{
|
| - return lroundf(floatWidth(run));
|
| -}
|
| -
|
| -int Font::ascent() const
|
| -{
|
| - return primaryFont()->ascent();
|
| -}
|
| -
|
| -int Font::descent() const
|
| -{
|
| - return primaryFont()->descent();
|
| -}
|
| -
|
| -int Font::lineSpacing() const
|
| -{
|
| - return primaryFont()->lineSpacing();
|
| -}
|
| -
|
| -int Font::lineGap() const
|
| -{
|
| - return primaryFont()->lineGap();
|
| -}
|
| -
|
| -float Font::xHeight() const
|
| -{
|
| - return primaryFont()->xHeight();
|
| -}
|
| -
|
| -unsigned Font::unitsPerEm() const
|
| -{
|
| - return primaryFont()->unitsPerEm();
|
| -}
|
| -
|
| -int Font::spaceWidth() const
|
| -{
|
| - return (int)ceilf(primaryFont()->m_adjustedSpaceWidth + m_letterSpacing);
|
| -}
|
| -
|
| -bool Font::isFixedPitch() const
|
| -{
|
| - ASSERT(m_fontList);
|
| - return m_fontList->isFixedPitch(this);
|
| -}
|
| -
|
| -void Font::setCodePath(CodePath p)
|
| -{
|
| - codePath = p;
|
| -}
|
| -
|
| -bool Font::canUseGlyphCache(const TextRun& run) const
|
| -{
|
| - switch (codePath) {
|
| - case Auto:
|
| - break;
|
| - case Simple:
|
| - return true;
|
| - case Complex:
|
| - return false;
|
| - }
|
| -
|
| - // Start from 0 since drawing and highlighting also measure the characters before run->from
|
| - for (int i = 0; i < run.length(); i++) {
|
| - const UChar c = run[i];
|
| - if (c < 0x300) // U+0300 through U+036F Combining diacritical marks
|
| - continue;
|
| - if (c <= 0x36F)
|
| - return false;
|
| -
|
| - 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 false;
|
| -
|
| - if (c < 0x0600) // U+0600 through U+1059 Arabic, Syriac, Thaana, Devanagari, Bengali, Gurmukhi, Gujarati, Oriya, Tamil, Telugu, Kannada, Malayalam, Sinhala, Thai, Lao, Tibetan, Myanmar
|
| - continue;
|
| - if (c <= 0x1059)
|
| - return false;
|
| -
|
| - if (c < 0x1100) // 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)
|
| - continue;
|
| - if (c <= 0x11FF)
|
| - return false;
|
| -
|
| - if (c < 0x1780) // U+1780 through U+18AF Khmer, Mongolian
|
| - continue;
|
| - if (c <= 0x18AF)
|
| - return false;
|
| -
|
| - if (c < 0x1900) // U+1900 through U+194F Limbu (Unicode 4.0)
|
| - continue;
|
| - if (c <= 0x194F)
|
| - return false;
|
| -
|
| - if (c < 0x20D0) // U+20D0 through U+20FF Combining marks for symbols
|
| - continue;
|
| - if (c <= 0x20FF)
|
| - return false;
|
| -
|
| - if (c < 0xFE20) // U+FE20 through U+FE2F Combining half marks
|
| - continue;
|
| - if (c <= 0xFE2F)
|
| - return false;
|
| - }
|
| -
|
| - return true;
|
| -
|
| -}
|
| -
|
| -void Font::drawSimpleText(GraphicsContext* context, const TextRun& run, const FloatPoint& point, int from, int to) const
|
| -{
|
| - // This glyph buffer holds our glyphs+advances+font data for each glyph.
|
| - GlyphBuffer glyphBuffer;
|
| -
|
| - float startX = point.x();
|
| - WidthIterator it(this, run);
|
| - it.advance(from);
|
| - float beforeWidth = it.m_runWidthSoFar;
|
| - it.advance(to, &glyphBuffer);
|
| -
|
| - // We couldn't generate any glyphs for the run. Give up.
|
| - if (glyphBuffer.isEmpty())
|
| - return;
|
| -
|
| - float afterWidth = it.m_runWidthSoFar;
|
| -
|
| - if (run.rtl()) {
|
| - float finalRoundingWidth = it.m_finalRoundingWidth;
|
| - it.advance(run.length());
|
| - startX += finalRoundingWidth + it.m_runWidthSoFar - afterWidth;
|
| - } else
|
| - startX += beforeWidth;
|
| -
|
| - // Swap the order of the glyphs if right-to-left.
|
| - if (run.rtl())
|
| - for (int i = 0, end = glyphBuffer.size() - 1; i < glyphBuffer.size() / 2; ++i, --end)
|
| - glyphBuffer.swap(i, end);
|
| -
|
| - // Calculate the starting point of the glyphs to be displayed by adding
|
| - // all the advances up to the first glyph.
|
| - FloatPoint startPoint(startX, point.y());
|
| - drawGlyphBuffer(context, glyphBuffer, run, startPoint);
|
| -}
|
| -
|
| -void Font::drawGlyphBuffer(GraphicsContext* context, const GlyphBuffer& glyphBuffer,
|
| - const TextRun& run, const FloatPoint& point) const
|
| -{
|
| - // Draw each contiguous run of glyphs that use the same font data.
|
| - const SimpleFontData* fontData = glyphBuffer.fontDataAt(0);
|
| - FloatSize offset = glyphBuffer.offsetAt(0);
|
| - FloatPoint startPoint(point);
|
| - float nextX = startPoint.x();
|
| - int lastFrom = 0;
|
| - int nextGlyph = 0;
|
| - while (nextGlyph < glyphBuffer.size()) {
|
| - const SimpleFontData* nextFontData = glyphBuffer.fontDataAt(nextGlyph);
|
| - FloatSize nextOffset = glyphBuffer.offsetAt(nextGlyph);
|
| - if (nextFontData != fontData || nextOffset != offset) {
|
| - drawGlyphs(context, fontData, glyphBuffer, lastFrom, nextGlyph - lastFrom, startPoint);
|
| -
|
| - lastFrom = nextGlyph;
|
| - fontData = nextFontData;
|
| - offset = nextOffset;
|
| - startPoint.setX(nextX);
|
| - }
|
| - nextX += glyphBuffer.advanceAt(nextGlyph);
|
| - nextGlyph++;
|
| - }
|
| -
|
| - drawGlyphs(context, fontData, glyphBuffer, lastFrom, nextGlyph - lastFrom, startPoint);
|
| -}
|
| -
|
| -void Font::drawText(GraphicsContext* context, const TextRun& run, const FloatPoint& point, int from, int to) const
|
| -{
|
| - // Don't draw anything while we are using custom fonts that are in the process of loading.
|
| - if (m_fontList && m_fontList->loadingCustomFonts())
|
| - return;
|
| -
|
| - to = (to == -1 ? run.length() : to);
|
| -
|
| -#if ENABLE(SVG_FONTS)
|
| - if (primaryFont()->isSVGFont()) {
|
| - drawTextUsingSVGFont(context, run, point, from, to);
|
| - return;
|
| - }
|
| -#endif
|
| -
|
| - if (canUseGlyphCache(run))
|
| - drawSimpleText(context, run, point, from, to);
|
| - else
|
| - drawComplexText(context, run, point, from, to);
|
| -}
|
| -
|
| -float Font::floatWidth(const TextRun& run) const
|
| -{
|
| -#if ENABLE(SVG_FONTS)
|
| - if (primaryFont()->isSVGFont())
|
| - return floatWidthUsingSVGFont(run);
|
| -#endif
|
| -
|
| - if (canUseGlyphCache(run))
|
| - return floatWidthForSimpleText(run, 0);
|
| - return floatWidthForComplexText(run);
|
| -}
|
| -
|
| -float Font::floatWidth(const TextRun& run, int extraCharsAvailable, int& charsConsumed, String& glyphName) const
|
| -{
|
| -#if ENABLE(SVG_FONTS)
|
| - if (primaryFont()->isSVGFont())
|
| - return floatWidthUsingSVGFont(run, extraCharsAvailable, charsConsumed, glyphName);
|
| -#endif
|
| -
|
| - charsConsumed = run.length();
|
| - glyphName = "";
|
| - if (canUseGlyphCache(run))
|
| - return floatWidthForSimpleText(run, 0);
|
| - return floatWidthForComplexText(run);
|
| -}
|
| -
|
| -float Font::floatWidthForSimpleText(const TextRun& run, GlyphBuffer* glyphBuffer) const
|
| -{
|
| - WidthIterator it(this, run);
|
| - it.advance(run.length(), glyphBuffer);
|
| - return it.m_runWidthSoFar;
|
| -}
|
| -
|
| -FloatRect Font::selectionRectForText(const TextRun& run, const IntPoint& point, int h, int from, int to) const
|
| -{
|
| -#if ENABLE(SVG_FONTS)
|
| - if (primaryFont()->isSVGFont())
|
| - return selectionRectForTextUsingSVGFont(run, point, h, from, to);
|
| -#endif
|
| -
|
| - to = (to == -1 ? run.length() : to);
|
| - if (canUseGlyphCache(run))
|
| - return selectionRectForSimpleText(run, point, h, from, to);
|
| - return selectionRectForComplexText(run, point, h, from, to);
|
| -}
|
| -
|
| -FloatRect Font::selectionRectForSimpleText(const TextRun& run, const IntPoint& point, int h, int from, int to) const
|
| -{
|
| - WidthIterator it(this, run);
|
| - it.advance(from);
|
| - float beforeWidth = it.m_runWidthSoFar;
|
| - it.advance(to);
|
| - float afterWidth = it.m_runWidthSoFar;
|
| -
|
| - // Using roundf() rather than ceilf() for the right edge as a compromise to ensure correct caret positioning
|
| - if (run.rtl()) {
|
| - it.advance(run.length());
|
| - float totalWidth = it.m_runWidthSoFar;
|
| - return FloatRect(point.x() + floorf(totalWidth - afterWidth), point.y(), roundf(totalWidth - beforeWidth) - floorf(totalWidth - afterWidth), h);
|
| - } else {
|
| - return FloatRect(point.x() + floorf(beforeWidth), point.y(), roundf(afterWidth) - floorf(beforeWidth), h);
|
| - }
|
| -}
|
| -
|
| -int Font::offsetForPosition(const TextRun& run, int x, bool includePartialGlyphs) const
|
| -{
|
| -#if ENABLE(SVG_FONTS)
|
| - if (primaryFont()->isSVGFont())
|
| - return offsetForPositionForTextUsingSVGFont(run, x, includePartialGlyphs);
|
| -#endif
|
| -
|
| - if (canUseGlyphCache(run))
|
| - return offsetForPositionForSimpleText(run, x, includePartialGlyphs);
|
| - return offsetForPositionForComplexText(run, x, includePartialGlyphs);
|
| -}
|
| -
|
| -int Font::offsetForPositionForSimpleText(const TextRun& run, int x, bool includePartialGlyphs) const
|
| -{
|
| - float delta = (float)x;
|
| -
|
| - WidthIterator it(this, run);
|
| - GlyphBuffer localGlyphBuffer;
|
| - unsigned offset;
|
| - if (run.rtl()) {
|
| - delta -= floatWidthForSimpleText(run, 0);
|
| - 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;
|
| -}
|
| -
|
| -#if ENABLE(SVG_FONTS)
|
| -bool Font::isSVGFont() const
|
| -{
|
| - return primaryFont()->isSVGFont();
|
| -}
|
| -#endif
|
| -
|
| -FontSelector* Font::fontSelector() const
|
| -{
|
| - return m_fontList ? m_fontList->fontSelector() : 0;
|
| -}
|
| -
|
| -}
|
|
|