summaryrefslogtreecommitdiffstats
path: root/WebCore/platform/graphics/Font.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'WebCore/platform/graphics/Font.cpp')
-rw-r--r--WebCore/platform/graphics/Font.cpp311
1 files changed, 47 insertions, 264 deletions
diff --git a/WebCore/platform/graphics/Font.cpp b/WebCore/platform/graphics/Font.cpp
index 82bf3b1..a78d27b 100644
--- a/WebCore/platform/graphics/Font.cpp
+++ b/WebCore/platform/graphics/Font.cpp
@@ -1,10 +1,8 @@
-/**
- * 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.
+ * Copyright (C) 2003, 2006 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
@@ -32,21 +30,14 @@
#include "FontFallbackList.h"
#include "IntPoint.h"
#include "GlyphBuffer.h"
-#include <wtf/unicode/Unicode.h>
+#include "WidthIterator.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 /*?*/,
@@ -58,250 +49,7 @@ const uint8_t Font::gRoundingHackCharacterTable[256] = {
0, 0, 0, 0, 0, 0, 0, 0, 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);
-
-#ifdef ANDROID_GLYPHBUFFER_HAS_ADJUSTED_WIDTHS
- bool
-#else
- void
-#endif
- 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);
- }
-}
-
-#ifdef ANDROID_GLYPHBUFFER_HAS_ADJUSTED_WIDTHS
-#define SIGNAL_ADJUSTED_WIDTHS() adjustedWidths = true
-bool WidthIterator::advance(int offset, GlyphBuffer* glyphBuffer)
-#else
-#define SIGNAL_ADJUSTED_WIDTHS()
-void WidthIterator::advance(int offset, GlyphBuffer* glyphBuffer)
-#endif
-{
- 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;
-
-#ifdef ANDROID_GLYPHBUFFER_HAS_ADJUSTED_WIDTHS
- bool adjustedWidths = false;
-#endif
-
- 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);
- SIGNAL_ADJUSTED_WIDTHS();
- } else {
- width = fontData->widthForGlyph(glyph);
-#ifndef ANDROID_NEVER_ROUND_FONT_METRICS
- // 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;
- SIGNAL_ADJUSTED_WIDTHS();
- }
-#endif
- }
-
- if (hasExtraSpacing && !m_run.spacingDisabled()) {
- // Account for letter-spacing.
- if (width && m_font->letterSpacing()) {
- width += m_font->letterSpacing();
- SIGNAL_ADJUSTED_WIDTHS();
- }
-
- 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;
- }
- SIGNAL_ADJUSTED_WIDTHS();
- }
-
- // 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();
- SIGNAL_ADJUSTED_WIDTHS();
- }
- }
- }
-
- // 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;
-
-#ifndef ANDROID_NEVER_ROUND_FONT_METRICS
- // 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);
- SIGNAL_ADJUSTED_WIDTHS();
- }
-
- // 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;
- SIGNAL_ADJUSTED_WIDTHS();
- }
-#endif
-
- runWidthSoFar += width;
-
- if (glyphBuffer)
- glyphBuffer->add(glyph, fontData, (rtl ? oldWidth + lastRoundingWidth : width));
-
- lastRoundingWidth = width - oldWidth;
- }
-
- m_currentCharacter = currentCharacter;
- m_runWidthSoFar = runWidthSoFar;
- m_finalRoundingWidth = lastRoundingWidth;
-
-#ifdef ANDROID_GLYPHBUFFER_HAS_ADJUSTED_WIDTHS
- return adjustedWidths;
-#endif
-}
-
-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::CodePath Font::s_codePath = Auto;
// ============================================================================================
// Font Implementation (Cross-Platform Portion)
@@ -327,7 +75,7 @@ Font::Font(const FontDescription& fd, short letterSpacing, short wordSpacing)
}
Font::Font(const FontPlatformData& fontData, bool isPrinterFont)
- : m_fontList(new FontFallbackList)
+ : m_fontList(FontFallbackList::create())
, m_pageZero(0)
, m_cachedPrimaryFont(0)
, m_letterSpacing(0)
@@ -381,7 +129,8 @@ bool Font::operator==(const Font& other) const
return first == second
&& m_fontDescription == other.m_fontDescription
&& m_letterSpacing == other.m_letterSpacing
- && m_wordSpacing == other.m_wordSpacing;
+ && 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
@@ -442,9 +191,12 @@ const GlyphData& Font::glyphDataForCharacter(UChar32 c, bool mirror, bool forceS
return data;
GlyphPageTreeNode* smallCapsNode = GlyphPageTreeNode::getRootChild(smallCapsFontData, pageNumber);
- const GlyphData& data = smallCapsNode->page()->glyphDataForCharacter(c);
- if (data.fontData)
- return data;
+ 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.
@@ -534,7 +286,7 @@ void Font::update(PassRefPtr<FontSelector> fontSelector) const
// 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 = new FontFallbackList();
+ m_fontList = FontFallbackList::create();
m_fontList->invalidate(fontSelector);
m_cachedPrimaryFont = 0;
m_pageZero = 0;
@@ -561,6 +313,11 @@ int Font::lineSpacing() const
return primaryFont()->lineSpacing();
}
+int Font::lineGap() const
+{
+ return primaryFont()->lineGap();
+}
+
float Font::xHeight() const
{
return primaryFont()->xHeight();
@@ -584,12 +341,17 @@ bool Font::isFixedPitch() const
void Font::setCodePath(CodePath p)
{
- codePath = p;
+ s_codePath = p;
+}
+
+Font::CodePath Font::codePath()
+{
+ return s_codePath;
}
bool Font::canUseGlyphCache(const TextRun& run) const
{
- switch (codePath) {
+ switch (s_codePath) {
case Auto:
break;
case Simple:
@@ -753,6 +515,20 @@ float Font::floatWidth(const TextRun& run) const
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);
@@ -846,6 +622,13 @@ int Font::offsetForPositionForSimpleText(const TextRun& run, int x, bool include
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;