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