summaryrefslogtreecommitdiffstats
path: root/WebCore/platform/graphics/qt/FontQt.cpp
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2009-03-03 19:30:52 -0800
committerThe Android Open Source Project <initial-contribution@android.com>2009-03-03 19:30:52 -0800
commit8e35f3cfc7fba1d1c829dc557ebad6409cbe16a2 (patch)
tree11425ea0b299d6fb89c6d3618a22d97d5bf68d0f /WebCore/platform/graphics/qt/FontQt.cpp
parent648161bb0edfc3d43db63caed5cc5213bc6cb78f (diff)
downloadexternal_webkit-8e35f3cfc7fba1d1c829dc557ebad6409cbe16a2.zip
external_webkit-8e35f3cfc7fba1d1c829dc557ebad6409cbe16a2.tar.gz
external_webkit-8e35f3cfc7fba1d1c829dc557ebad6409cbe16a2.tar.bz2
auto import from //depot/cupcake/@135843
Diffstat (limited to 'WebCore/platform/graphics/qt/FontQt.cpp')
-rw-r--r--WebCore/platform/graphics/qt/FontQt.cpp706
1 files changed, 706 insertions, 0 deletions
diff --git a/WebCore/platform/graphics/qt/FontQt.cpp b/WebCore/platform/graphics/qt/FontQt.cpp
new file mode 100644
index 0000000..e2ef605
--- /dev/null
+++ b/WebCore/platform/graphics/qt/FontQt.cpp
@@ -0,0 +1,706 @@
+/*
+ Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
+
+ 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 "FontDescription.h"
+#include "FontSelector.h"
+
+#include "GraphicsContext.h"
+#include <QTextLayout>
+#include <QPainter>
+#include <QFontMetrics>
+#include <QFontInfo>
+#include <qalgorithms.h>
+#include <qdebug.h>
+
+#include <limits.h>
+namespace WebCore {
+
+#if QT_VERSION >= 0x040400
+
+Font::Font()
+ : m_letterSpacing(0)
+ , m_wordSpacing(0)
+ , m_font()
+ , m_scFont()
+{
+ QFontMetrics metrics(m_font);
+ m_spaceWidth = metrics.width(QLatin1Char(' '));
+}
+
+Font::Font(const FontDescription& description, short letterSpacing, short wordSpacing)
+ : m_fontDescription(description)
+ , m_letterSpacing(letterSpacing)
+ , m_wordSpacing(wordSpacing)
+{
+ const FontFamily* family = &description.family();
+ QString familyName;
+ while (family) {
+ familyName += family->family();
+ family = family->next();
+ if (family)
+ familyName += QLatin1Char(',');
+ }
+
+ m_font.setFamily(familyName);
+ m_font.setPixelSize(qRound(description.computedSize()));
+ m_font.setItalic(description.italic());
+ // FIXME: Map all FontWeight values to QFont weights.
+ if (description.weight() >= FontWeight600)
+ m_font.setWeight(QFont::Bold);
+ else
+ m_font.setWeight(QFont::Normal);
+
+ bool smallCaps = description.smallCaps();
+ m_font.setCapitalization(smallCaps ? QFont::SmallCaps : QFont::MixedCase);
+
+ QFontMetrics metrics = QFontMetrics(m_font);
+ m_spaceWidth = metrics.width(QLatin1Char(' '));
+
+ if (wordSpacing)
+ m_font.setWordSpacing(wordSpacing);
+ if (letterSpacing)
+ m_font.setLetterSpacing(QFont::AbsoluteSpacing, letterSpacing);
+}
+
+void Font::setWordSpacing(short s)
+{
+ m_font.setWordSpacing(s);
+ m_wordSpacing = s;
+}
+void Font::setLetterSpacing(short s)
+{
+ m_font.setLetterSpacing(QFont::AbsoluteSpacing, s);
+ m_letterSpacing = s;
+}
+
+
+static QString qstring(const TextRun& run)
+{
+ QString string((QChar *)run.characters(), run.length());
+ QChar *uc = string.data();
+ for (int i = 0; i < string.length(); ++i) {
+ if (Font::treatAsSpace(uc[i].unicode()))
+ uc[i] = 0x20;
+ else if (Font::treatAsZeroWidthSpace(uc[i].unicode()))
+ uc[i] = 0x200c;
+ }
+ return string;
+}
+
+
+static QTextLine setupLayout(QTextLayout* layout, const TextRun& style)
+{
+ int flags = style.rtl() ? Qt::TextForceRightToLeft : Qt::TextForceLeftToRight;
+ if (style.padding())
+ flags |= Qt::TextJustificationForced;
+ layout->setFlags(flags);
+ layout->beginLayout();
+ QTextLine line = layout->createLine();
+ line.setLineWidth(INT_MAX/256);
+ if (style.padding())
+ line.setLineWidth(line.naturalTextWidth() + style.padding());
+ layout->endLayout();
+ return line;
+}
+
+void Font::drawText(GraphicsContext* ctx, const TextRun& run, const FloatPoint& point, int from, int to) const
+{
+ if (to < 0)
+ to = run.length();
+ QPainter *p = ctx->platformContext();
+ Color color = ctx->fillColor();
+ p->setPen(QColor(color));
+
+ QString string = qstring(run);
+
+ // text shadow
+ IntSize shadowSize;
+ int shadowBlur;
+ Color shadowColor;
+ bool hasShadow = ctx->textDrawingMode() == cTextFill && ctx->getShadow(shadowSize, shadowBlur, shadowColor);
+
+ if (from > 0 || to < run.length()) {
+ QTextLayout layout(string, m_font);
+ QTextLine line = setupLayout(&layout, run);
+ float x1 = line.cursorToX(from);
+ float x2 = line.cursorToX(to);
+ if (x2 < x1)
+ qSwap(x1, x2);
+
+ QFontMetrics fm(m_font);
+ int ascent = fm.ascent();
+ QRectF clip(point.x() + x1, point.y() - ascent, x2 - x1, fm.height());
+
+ if (hasShadow) {
+ // TODO: when blur support is added, the clip will need to account
+ // for the blur radius
+ qreal dx1 = 0, dx2 = 0, dy1 = 0, dy2 = 0;
+ if (shadowSize.width() > 0)
+ dx2 = shadowSize.width();
+ else
+ dx1 = -shadowSize.width();
+ if (shadowSize.height() > 0)
+ dy2 = shadowSize.height();
+ else
+ dy1 = -shadowSize.height();
+ // expand the clip rect to include the text shadow as well
+ clip.adjust(dx1, dx2, dy1, dy2);
+ }
+ p->save();
+ p->setClipRect(clip.toRect());
+ QPointF pt(point.x(), point.y() - ascent);
+ if (hasShadow) {
+ p->save();
+ p->setPen(QColor(shadowColor));
+ p->translate(shadowSize.width(), shadowSize.height());
+ line.draw(p, pt);
+ p->restore();
+ }
+ line.draw(p, pt);
+ p->restore();
+ return;
+ }
+
+ p->setFont(m_font);
+
+ QPointF pt(point.x(), point.y());
+ int flags = run.rtl() ? Qt::TextForceRightToLeft : Qt::TextForceLeftToRight;
+ if (hasShadow) {
+ // TODO: text shadow blur support
+ p->save();
+ p->setPen(QColor(shadowColor));
+ p->translate(shadowSize.width(), shadowSize.height());
+ p->drawText(pt, string, flags, run.padding());
+ p->restore();
+ }
+ p->drawText(pt, string, flags, run.padding());
+}
+
+int Font::width(const TextRun& run) const
+{
+ if (!run.length())
+ return 0;
+ QString string = qstring(run);
+ QTextLayout layout(string, m_font);
+ QTextLine line = setupLayout(&layout, run);
+ int w = int(line.naturalTextWidth());
+ // WebKit expects us to ignore word spacing on the first character (as opposed to what Qt does)
+ if (treatAsSpace(run[0]))
+ w -= m_wordSpacing;
+
+ return w + run.padding();
+}
+
+float Font::floatWidth(const TextRun& run) const
+{
+ return width(run);
+}
+
+float Font::floatWidth(const TextRun& run, int /*extraCharsAvailable*/, int& charsConsumed, String& glyphName) const
+{
+ charsConsumed = run.length();
+ glyphName = "";
+ return width(run);
+}
+
+int Font::offsetForPosition(const TextRun& run, int position, bool /*includePartialGlyphs*/) const
+{
+ QString string = qstring(run);
+ QTextLayout layout(string, m_font);
+ QTextLine line = setupLayout(&layout, run);
+ return line.xToCursor(position);
+}
+
+FloatRect Font::selectionRectForText(const TextRun& run, const IntPoint& pt, int h, int from, int to) const
+{
+ QString string = qstring(run);
+ QTextLayout layout(string, m_font);
+ QTextLine line = setupLayout(&layout, run);
+
+ float x1 = line.cursorToX(from);
+ float x2 = line.cursorToX(to);
+ if (x2 < x1)
+ qSwap(x1, x2);
+
+ return FloatRect(pt.x() + x1, pt.y(), x2 - x1, h);
+}
+
+#else
+
+
+struct TextRunComponent {
+ TextRunComponent() : font(0) {}
+ TextRunComponent(const UChar *start, int length, bool rtl, const QFont *font, int offset, bool sc = false);
+ TextRunComponent(int spaces, bool rtl, const QFont *font, int offset);
+
+ inline bool isSpace() const { return spaces != 0; }
+
+ QString string;
+ const QFont *font;
+ int width;
+ int offset;
+ int spaces;
+};
+
+TextRunComponent::TextRunComponent(const UChar *start, int length, bool rtl, const QFont *f, int o, bool sc)
+ : string(reinterpret_cast<const QChar*>(start), length)
+ , font(f)
+ , offset(o)
+ , spaces(0)
+{
+ if (sc)
+ string = string.toUpper();
+ string.prepend(rtl ? QChar(0x202e) : QChar(0x202d));
+ width = QFontMetrics(*font).width(string);
+}
+
+TextRunComponent::TextRunComponent(int s, bool rtl, const QFont *f, int o)
+ : string(s, QLatin1Char(' '))
+ , font(f)
+ , offset(o)
+ , spaces(s)
+{
+ string.prepend(rtl ? QChar(0x202e) : QChar(0x202d));
+ width = spaces * QFontMetrics(*font).width(QLatin1Char(' '));
+}
+
+
+Font::Font()
+ : m_letterSpacing(0)
+ , m_wordSpacing(0)
+ , m_font()
+ , m_scFont()
+{
+ QFontMetrics metrics(m_font);
+ m_spaceWidth = metrics.width(QLatin1Char(' '));
+ qreal pointsize = m_font.pointSizeF();
+ if (pointsize > 0)
+ m_scFont.setPointSizeF(pointsize*0.7);
+ else
+ m_scFont.setPixelSize(qRound(m_font.pixelSize()*.7));
+}
+
+Font::Font(const FontDescription& description, short letterSpacing, short wordSpacing)
+ : m_fontDescription(description)
+ , m_letterSpacing(letterSpacing)
+ , m_wordSpacing(wordSpacing)
+{
+ const FontFamily* family = &description.family();
+ QString familyName;
+ while (family) {
+ familyName += family->family();
+ family = family->next();
+ if (family)
+ familyName += QLatin1Char(',');
+ }
+
+ m_font.setFamily(familyName);
+ m_font.setPixelSize(qRound(description.computedSize()));
+ m_font.setItalic(description.italic());
+ // FIXME: Map all FontWeight values to QFont weights.
+ if (description.weight() >= FontWeight600)
+ m_font.setWeight(QFont::Bold);
+ else
+ m_font.setWeight(QFont::Normal);
+
+ QFontMetrics metrics = QFontMetrics(m_font);
+ m_spaceWidth = metrics.width(QLatin1Char(' '));
+ m_scFont = m_font;
+ m_scFont.setPixelSize(qRound(description.computedSize()*.7));
+}
+
+void Font::setWordSpacing(short s)
+{
+ m_wordSpacing = s;
+}
+void Font::setLetterSpacing(short s)
+{
+ m_letterSpacing = s;
+}
+
+static int generateComponents(Vector<TextRunComponent, 1024>* components, const Font &font, const TextRun &run)
+{
+// qDebug() << "generateComponents" << QString((const QChar *)run.characters(), run.length());
+ int letterSpacing = font.letterSpacing();
+ int wordSpacing = font.wordSpacing();
+ bool smallCaps = font.fontDescription().smallCaps();
+ int padding = run.padding();
+ int numSpaces = 0;
+ if (padding) {
+ for (int i = 0; i < run.length(); i++)
+ if (Font::treatAsSpace(run[i]))
+ ++numSpaces;
+ }
+
+ int offset = 0;
+ const QFont *f = &font.font();
+ if (letterSpacing || smallCaps) {
+ // need to draw every letter on it's own
+ int start = 0;
+ if (Font::treatAsSpace(run[0])) {
+ int add = 0;
+ if (numSpaces) {
+ add = padding/numSpaces;
+ padding -= add;
+ --numSpaces;
+ }
+ components->append(TextRunComponent(1, run.rtl(), &font.font(), offset));
+ offset += add + letterSpacing + components->last().width;
+ start = 1;
+// qDebug() << "space at 0" << offset;
+ } else if (smallCaps) {
+ f = (QChar::category(run[0]) == QChar::Letter_Lowercase ? &font.scFont() : &font.font());
+ }
+ for (int i = 1; i < run.length(); ++i) {
+ uint ch = run[i];
+ if (QChar(ch).isHighSurrogate() && QChar(run[i-1]).isLowSurrogate())
+ ch = QChar::surrogateToUcs4(ch, run[i-1]);
+ if (QChar(ch).isLowSurrogate() || QChar::category(ch) == QChar::Mark_NonSpacing)
+ continue;
+ if (Font::treatAsSpace(run[i])) {
+ int add = 0;
+// qDebug() << " treatAsSpace:" << i << start;
+ if (i - start > 0) {
+ components->append(TextRunComponent(run.characters() + start, i - start,
+ run.rtl(),
+ f, offset, f == &font.scFont()));
+ offset += components->last().width + letterSpacing;
+// qDebug() << " appending(1) " << components->last().string << components->last().width;
+ }
+ if (numSpaces) {
+ add = padding/numSpaces;
+ padding -= add;
+ --numSpaces;
+ }
+ components->append(TextRunComponent(1, run.rtl(), &font.font(), offset));
+ offset += wordSpacing + add + components->last().width + letterSpacing;
+ start = i + 1;
+ continue;
+ } else if (!letterSpacing) {
+// qDebug() << i << char(run[i]) << (QChar::category(ch) == QChar::Letter_Lowercase) <<
+// QFontInfo(*f).pointSizeF();
+ if (QChar::category(ch) == QChar::Letter_Lowercase) {
+ if (f == &font.scFont())
+ continue;
+ } else {
+ if (f == &font.font())
+ continue;
+ }
+ }
+ if (i - start > 0) {
+ components->append(TextRunComponent(run.characters() + start, i - start,
+ run.rtl(),
+ f, offset, f == &font.scFont()));
+ offset += components->last().width + letterSpacing;
+// qDebug() << " appending(2) " << components->last().string << components->last().width;
+ }
+ if (smallCaps)
+ f = (QChar::category(ch) == QChar::Letter_Lowercase ? &font.scFont() : &font.font());
+ start = i;
+ }
+ if (run.length() - start > 0) {
+ components->append(TextRunComponent(run.characters() + start, run.length() - start,
+ run.rtl(),
+ f, offset, f == &font.scFont()));
+ offset += components->last().width;
+// qDebug() << " appending(3) " << components->last().string << components->last().width;
+ }
+ offset += letterSpacing;
+ } else {
+ int start = 0;
+ for (int i = 0; i < run.length(); ++i) {
+ if (Font::treatAsSpace(run[i])) {
+ if (i - start > 0) {
+ components->append(TextRunComponent(run.characters() + start, i - start,
+ run.rtl(),
+ f, offset));
+ offset += components->last().width;
+ }
+ int add = 0;
+ if (numSpaces) {
+ add = padding/numSpaces;
+ padding -= add;
+ --numSpaces;
+ }
+ components->append(TextRunComponent(1, run.rtl(), &font.font(), offset));
+ offset += add + components->last().width;
+ if (i)
+ offset += wordSpacing;
+ start = i + 1;
+ }
+ }
+ if (run.length() - start > 0) {
+ components->append(TextRunComponent(run.characters() + start, run.length() - start,
+ run.rtl(),
+ f, offset));
+ offset += components->last().width;
+ }
+ }
+ return offset;
+}
+
+void Font::drawText(GraphicsContext* ctx, const TextRun& run, const FloatPoint& point, int from, int to) const
+{
+ if (to < 0)
+ to = run.length();
+ QPainter *p = ctx->platformContext();
+ Color color = ctx->fillColor();
+ p->setPen(QColor(color));
+
+ Vector<TextRunComponent, 1024> components;
+ int w = generateComponents(&components, *this, run);
+
+ if (from > 0 || to < run.length()) {
+ FloatRect clip = selectionRectForText(run,
+ IntPoint(qRound(point.x()), qRound(point.y())),
+ QFontMetrics(m_font).height(), from, to);
+ QRectF rect(clip.x(), clip.y() - ascent(), clip.width(), clip.height());
+ p->save();
+ p->setClipRect(rect.toRect());
+ }
+
+ if (run.rtl()) {
+ for (int i = 0; i < components.size(); ++i) {
+ if (!components.at(i).isSpace()) {
+ p->setFont(*components.at(i).font);
+ QPointF pt(point.x() + w - components.at(i).offset - components.at(i).width, point.y());
+ p->drawText(pt, components.at(i).string);
+ }
+ }
+ } else {
+ for (int i = 0; i < components.size(); ++i) {
+ if (!components.at(i).isSpace()) {
+ p->setFont(*components.at(i).font);
+ QPointF pt(point.x() + components.at(i).offset, point.y());
+ p->drawText(pt, components.at(i).string);
+ }
+ }
+ }
+ if (from > 0 || to < run.length())
+ p->restore();
+}
+
+int Font::width(const TextRun& run) const
+{
+ Vector<TextRunComponent, 1024> components;
+ int w = generateComponents(&components, *this, run);
+
+// qDebug() << " width=" << w;
+ return w;
+}
+
+float Font::floatWidth(const TextRun& run) const
+{
+ return width(run);
+}
+
+float Font::floatWidth(const TextRun& run, int /*extraCharsAvailable*/, int& charsConsumed, String& glyphName) const
+{
+ charsConsumed = run.length();
+ glyphName = "";
+ return width(run);
+}
+
+int Font::offsetForPosition(const TextRun& run, int position, bool includePartialGlyphs) const
+{
+ Vector<TextRunComponent, 1024> components;
+ int w = generateComponents(&components, *this, run);
+
+ int offset = 0;
+ if (run.rtl()) {
+ for (int i = 0; i < components.size(); ++i) {
+ int xe = w - components.at(i).offset;
+ int xs = xe - components.at(i).width;
+ if (position >= xs) {
+ QTextLayout layout(components.at(i).string, *components.at(i).font);
+ layout.beginLayout();
+ QTextLine l = layout.createLine();
+ if (!l.isValid())
+ return offset;
+
+ l.setLineWidth(INT_MAX/256);
+ layout.endLayout();
+
+ if (position - xs >= l.width())
+ return offset;
+ int cursor = l.xToCursor(position - xs);
+ if (cursor > 1)
+ --cursor;
+ return offset + cursor;
+ } else {
+ offset += components.at(i).string.length() - 1;
+ }
+ }
+ } else {
+ for (int i = 0; i < components.size(); ++i) {
+ int xs = components.at(i).offset;
+ int xe = xs + components.at(i).width;
+ if (position <= xe) {
+ QTextLayout layout(components.at(i).string, *components.at(i).font);
+ layout.beginLayout();
+ QTextLine l = layout.createLine();
+ if (!l.isValid())
+ return offset;
+
+ l.setLineWidth(INT_MAX/256);
+ layout.endLayout();
+
+ if (position - xs >= l.width())
+ return offset + components.at(i).string.length() - 1;
+ int cursor = l.xToCursor(position - xs);
+ if (cursor > 1)
+ --cursor;
+ return offset + cursor;
+ } else {
+ offset += components.at(i).string.length() - 1;
+ }
+ }
+ }
+ return run.length();
+}
+
+static float cursorToX(const Vector<TextRunComponent, 1024>& components, int width, bool rtl, int cursor)
+{
+ int start = 0;
+ for (int i = 0; i < components.size(); ++i) {
+ if (start + components.at(i).string.length() - 1 < cursor) {
+ start += components.at(i).string.length() - 1;
+ continue;
+ }
+ int xs = components.at(i).offset;
+ if (rtl)
+ xs = width - xs - components.at(i).width;
+ QTextLayout layout(components.at(i).string, *components.at(i).font);
+ layout.beginLayout();
+ QTextLine l = layout.createLine();
+ if (!l.isValid())
+ return 0;
+
+ l.setLineWidth(INT_MAX/256);
+ layout.endLayout();
+
+ return xs + l.cursorToX(cursor - start + 1);
+ }
+ return width;
+}
+
+FloatRect Font::selectionRectForText(const TextRun& run, const IntPoint& pt,
+ int h, int from, int to) const
+{
+ Vector<TextRunComponent, 1024> components;
+ int w = generateComponents(&components, *this, run);
+
+ if (from == 0 && to == run.length())
+ return FloatRect(pt.x(), pt.y(), w, h);
+
+ float x1 = cursorToX(components, w, run.rtl(), from);
+ float x2 = cursorToX(components, w, run.rtl(), to);
+ if (x2 < x1)
+ qSwap(x1, x2);
+
+ return FloatRect(pt.x() + x1, pt.y(), x2 - x1, h);
+}
+#endif
+
+
+Font::~Font()
+{
+}
+
+Font::Font(const Font& other)
+ : m_fontDescription(other.m_fontDescription)
+ , m_letterSpacing(other.m_letterSpacing)
+ , m_wordSpacing(other.m_wordSpacing)
+ , m_font(other.m_font)
+ , m_scFont(other.m_scFont)
+ , m_spaceWidth(other.m_spaceWidth)
+{
+}
+
+Font& Font::operator=(const Font& other)
+{
+ m_fontDescription = other.m_fontDescription;
+ m_letterSpacing = other.m_letterSpacing;
+ m_wordSpacing = other.m_wordSpacing;
+ m_font = other.m_font;
+ m_scFont = other.m_scFont;
+ m_spaceWidth = other.m_spaceWidth;
+ return *this;
+}
+
+bool Font::operator==(const Font& other) const
+{
+ return m_fontDescription == other.m_fontDescription
+ && m_letterSpacing == other.m_letterSpacing
+ && m_wordSpacing == other.m_wordSpacing
+ && m_font == other.m_font
+ && m_scFont == other.m_scFont
+ && m_spaceWidth == other.m_spaceWidth;
+}
+
+void Font::update(PassRefPtr<FontSelector>) const
+{
+ // don't think we need this
+}
+
+
+bool Font::isFixedPitch() const
+{
+ return QFontInfo(m_font).fixedPitch();
+}
+
+// Metrics that we query the FontFallbackList for.
+int Font::ascent() const
+{
+ return QFontMetrics(m_font).ascent();
+}
+
+int Font::descent() const
+{
+ return QFontMetrics(m_font).descent();
+}
+
+int Font::lineSpacing() const
+{
+ return QFontMetrics(m_font).lineSpacing();
+}
+
+int Font::lineGap() const
+{
+ return QFontMetrics(m_font).leading();
+}
+
+float Font::xHeight() const
+{
+ return QFontMetrics(m_font).xHeight();
+}
+
+unsigned Font::unitsPerEm() const
+{
+ return 1; // FIXME!
+}
+
+int Font::spaceWidth() const
+{
+ return m_spaceWidth;
+}
+
+}