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@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
fromRawDataWithoutRef(const String & string,int start=0,int len=-1)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
setupLayout(QTextLayout * layout,const TextRun & style)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
drawTextCommon(GraphicsContext * ctx,const TextRun & run,const FloatPoint & point,int from,int to,const QFont & font,bool isComplexText)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
drawSimpleText(GraphicsContext * ctx,const TextRun & run,const FloatPoint & point,int from,int to) const238 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
drawComplexText(GraphicsContext * ctx,const TextRun & run,const FloatPoint & point,int from,int to) const243 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
emphasisMarkAscent(const AtomicString &) const248 int Font::emphasisMarkAscent(const AtomicString&) const
249 {
250 notImplemented();
251 return 0;
252 }
253
emphasisMarkDescent(const AtomicString &) const254 int Font::emphasisMarkDescent(const AtomicString&) const
255 {
256 notImplemented();
257 return 0;
258 }
259
emphasisMarkHeight(const AtomicString &) const260 int Font::emphasisMarkHeight(const AtomicString&) const
261 {
262 notImplemented();
263 return 0;
264 }
265
drawEmphasisMarksForSimpleText(GraphicsContext *,const TextRun &,const AtomicString &,const FloatPoint &,int,int) const266 void Font::drawEmphasisMarksForSimpleText(GraphicsContext* /* context */, const TextRun& /* run */, const AtomicString& /* mark */, const FloatPoint& /* point */, int /* from */, int /* to */) const
267 {
268 notImplemented();
269 }
270
drawEmphasisMarksForComplexText(GraphicsContext *,const TextRun &,const AtomicString &,const FloatPoint &,int,int) const271 void Font::drawEmphasisMarksForComplexText(GraphicsContext* /* context */, const TextRun& /* run */, const AtomicString& /* mark */, const FloatPoint& /* point */, int /* from */, int /* to */) const
272 {
273 notImplemented();
274 }
275
floatWidthForSimpleText(const TextRun & run,GlyphBuffer * glyphBuffer,HashSet<const SimpleFontData * > * fallbackFonts,GlyphOverflow * glyphOverflow) const276 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
floatWidthForComplexText(const TextRun & run,HashSet<const SimpleFontData * > *,GlyphOverflow *) const296 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
offsetForPositionForSimpleText(const TextRun & run,float position,bool includePartialGlyphs) const318 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
offsetForPositionForComplexText(const TextRun & run,float position,bool) const341 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
selectionRectForSimpleText(const TextRun & run,const FloatPoint & pt,int h,int from,int to) const351 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
selectionRectForComplexText(const TextRun & run,const FloatPoint & pt,int h,int from,int to) const363 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
canReturnFallbackFontsForComplexText()379 bool Font::canReturnFallbackFontsForComplexText()
380 {
381 return false;
382 }
383
canExpandAroundIdeographsInComplexText()384 bool Font::canExpandAroundIdeographsInComplexText()
385 {
386 return false;
387 }
388
primaryFontHasGlyphForCharacter(UChar32) const389 bool Font::primaryFontHasGlyphForCharacter(UChar32) const
390 {
391 notImplemented();
392 return true;
393 }
394
font() const395 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