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.cpp626
1 files changed, 626 insertions, 0 deletions
diff --git a/WebCore/platform/graphics/Font.cpp b/WebCore/platform/graphics/Font.cpp
new file mode 100644
index 0000000..138e322
--- /dev/null
+++ b/WebCore/platform/graphics/Font.cpp
@@ -0,0 +1,626 @@
+/*
+ * 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 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 "Font.h"
+
+#include "CharacterNames.h"
+#include "FloatRect.h"
+#include "FontCache.h"
+#include "FontFallbackList.h"
+#include "IntPoint.h"
+#include "GlyphBuffer.h"
+#include "WidthIterator.h"
+#include <wtf/MathExtras.h>
+
+using namespace WTF;
+using namespace Unicode;
+
+namespace WebCore {
+
+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::s_codePath = Auto;
+
+// ============================================================================================
+// 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)
+{
+ s_codePath = p;
+}
+
+Font::CodePath Font::codePath()
+{
+ return s_codePath;
+}
+
+bool Font::canUseGlyphCache(const TextRun& run) const
+{
+ switch (s_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;
+}
+
+}