Home | History | Annotate | Download | only in qt
      1 /*
      2     Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
      3     Copyright (C) 2008 Holger Hans Peter Freyther
      4     Copyright (C) 2009 Dirk Schulze <krit (at) webkit.org>
      5 
      6     This library is free software; you can redistribute it and/or
      7     modify it under the terms of the GNU Library General Public
      8     License as published by the Free Software Foundation; either
      9     version 2 of the License, or (at your option) any later version.
     10 
     11     This library is distributed in the hope that it will be useful,
     12     but WITHOUT ANY WARRANTY; without even the implied warranty of
     13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     14     Library General Public License for more details.
     15 
     16     You should have received a copy of the GNU Library General Public License
     17     along with this library; see the file COPYING.LIB.  If not, write to
     18     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     19     Boston, MA 02110-1301, USA.
     20 */
     21 
     22 #include "config.h"
     23 #include "Font.h"
     24 
     25 #include "AffineTransform.h"
     26 #include "FontDescription.h"
     27 #include "FontFallbackList.h"
     28 #include "FontSelector.h"
     29 #include "Gradient.h"
     30 #include "GraphicsContext.h"
     31 #include "Pattern.h"
     32 
     33 #include <QBrush>
     34 #include <QFontInfo>
     35 #include <QFontMetrics>
     36 #include <QPainter>
     37 #include <QPainterPath>
     38 #include <QPen>
     39 #include <QTextLayout>
     40 #include <qalgorithms.h>
     41 #include <qdebug.h>
     42 
     43 #include <limits.h>
     44 
     45 namespace WebCore {
     46 
     47 static const QString fromRawDataWithoutRef(const String& string)
     48 {
     49     // We don't detach. This assumes the WebCore string data will stay valid for the
     50     // lifetime of the QString we pass back, since we don't ref the WebCore string.
     51     return QString::fromRawData(reinterpret_cast<const QChar*>(string.characters()), string.length());
     52 }
     53 
     54 static QTextLine setupLayout(QTextLayout* layout, const TextRun& style)
     55 {
     56     int flags = style.rtl() ? Qt::TextForceRightToLeft : Qt::TextForceLeftToRight;
     57     if (style.padding())
     58         flags |= Qt::TextJustificationForced;
     59     layout->setFlags(flags);
     60     layout->beginLayout();
     61     QTextLine line = layout->createLine();
     62     line.setLineWidth(INT_MAX/256);
     63     if (style.padding())
     64         line.setLineWidth(line.naturalTextWidth() + style.padding());
     65     layout->endLayout();
     66     return line;
     67 }
     68 
     69 void Font::drawComplexText(GraphicsContext* ctx, const TextRun& run, const FloatPoint& point, int from, int to) const
     70 {
     71     if (to < 0)
     72         to = run.length();
     73 
     74     QPainter *p = ctx->platformContext();
     75 
     76     if (ctx->textDrawingMode() & cTextFill) {
     77         if (ctx->fillGradient()) {
     78             QBrush brush(*ctx->fillGradient()->platformGradient());
     79             brush.setTransform(ctx->fillGradient()->gradientSpaceTransform());
     80             p->setPen(QPen(brush, 0));
     81         } else if (ctx->fillPattern()) {
     82             AffineTransform affine;
     83             p->setPen(QPen(QBrush(ctx->fillPattern()->createPlatformPattern(affine)), 0));
     84         } else
     85             p->setPen(QColor(ctx->fillColor()));
     86     }
     87 
     88     if (ctx->textDrawingMode() & cTextStroke) {
     89         if (ctx->strokeGradient()) {
     90             QBrush brush(*ctx->strokeGradient()->platformGradient());
     91             brush.setTransform(ctx->strokeGradient()->gradientSpaceTransform());
     92             p->setPen(QPen(brush, ctx->strokeThickness()));
     93         } else if (ctx->strokePattern()) {
     94             AffineTransform affine;
     95             p->setPen(QPen(QBrush(ctx->strokePattern()->createPlatformPattern(affine)), ctx->strokeThickness()));
     96         } else
     97             p->setPen(QPen(QColor(ctx->strokeColor()), ctx->strokeThickness()));
     98     }
     99 
    100     String sanitized = Font::normalizeSpaces(String(run.characters(), run.length()));
    101     QString string = fromRawDataWithoutRef(sanitized);
    102 
    103     // text shadow
    104     IntSize shadowSize;
    105     int shadowBlur;
    106     Color shadowColor;
    107     bool hasShadow = ctx->textDrawingMode() == cTextFill && ctx->getShadow(shadowSize, shadowBlur, shadowColor);
    108 
    109     if (from > 0 || to < run.length()) {
    110         QTextLayout layout(string, font());
    111         QTextLine line = setupLayout(&layout, run);
    112         float x1 = line.cursorToX(from);
    113         float x2 = line.cursorToX(to);
    114         if (x2 < x1)
    115             qSwap(x1, x2);
    116 
    117         QFontMetrics fm(font());
    118         int ascent = fm.ascent();
    119         QRectF clip(point.x() + x1, point.y() - ascent, x2 - x1, fm.height());
    120 
    121         if (hasShadow) {
    122             // TODO: when blur support is added, the clip will need to account
    123             // for the blur radius
    124             qreal dx1 = 0, dx2 = 0, dy1 = 0, dy2 = 0;
    125             if (shadowSize.width() > 0)
    126                 dx2 = shadowSize.width();
    127             else
    128                 dx1 = -shadowSize.width();
    129             if (shadowSize.height() > 0)
    130                 dy2 = shadowSize.height();
    131             else
    132                 dy1 = -shadowSize.height();
    133             // expand the clip rect to include the text shadow as well
    134             clip.adjust(dx1, dx2, dy1, dy2);
    135         }
    136         p->save();
    137         p->setClipRect(clip.toRect());
    138         QPointF pt(point.x(), point.y() - ascent);
    139         if (hasShadow) {
    140             p->save();
    141             p->setPen(QColor(shadowColor));
    142             p->translate(shadowSize.width(), shadowSize.height());
    143             line.draw(p, pt);
    144             p->restore();
    145         }
    146         line.draw(p, pt);
    147         p->restore();
    148         return;
    149     }
    150 
    151     p->setFont(font());
    152 
    153     QPointF pt(point.x(), point.y());
    154     int flags = run.rtl() ? Qt::TextForceRightToLeft : Qt::TextForceLeftToRight;
    155     if (hasShadow) {
    156         // TODO: text shadow blur support
    157         p->save();
    158         p->setPen(QColor(shadowColor));
    159         p->translate(shadowSize.width(), shadowSize.height());
    160         p->drawText(pt, string, flags, run.padding());
    161         p->restore();
    162     }
    163     if (ctx->textDrawingMode() & cTextStroke) {
    164         QPainterPath path;
    165         path.addText(pt, font(), string);
    166         p->strokePath(path, p->pen());
    167     }
    168     if (ctx->textDrawingMode() & cTextFill)
    169         p->drawText(pt, string, flags, run.padding());
    170 }
    171 
    172 float Font::floatWidthForComplexText(const TextRun& run, HashSet<const SimpleFontData*>*) const
    173 {
    174     if (!run.length())
    175         return 0;
    176 
    177     String sanitized = Font::normalizeSpaces(String(run.characters(), run.length()));
    178     QString string = fromRawDataWithoutRef(sanitized);
    179 
    180     QTextLayout layout(string, font());
    181     QTextLine line = setupLayout(&layout, run);
    182     int w = int(line.naturalTextWidth());
    183     // WebKit expects us to ignore word spacing on the first character (as opposed to what Qt does)
    184     if (treatAsSpace(run[0]))
    185         w -= m_wordSpacing;
    186 
    187     return w + run.padding();
    188 }
    189 
    190 int Font::offsetForPositionForComplexText(const TextRun& run, int position, bool) const
    191 {
    192     String sanitized = Font::normalizeSpaces(String(run.characters(), run.length()));
    193     QString string = fromRawDataWithoutRef(sanitized);
    194 
    195     QTextLayout layout(string, font());
    196     QTextLine line = setupLayout(&layout, run);
    197     return line.xToCursor(position);
    198 }
    199 
    200 FloatRect Font::selectionRectForComplexText(const TextRun& run, const IntPoint& pt, int h, int from, int to) const
    201 {
    202     String sanitized = Font::normalizeSpaces(String(run.characters(), run.length()));
    203     QString string = fromRawDataWithoutRef(sanitized);
    204 
    205     QTextLayout layout(string, font());
    206     QTextLine line = setupLayout(&layout, run);
    207 
    208     float x1 = line.cursorToX(from);
    209     float x2 = line.cursorToX(to);
    210     if (x2 < x1)
    211         qSwap(x1, x2);
    212 
    213     return FloatRect(pt.x() + x1, pt.y(), x2 - x1, h);
    214 }
    215 
    216 QFont Font::font() const
    217 {
    218     QFont f = primaryFont()->getQtFont();
    219     f.setLetterSpacing(QFont::AbsoluteSpacing, m_letterSpacing);
    220     f.setWordSpacing(m_wordSpacing);
    221     return f;
    222 }
    223 
    224 }
    225 
    226