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 "FontPlatformData.h"
31 #if HAVE(QRAWFONT)
32 #include "GlyphBuffer.h"
33 #endif
34 #include "Gradient.h"
35 #include "GraphicsContext.h"
36 #include "NotImplemented.h"
37 #include "Pattern.h"
38 #include "TextRun.h"
39
40 #include <QBrush>
41 #include <QFontInfo>
42 #include <QFontMetrics>
43 #include <QPainter>
44 #include <QPainterPath>
45 #include <QPen>
46 #include <QTextLayout>
47 #include <qalgorithms.h>
48 #include <qdebug.h>
49
50 #include <limits.h>
51
52 namespace WebCore {
53
fromRawDataWithoutRef(const String & string,int start=0,int len=-1)54 static const QString fromRawDataWithoutRef(const String& string, int start = 0, int len = -1)
55 {
56 if (len < 0)
57 len = string.length() - start;
58 Q_ASSERT(start + len <= string.length());
59
60 // We don't detach. This assumes the WebCore string data will stay valid for the
61 // lifetime of the QString we pass back, since we don't ref the WebCore string.
62 return QString::fromRawData(reinterpret_cast<const QChar*>(string.characters() + start), len);
63 }
64
setupLayout(QTextLayout * layout,const TextRun & style)65 static QTextLine setupLayout(QTextLayout* layout, const TextRun& style)
66 {
67 int flags = style.rtl() ? Qt::TextForceRightToLeft : Qt::TextForceLeftToRight;
68 if (style.expansion())
69 flags |= Qt::TextJustificationForced;
70 layout->setFlags(flags);
71 layout->beginLayout();
72 QTextLine line = layout->createLine();
73 line.setLineWidth(INT_MAX/256);
74 if (style.expansion())
75 line.setLineWidth(line.naturalTextWidth() + style.expansion());
76 layout->endLayout();
77 return line;
78 }
79
fillPenForContext(GraphicsContext * ctx)80 static QPen fillPenForContext(GraphicsContext* ctx)
81 {
82 if (ctx->fillGradient()) {
83 QBrush brush(*ctx->fillGradient()->platformGradient());
84 brush.setTransform(ctx->fillGradient()->gradientSpaceTransform());
85 return QPen(brush, 0);
86 }
87
88 if (ctx->fillPattern()) {
89 AffineTransform affine;
90 return QPen(QBrush(ctx->fillPattern()->createPlatformPattern(affine)), 0);
91 }
92
93 return QPen(QColor(ctx->fillColor()));
94 }
95
strokePenForContext(GraphicsContext * ctx)96 static QPen strokePenForContext(GraphicsContext* ctx)
97 {
98 if (ctx->strokeGradient()) {
99 QBrush brush(*ctx->strokeGradient()->platformGradient());
100 brush.setTransform(ctx->strokeGradient()->gradientSpaceTransform());
101 return QPen(brush, ctx->strokeThickness());
102 }
103
104 if (ctx->strokePattern()) {
105 AffineTransform affine;
106 QBrush brush(ctx->strokePattern()->createPlatformPattern(affine));
107 return QPen(brush, ctx->strokeThickness());
108 }
109
110 return QPen(QColor(ctx->strokeColor()), ctx->strokeThickness());
111 }
112
drawTextCommon(GraphicsContext * ctx,const TextRun & run,const FloatPoint & point,int from,int to,const QFont & font,bool isComplexText)113 static void drawTextCommon(GraphicsContext* ctx, const TextRun& run, const FloatPoint& point, int from, int to, const QFont& font, bool isComplexText)
114 {
115 if (to < 0)
116 to = run.length();
117
118 QPainter *p = ctx->platformContext();
119
120 QPen textFillPen;
121 if (ctx->textDrawingMode() & TextModeFill)
122 textFillPen = fillPenForContext(ctx);
123
124 QPen textStrokePen;
125 if (ctx->textDrawingMode() & TextModeStroke)
126 textStrokePen = strokePenForContext(ctx);
127
128 String sanitized = Font::normalizeSpaces(run.characters(), run.length());
129 QString string = fromRawDataWithoutRef(sanitized);
130 QPointF pt(point.x(), point.y());
131
132 if (from > 0 || to < run.length()) {
133 if (isComplexText) {
134 QTextLayout layout(string, font);
135 QTextLine line = setupLayout(&layout, run);
136 float x1 = line.cursorToX(from);
137 float x2 = line.cursorToX(to);
138 if (x2 < x1)
139 qSwap(x1, x2);
140
141 QFontMetrics fm(font);
142 int ascent = fm.ascent();
143 QRectF boundingRect(point.x() + x1, point.y() - ascent, x2 - x1, fm.height());
144 QRectF clip = boundingRect;
145
146 ContextShadow* ctxShadow = ctx->contextShadow();
147
148 if (ctxShadow->m_type != ContextShadow::NoShadow) {
149 qreal dx1 = 0, dx2 = 0, dy1 = 0, dy2 = 0;
150 if (ctxShadow->offset().x() > 0)
151 dx2 = ctxShadow->offset().x();
152 else
153 dx1 = -ctxShadow->offset().x();
154 if (ctxShadow->offset().y() > 0)
155 dy2 = ctxShadow->offset().y();
156 else
157 dy1 = -ctxShadow->offset().y();
158 // expand the clip rect to include the text shadow as well
159 clip.adjust(dx1, dx2, dy1, dy2);
160 clip.adjust(-ctxShadow->m_blurDistance, -ctxShadow->m_blurDistance, ctxShadow->m_blurDistance, ctxShadow->m_blurDistance);
161 }
162 p->save();
163 p->setClipRect(clip.toRect(), Qt::IntersectClip);
164 pt.setY(pt.y() - ascent);
165
166 if (ctxShadow->m_type != ContextShadow::NoShadow) {
167 ContextShadow* ctxShadow = ctx->contextShadow();
168 if (!ctxShadow->mustUseContextShadow(ctx)) {
169 p->save();
170 p->setPen(ctxShadow->m_color);
171 p->translate(ctxShadow->offset());
172 line.draw(p, pt);
173 p->restore();
174 } else {
175 QPainter* shadowPainter = ctxShadow->beginShadowLayer(ctx, boundingRect);
176 if (shadowPainter) {
177 // Since it will be blurred anyway, we don't care about render hints.
178 shadowPainter->setFont(p->font());
179 shadowPainter->setPen(ctxShadow->m_color);
180 line.draw(shadowPainter, pt);
181 ctxShadow->endShadowLayer(ctx);
182 }
183 }
184 }
185 p->setPen(textFillPen);
186 line.draw(p, pt);
187 p->restore();
188 return;
189 }
190 int skipWidth = QFontMetrics(font).width(string, from, Qt::TextBypassShaping);
191 pt.setX(pt.x() + skipWidth);
192 string = fromRawDataWithoutRef(sanitized, from, to - from);
193 }
194
195 p->setFont(font);
196
197 int flags = run.rtl() ? Qt::TextForceRightToLeft : Qt::TextForceLeftToRight;
198 if (!isComplexText && !(ctx->textDrawingMode() & TextModeStroke))
199 flags |= Qt::TextBypassShaping;
200
201 QPainterPath textStrokePath;
202 if (ctx->textDrawingMode() & TextModeStroke)
203 textStrokePath.addText(pt, font, string);
204
205 ContextShadow* ctxShadow = ctx->contextShadow();
206 if (ctxShadow->m_type != ContextShadow::NoShadow) {
207 if (ctx->textDrawingMode() & TextModeFill) {
208 if (ctxShadow->m_type != ContextShadow::BlurShadow) {
209 p->save();
210 p->setPen(ctxShadow->m_color);
211 p->translate(ctxShadow->offset());
212 p->drawText(pt, string, flags, run.expansion());
213 p->restore();
214 } else {
215 QFontMetrics fm(font);
216 QRectF boundingRect(pt.x(), point.y() - fm.ascent(), fm.width(string, -1, flags), fm.height());
217 QPainter* shadowPainter = ctxShadow->beginShadowLayer(ctx, boundingRect);
218 if (shadowPainter) {
219 // Since it will be blurred anyway, we don't care about render hints.
220 shadowPainter->setFont(p->font());
221 shadowPainter->setPen(ctxShadow->m_color);
222 shadowPainter->drawText(pt, string, flags, run.expansion());
223 ctxShadow->endShadowLayer(ctx);
224 }
225 }
226 } else if (ctx->textDrawingMode() & TextModeStroke) {
227 if (ctxShadow->m_type != ContextShadow::BlurShadow) {
228 p->translate(ctxShadow->offset());
229 p->strokePath(textStrokePath, QPen(ctxShadow->m_color));
230 p->translate(-ctxShadow->offset());
231 } else {
232 QFontMetrics fm(font);
233 QRectF boundingRect(pt.x(), point.y() - fm.ascent(), fm.width(string, -1, flags), fm.height());
234 QPainter* shadowPainter = ctxShadow->beginShadowLayer(ctx, boundingRect);
235 if (shadowPainter) {
236 // Since it will be blurred anyway, we don't care about render hints.
237 shadowPainter->setFont(p->font());
238 shadowPainter->strokePath(textStrokePath, QPen(ctxShadow->m_color));
239 ctxShadow->endShadowLayer(ctx);
240 }
241 }
242 }
243 }
244
245 if (ctx->textDrawingMode() & TextModeStroke)
246 p->strokePath(textStrokePath, textStrokePen);
247
248 if (ctx->textDrawingMode() & TextModeFill) {
249 QPen previousPen = p->pen();
250 p->setPen(textFillPen);
251 p->drawText(pt, string, flags, run.expansion());
252 p->setPen(previousPen);
253 }
254 }
255
drawComplexText(GraphicsContext * ctx,const TextRun & run,const FloatPoint & point,int from,int to) const256 void Font::drawComplexText(GraphicsContext* ctx, const TextRun& run, const FloatPoint& point, int from, int to) const
257 {
258 drawTextCommon(ctx, run, point, from, to, font(), /* isComplexText = */true);
259 }
260
floatWidthForComplexText(const TextRun & run,HashSet<const SimpleFontData * > *,GlyphOverflow *) const261 float Font::floatWidthForComplexText(const TextRun& run, HashSet<const SimpleFontData*>*, GlyphOverflow*) const
262 {
263 if (!primaryFont()->platformData().size())
264 return 0;
265
266 if (!run.length())
267 return 0;
268
269 if (run.length() == 1 && treatAsSpace(run[0]))
270 return QFontMetrics(font()).width(space) + run.expansion();
271
272 String sanitized = Font::normalizeSpaces(run.characters(), run.length());
273 QString string = fromRawDataWithoutRef(sanitized);
274
275 int w = QFontMetrics(font()).width(string);
276 // WebKit expects us to ignore word spacing on the first character (as opposed to what Qt does)
277 if (treatAsSpace(run[0]))
278 w -= m_wordSpacing;
279
280 return w + run.expansion();
281 }
282
offsetForPositionForComplexText(const TextRun & run,float position,bool) const283 int Font::offsetForPositionForComplexText(const TextRun& run, float position, bool) const
284 {
285 String sanitized = Font::normalizeSpaces(run.characters(), run.length());
286 QString string = fromRawDataWithoutRef(sanitized);
287
288 QTextLayout layout(string, font());
289 QTextLine line = setupLayout(&layout, run);
290 return line.xToCursor(position);
291 }
292
selectionRectForComplexText(const TextRun & run,const FloatPoint & pt,int h,int from,int to) const293 FloatRect Font::selectionRectForComplexText(const TextRun& run, const FloatPoint& pt, int h, int from, int to) const
294 {
295 String sanitized = Font::normalizeSpaces(run.characters(), run.length());
296 QString string = fromRawDataWithoutRef(sanitized);
297
298 QTextLayout layout(string, font());
299 QTextLine line = setupLayout(&layout, run);
300
301 float x1 = line.cursorToX(from);
302 float x2 = line.cursorToX(to);
303 if (x2 < x1)
304 qSwap(x1, x2);
305
306 return FloatRect(pt.x() + x1, pt.y(), x2 - x1, h);
307 }
308
canReturnFallbackFontsForComplexText()309 bool Font::canReturnFallbackFontsForComplexText()
310 {
311 return false;
312 }
313
drawEmphasisMarksForComplexText(GraphicsContext *,const TextRun &,const AtomicString &,const FloatPoint &,int,int) const314 void Font::drawEmphasisMarksForComplexText(GraphicsContext* /* context */, const TextRun& /* run */, const AtomicString& /* mark */, const FloatPoint& /* point */, int /* from */, int /* to */) const
315 {
316 notImplemented();
317 }
318
319 #if HAVE(QRAWFONT)
drawGlyphs(GraphicsContext * context,const SimpleFontData * fontData,const GlyphBuffer & glyphBuffer,int from,int numGlyphs,const FloatPoint & point) const320 void Font::drawGlyphs(GraphicsContext* context, const SimpleFontData* fontData, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const
321 {
322 if (context->paintingDisabled())
323 return;
324
325 bool shouldFill = context->textDrawingMode() & TextModeFill;
326 bool shouldStroke = context->textDrawingMode() & TextModeStroke;
327
328 // Stroking text should always take the complex path.
329 ASSERT(!shouldStroke);
330
331 // Shadowed text should always take the complex path.
332 ASSERT(context->contextShadow()->m_type == ContextShadow::NoShadow);
333
334 if (!shouldFill && !shouldStroke)
335 return;
336
337 QVector<quint32> glyphIndexes;
338 QVector<QPointF> positions;
339
340 glyphIndexes.reserve(numGlyphs);
341 positions.reserve(numGlyphs);
342
343 float x = 0;
344
345 for (int i = 0; i < numGlyphs; ++i) {
346 Glyph glyph = glyphBuffer.glyphAt(from + i);
347 float advance = glyphBuffer.advanceAt(from + i);
348 if (!glyph)
349 continue;
350 glyphIndexes.append(glyph);
351 positions.append(QPointF(x, 0));
352 x += advance;
353 }
354
355 QGlyphs qtGlyphs;
356 qtGlyphs.setGlyphIndexes(glyphIndexes);
357 qtGlyphs.setPositions(positions);
358 qtGlyphs.setFont(fontData->platformData().rawFont());
359
360 QPainter* painter = context->platformContext();
361
362 QPen previousPen = painter->pen();
363 painter->setPen(fillPenForContext(context));
364 painter->drawGlyphs(point, qtGlyphs);
365 painter->setPen(previousPen);
366 }
367
canExpandAroundIdeographsInComplexText()368 bool Font::canExpandAroundIdeographsInComplexText()
369 {
370 return false;
371 }
372
373 #else // !HAVE(QRAWFONT)
374
drawSimpleText(GraphicsContext * ctx,const TextRun & run,const FloatPoint & point,int from,int to) const375 void Font::drawSimpleText(GraphicsContext* ctx, const TextRun& run, const FloatPoint& point, int from, int to) const
376 {
377 drawTextCommon(ctx, run, point, from, to, font(), /* isComplexText = */false);
378 }
379
offsetForPositionForSimpleText(const TextRun & run,float position,bool includePartialGlyphs) const380 int Font::offsetForPositionForSimpleText(const TextRun& run, float position, bool includePartialGlyphs) const
381 {
382 String sanitized = Font::normalizeSpaces(run.characters(), run.length());
383 QString string = fromRawDataWithoutRef(sanitized);
384
385 QFontMetrics fm(font());
386 float delta = position;
387 int curPos = 0;
388 do {
389 float charWidth = fm.width(string[curPos]);
390 delta -= charWidth;
391 if (includePartialGlyphs) {
392 if (delta + charWidth / 2 <= 0)
393 break;
394 } else {
395 if (delta + charWidth <= 0)
396 break;
397 }
398 } while (++curPos < string.size());
399
400 return curPos;
401 }
402
403
floatWidthForSimpleText(const TextRun & run,GlyphBuffer * glyphBuffer,HashSet<const SimpleFontData * > * fallbackFonts,GlyphOverflow * glyphOverflow) const404 float Font::floatWidthForSimpleText(const TextRun& run, GlyphBuffer* glyphBuffer, HashSet<const SimpleFontData*>* fallbackFonts, GlyphOverflow* glyphOverflow) const
405 {
406 if (!primaryFont()->platformData().size())
407 return 0;
408
409 if (!run.length())
410 return 0;
411
412 String sanitized = Font::normalizeSpaces(run.characters(), run.length());
413 QString string = fromRawDataWithoutRef(sanitized);
414
415 int w = QFontMetrics(font()).width(string, -1, Qt::TextBypassShaping);
416
417 // WebKit expects us to ignore word spacing on the first character (as opposed to what Qt does)
418 if (treatAsSpace(run[0]))
419 w -= m_wordSpacing;
420
421 return w + run.expansion();
422 }
423
424
selectionRectForSimpleText(const TextRun & run,const FloatPoint & pt,int h,int from,int to) const425 FloatRect Font::selectionRectForSimpleText(const TextRun& run, const FloatPoint& pt, int h, int from, int to) const
426 {
427 String sanitized = Font::normalizeSpaces(run.characters(), run.length());
428 QString wholeText = fromRawDataWithoutRef(sanitized);
429 QString selectedText = fromRawDataWithoutRef(sanitized, from, qMin(to - from, wholeText.length() - from));
430
431 int startX = QFontMetrics(font()).width(wholeText, from, Qt::TextBypassShaping);
432 int width = QFontMetrics(font()).width(selectedText, -1, Qt::TextBypassShaping);
433
434 return FloatRect(pt.x() + startX, pt.y(), width, h);
435 }
436
canExpandAroundIdeographsInComplexText()437 bool Font::canExpandAroundIdeographsInComplexText()
438 {
439 return false;
440 }
441
primaryFontHasGlyphForCharacter(UChar32) const442 bool Font::primaryFontHasGlyphForCharacter(UChar32) const
443 {
444 notImplemented();
445 return true;
446 }
447
emphasisMarkAscent(const AtomicString &) const448 int Font::emphasisMarkAscent(const AtomicString&) const
449 {
450 notImplemented();
451 return 0;
452 }
453
emphasisMarkDescent(const AtomicString &) const454 int Font::emphasisMarkDescent(const AtomicString&) const
455 {
456 notImplemented();
457 return 0;
458 }
459
emphasisMarkHeight(const AtomicString &) const460 int Font::emphasisMarkHeight(const AtomicString&) const
461 {
462 notImplemented();
463 return 0;
464 }
465
drawEmphasisMarksForSimpleText(GraphicsContext *,const TextRun &,const AtomicString &,const FloatPoint &,int,int) const466 void Font::drawEmphasisMarksForSimpleText(GraphicsContext* /* context */, const TextRun& /* run */, const AtomicString& /* mark */, const FloatPoint& /* point */, int /* from */, int /* to */) const
467 {
468 notImplemented();
469 }
470 #endif // HAVE(QRAWFONT)
471
font() const472 QFont Font::font() const
473 {
474 QFont f = primaryFont()->getQtFont();
475 f.setWeight(toQFontWeight(weight()));
476 f.setItalic(italic());
477 if (m_letterSpacing != 0)
478 f.setLetterSpacing(QFont::AbsoluteSpacing, m_letterSpacing);
479 if (m_wordSpacing != 0)
480 f.setWordSpacing(m_wordSpacing);
481 return f;
482 }
483
484
485 }
486
487