• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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