Home | History | Annotate | Download | only in qt
      1 /*
      2     Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
      3     Copyright (C) 2008, 2010 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 "ContextShadow.h"
     27 #include "FontDescription.h"
     28 #include "FontFallbackList.h"
     29 #include "FontSelector.h"
     30 #include "Gradient.h"
     31 #include "GraphicsContext.h"
     32 #include "NotImplemented.h"
     33 #include "Pattern.h"
     34 #include "TextRun.h"
     35 
     36 #include <QBrush>
     37 #include <QFontInfo>
     38 #include <QFontMetrics>
     39 #include <QPainter>
     40 #include <QPainterPath>
     41 #include <QPen>
     42 #include <QTextLayout>
     43 #include <qalgorithms.h>
     44 #include <qdebug.h>
     45 
     46 #include <limits.h>
     47 
     48 namespace WebCore {
     49 
     50 static const QString fromRawDataWithoutRef(const String& string, int start = 0, int len = -1)
     51 {
     52     if (len < 0)
     53         len = string.length() - start;
     54     Q_ASSERT(start + len <= string.length());
     55 
     56     // We don't detach. This assumes the WebCore string data will stay valid for the
     57     // lifetime of the QString we pass back, since we don't ref the WebCore string.
     58     return QString::fromRawData(reinterpret_cast<const QChar*>(string.characters() + start), len);
     59 }
     60 
     61 static QTextLine setupLayout(QTextLayout* layout, const TextRun& style)
     62 {
     63     int flags = style.rtl() ? Qt::TextForceRightToLeft : Qt::TextForceLeftToRight;
     64     if (style.expansion())
     65         flags |= Qt::TextJustificationForced;
     66     layout->setFlags(flags);
     67     layout->beginLayout();
     68     QTextLine line = layout->createLine();
     69     line.setLineWidth(INT_MAX/256);
     70     if (style.expansion())
     71         line.setLineWidth(line.naturalTextWidth() + style.expansion());
     72     layout->endLayout();
     73     return line;
     74 }
     75 
     76 static void drawTextCommon(GraphicsContext* ctx, const TextRun& run, const FloatPoint& point, int from, int to, const QFont& font, bool isComplexText)
     77 {
     78     if (to < 0)
     79         to = run.length();
     80 
     81     QPainter *p = ctx->platformContext();
     82 
     83     QPen textFillPen;
     84     if (ctx->textDrawingMode() & TextModeFill) {
     85         if (ctx->fillGradient()) {
     86             QBrush brush(*ctx->fillGradient()->platformGradient());
     87             brush.setTransform(ctx->fillGradient()->gradientSpaceTransform());
     88             textFillPen = QPen(brush, 0);
     89         } else if (ctx->fillPattern()) {
     90             AffineTransform affine;
     91             textFillPen = QPen(QBrush(ctx->fillPattern()->createPlatformPattern(affine)), 0);
     92         } else
     93             textFillPen = QPen(QColor(ctx->fillColor()));
     94     }
     95 
     96     QPen textStrokePen;
     97     if (ctx->textDrawingMode() & TextModeStroke) {
     98         if (ctx->strokeGradient()) {
     99             QBrush brush(*ctx->strokeGradient()->platformGradient());
    100             brush.setTransform(ctx->strokeGradient()->gradientSpaceTransform());
    101             textStrokePen = QPen(brush, ctx->strokeThickness());
    102         } else if (ctx->strokePattern()) {
    103             AffineTransform affine;
    104             QBrush brush(ctx->strokePattern()->createPlatformPattern(affine));
    105             textStrokePen = QPen(brush, ctx->strokeThickness());
    106         } else
    107             textStrokePen = QPen(QColor(ctx->strokeColor()), ctx->strokeThickness());
    108     }
    109 
    110     String sanitized = Font::normalizeSpaces(run.characters(), run.length());
    111     QString string = fromRawDataWithoutRef(sanitized);
    112     QPointF pt(point.x(), point.y());
    113 
    114     if (from > 0 || to < run.length()) {
    115         if (isComplexText) {
    116             QTextLayout layout(string, font);
    117             QTextLine line = setupLayout(&layout, run);
    118             float x1 = line.cursorToX(from);
    119             float x2 = line.cursorToX(to);
    120             if (x2 < x1)
    121                 qSwap(x1, x2);
    122 
    123             QFontMetrics fm(font);
    124             int ascent = fm.ascent();
    125             QRectF boundingRect(point.x() + x1, point.y() - ascent, x2 - x1, fm.height());
    126             QRectF clip = boundingRect;
    127 
    128             ContextShadow* ctxShadow = ctx->contextShadow();
    129 
    130             if (ctxShadow->m_type != ContextShadow::NoShadow) {
    131                 qreal dx1 = 0, dx2 = 0, dy1 = 0, dy2 = 0;
    132                 if (ctxShadow->offset().x() > 0)
    133                     dx2 = ctxShadow->offset().x();
    134                 else
    135                     dx1 = -ctxShadow->offset().x();
    136                 if (ctxShadow->offset().y() > 0)
    137                     dy2 = ctxShadow->offset().y();
    138                 else
    139                     dy1 = -ctxShadow->offset().y();
    140                 // expand the clip rect to include the text shadow as well
    141                 clip.adjust(dx1, dx2, dy1, dy2);
    142                 clip.adjust(-ctxShadow->m_blurDistance, -ctxShadow->m_blurDistance, ctxShadow->m_blurDistance, ctxShadow->m_blurDistance);
    143             }
    144             p->save();
    145             p->setClipRect(clip.toRect(), Qt::IntersectClip);
    146             pt.setY(pt.y() - ascent);
    147 
    148             if (ctxShadow->m_type != ContextShadow::NoShadow) {
    149                 ContextShadow* ctxShadow = ctx->contextShadow();
    150                 if (!ctxShadow->mustUseContextShadow(ctx)) {
    151                     p->save();
    152                     p->setPen(ctxShadow->m_color);
    153                     p->translate(ctxShadow->offset());
    154                     line.draw(p, pt);
    155                     p->restore();
    156                 } else {
    157                     QPainter* shadowPainter = ctxShadow->beginShadowLayer(ctx, boundingRect);
    158                     if (shadowPainter) {
    159                         // Since it will be blurred anyway, we don't care about render hints.
    160                         shadowPainter->setFont(p->font());
    161                         shadowPainter->setPen(ctxShadow->m_color);
    162                         line.draw(shadowPainter, pt);
    163                         ctxShadow->endShadowLayer(ctx);
    164                     }
    165                 }
    166             }
    167             p->setPen(textFillPen);
    168             line.draw(p, pt);
    169             p->restore();
    170             return;
    171         }
    172         int skipWidth = QFontMetrics(font).width(string, from, Qt::TextBypassShaping);
    173         pt.setX(pt.x() + skipWidth);
    174         string = fromRawDataWithoutRef(sanitized, from, to - from);
    175     }
    176 
    177     p->setFont(font);
    178 
    179     int flags = run.rtl() ? Qt::TextForceRightToLeft : Qt::TextForceLeftToRight;
    180     if (!isComplexText && !(ctx->textDrawingMode() & TextModeStroke))
    181         flags |= Qt::TextBypassShaping;
    182 
    183     QPainterPath textStrokePath;
    184     if (ctx->textDrawingMode() & TextModeStroke)
    185         textStrokePath.addText(pt, font, string);
    186 
    187     ContextShadow* ctxShadow = ctx->contextShadow();
    188     if (ctxShadow->m_type != ContextShadow::NoShadow) {
    189         if (ctx->textDrawingMode() & TextModeFill) {
    190             if (ctxShadow->m_type != ContextShadow::BlurShadow) {
    191                 p->save();
    192                 p->setPen(ctxShadow->m_color);
    193                 p->translate(ctxShadow->offset());
    194                 p->drawText(pt, string, flags, run.expansion());
    195                 p->restore();
    196             } else {
    197                 QFontMetrics fm(font);
    198                 QRectF boundingRect(pt.x(), point.y() - fm.ascent(), fm.width(string, -1, flags), fm.height());
    199                 QPainter* shadowPainter = ctxShadow->beginShadowLayer(ctx, boundingRect);
    200                 if (shadowPainter) {
    201                     // Since it will be blurred anyway, we don't care about render hints.
    202                     shadowPainter->setFont(p->font());
    203                     shadowPainter->setPen(ctxShadow->m_color);
    204                     shadowPainter->drawText(pt, string, flags, run.expansion());
    205                     ctxShadow->endShadowLayer(ctx);
    206                 }
    207             }
    208         } else if (ctx->textDrawingMode() & TextModeStroke) {
    209             if (ctxShadow->m_type != ContextShadow::BlurShadow) {
    210                 p->translate(ctxShadow->offset());
    211                 p->strokePath(textStrokePath, QPen(ctxShadow->m_color));
    212                 p->translate(-ctxShadow->offset());
    213             } else {
    214                 QFontMetrics fm(font);
    215                 QRectF boundingRect(pt.x(), point.y() - fm.ascent(), fm.width(string, -1, flags), fm.height());
    216                 QPainter* shadowPainter = ctxShadow->beginShadowLayer(ctx, boundingRect);
    217                 if (shadowPainter) {
    218                     // Since it will be blurred anyway, we don't care about render hints.
    219                     shadowPainter->setFont(p->font());
    220                     shadowPainter->strokePath(textStrokePath, QPen(ctxShadow->m_color));
    221                     ctxShadow->endShadowLayer(ctx);
    222                 }
    223             }
    224         }
    225     }
    226 
    227     if (ctx->textDrawingMode() & TextModeStroke)
    228         p->strokePath(textStrokePath, textStrokePen);
    229 
    230     if (ctx->textDrawingMode() & TextModeFill) {
    231         QPen previousPen = p->pen();
    232         p->setPen(textFillPen);
    233         p->drawText(pt, string, flags, run.expansion());
    234         p->setPen(previousPen);
    235     }
    236 }
    237 
    238 void Font::drawSimpleText(GraphicsContext* ctx, const TextRun& run, const FloatPoint& point, int from, int to) const
    239 {
    240     drawTextCommon(ctx, run, point, from, to, font(), /* isComplexText = */false);
    241 }
    242 
    243 void Font::drawComplexText(GraphicsContext* ctx, const TextRun& run, const FloatPoint& point, int from, int to) const
    244 {
    245     drawTextCommon(ctx, run, point, from, to, font(), /* isComplexText = */true);
    246 }
    247 
    248 int Font::emphasisMarkAscent(const AtomicString&) const
    249 {
    250     notImplemented();
    251     return 0;
    252 }
    253 
    254 int Font::emphasisMarkDescent(const AtomicString&) const
    255 {
    256     notImplemented();
    257     return 0;
    258 }
    259 
    260 int Font::emphasisMarkHeight(const AtomicString&) const
    261 {
    262     notImplemented();
    263     return 0;
    264 }
    265 
    266 void Font::drawEmphasisMarksForSimpleText(GraphicsContext* /* context */, const TextRun& /* run */, const AtomicString& /* mark */, const FloatPoint& /* point */, int /* from */, int /* to */) const
    267 {
    268     notImplemented();
    269 }
    270 
    271 void Font::drawEmphasisMarksForComplexText(GraphicsContext* /* context */, const TextRun& /* run */, const AtomicString& /* mark */, const FloatPoint& /* point */, int /* from */, int /* to */) const
    272 {
    273     notImplemented();
    274 }
    275 
    276 float Font::floatWidthForSimpleText(const TextRun& run, GlyphBuffer* glyphBuffer, HashSet<const SimpleFontData*>* fallbackFonts, GlyphOverflow* glyphOverflow) const
    277 {
    278     if (!primaryFont()->platformData().size())
    279         return 0;
    280 
    281     if (!run.length())
    282         return 0;
    283 
    284     String sanitized = Font::normalizeSpaces(run.characters(), run.length());
    285     QString string = fromRawDataWithoutRef(sanitized);
    286 
    287     int w = QFontMetrics(font()).width(string, -1, Qt::TextBypassShaping);
    288 
    289     // WebKit expects us to ignore word spacing on the first character (as opposed to what Qt does)
    290     if (treatAsSpace(run[0]))
    291         w -= m_wordSpacing;
    292 
    293     return w + run.expansion();
    294 }
    295 
    296 float Font::floatWidthForComplexText(const TextRun& run, HashSet<const SimpleFontData*>*, GlyphOverflow*) const
    297 {
    298     if (!primaryFont()->platformData().size())
    299         return 0;
    300 
    301     if (!run.length())
    302         return 0;
    303 
    304     if (run.length() == 1 && treatAsSpace(run[0]))
    305         return QFontMetrics(font()).width(space) + run.expansion();
    306 
    307     String sanitized = Font::normalizeSpaces(run.characters(), run.length());
    308     QString string = fromRawDataWithoutRef(sanitized);
    309 
    310     int w = QFontMetrics(font()).width(string);
    311     // WebKit expects us to ignore word spacing on the first character (as opposed to what Qt does)
    312     if (treatAsSpace(run[0]))
    313         w -= m_wordSpacing;
    314 
    315     return w + run.expansion();
    316 }
    317 
    318 int Font::offsetForPositionForSimpleText(const TextRun& run, float position, bool includePartialGlyphs) const
    319 {
    320     String sanitized = Font::normalizeSpaces(run.characters(), run.length());
    321     QString string = fromRawDataWithoutRef(sanitized);
    322 
    323     QFontMetrics fm(font());
    324     float delta = position;
    325     int curPos = 0;
    326     do {
    327         float charWidth = fm.width(string[curPos]);
    328         delta -= charWidth;
    329         if (includePartialGlyphs) {
    330             if (delta + charWidth / 2 <= 0)
    331                 break;
    332         } else {
    333             if (delta + charWidth <= 0)
    334                 break;
    335         }
    336     } while (++curPos < string.size());
    337 
    338     return curPos;
    339 }
    340 
    341 int Font::offsetForPositionForComplexText(const TextRun& run, float position, bool) const
    342 {
    343     String sanitized = Font::normalizeSpaces(run.characters(), run.length());
    344     QString string = fromRawDataWithoutRef(sanitized);
    345 
    346     QTextLayout layout(string, font());
    347     QTextLine line = setupLayout(&layout, run);
    348     return line.xToCursor(position);
    349 }
    350 
    351 FloatRect Font::selectionRectForSimpleText(const TextRun& run, const FloatPoint& pt, int h, int from, int to) const
    352 {
    353     String sanitized = Font::normalizeSpaces(run.characters(), run.length());
    354     QString wholeText = fromRawDataWithoutRef(sanitized);
    355     QString selectedText = fromRawDataWithoutRef(sanitized, from, qMin(to - from, wholeText.length() - from));
    356 
    357     int startX = QFontMetrics(font()).width(wholeText, from, Qt::TextBypassShaping);
    358     int width = QFontMetrics(font()).width(selectedText, -1, Qt::TextBypassShaping);
    359 
    360     return FloatRect(pt.x() + startX, pt.y(), width, h);
    361 }
    362 
    363 FloatRect Font::selectionRectForComplexText(const TextRun& run, const FloatPoint& pt, int h, int from, int to) const
    364 {
    365     String sanitized = Font::normalizeSpaces(run.characters(), run.length());
    366     QString string = fromRawDataWithoutRef(sanitized);
    367 
    368     QTextLayout layout(string, font());
    369     QTextLine line = setupLayout(&layout, run);
    370 
    371     float x1 = line.cursorToX(from);
    372     float x2 = line.cursorToX(to);
    373     if (x2 < x1)
    374         qSwap(x1, x2);
    375 
    376     return FloatRect(pt.x() + x1, pt.y(), x2 - x1, h);
    377 }
    378 
    379 bool Font::canReturnFallbackFontsForComplexText()
    380 {
    381     return false;
    382 }
    383 
    384 bool Font::canExpandAroundIdeographsInComplexText()
    385 {
    386     return false;
    387 }
    388 
    389 bool Font::primaryFontHasGlyphForCharacter(UChar32) const
    390 {
    391     notImplemented();
    392     return true;
    393 }
    394 
    395 QFont Font::font() const
    396 {
    397     QFont f = primaryFont()->getQtFont();
    398     if (m_letterSpacing != 0)
    399         f.setLetterSpacing(QFont::AbsoluteSpacing, m_letterSpacing);
    400     if (m_wordSpacing != 0)
    401         f.setWordSpacing(m_wordSpacing);
    402     return f;
    403 }
    404 
    405 }
    406 
    407