• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2     Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
3     Copyright (C) 2008 Holger Hans Peter Freyther
4 
5     This library is free software; you can redistribute it and/or
6     modify it under the terms of the GNU Library General Public
7     License as published by the Free Software Foundation; either
8     version 2 of the License, or (at your option) any later version.
9 
10     This library is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13     Library General Public License for more details.
14 
15     You should have received a copy of the GNU Library General Public License
16     along with this library; see the file COPYING.LIB.  If not, write to
17     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18     Boston, MA 02110-1301, USA.
19 */
20 
21 #include "config.h"
22 #include "Font.h"
23 #include "FontDescription.h"
24 #include "FontFallbackList.h"
25 #include "FontSelector.h"
26 
27 #include "GraphicsContext.h"
28 #include <QTextLayout>
29 #include <QPainter>
30 #include <QFontMetrics>
31 #include <QFontInfo>
32 #include <qalgorithms.h>
33 #include <qdebug.h>
34 
35 #include <limits.h>
36 
37 #if QT_VERSION < 0x040400
38 
39 namespace WebCore {
40 
41 struct TextRunComponent {
TextRunComponentWebCore::TextRunComponent42     TextRunComponent() : font(0) {}
43     TextRunComponent(const UChar *start, int length, bool rtl, const QFont *font, int offset, bool sc = false);
44     TextRunComponent(int spaces, bool rtl, const QFont *font, int offset);
45 
isSpaceWebCore::TextRunComponent46     inline bool isSpace() const { return spaces != 0; }
47 
48     QString string;
49     const QFont *font;
50     int width;
51     int offset;
52     int spaces;
53 };
54 
TextRunComponent(const UChar * start,int length,bool rtl,const QFont * f,int o,bool sc)55 TextRunComponent::TextRunComponent(const UChar *start, int length, bool rtl, const QFont *f, int o, bool sc)
56     : string(reinterpret_cast<const QChar*>(start), length)
57     , font(f)
58     , offset(o)
59     , spaces(0)
60 {
61     if (sc)
62         string = string.toUpper();
63     string.prepend(rtl ? QChar(0x202e) : QChar(0x202d));
64     width = QFontMetrics(*font).width(string);
65 }
66 
TextRunComponent(int s,bool rtl,const QFont * f,int o)67 TextRunComponent::TextRunComponent(int s, bool rtl, const QFont *f, int o)
68     : string(s, QLatin1Char(' '))
69     , font(f)
70     , offset(o)
71     , spaces(s)
72 {
73     string.prepend(rtl ? QChar(0x202e) : QChar(0x202d));
74     width = spaces * QFontMetrics(*font).width(QLatin1Char(' '));
75 }
76 
77 
generateComponents(Vector<TextRunComponent,1024> * components,const Font & font,const TextRun & run)78 static int generateComponents(Vector<TextRunComponent, 1024>* components, const Font &font, const TextRun &run)
79 {
80 //     qDebug() << "generateComponents" << QString((const QChar *)run.characters(), run.length());
81     int letterSpacing = font.letterSpacing();
82     int wordSpacing = font.wordSpacing();
83     bool smallCaps = font.fontDescription().smallCaps();
84     int padding = run.padding();
85     int numSpaces = 0;
86     if (padding) {
87         for (int i = 0; i < run.length(); i++)
88             if (Font::treatAsSpace(run[i]))
89                 ++numSpaces;
90     }
91 
92     int offset = 0;
93     const QFont *f = &font.font();
94     if (letterSpacing || smallCaps) {
95         // need to draw every letter on it's own
96         int start = 0;
97         if (Font::treatAsSpace(run[0])) {
98             int add = 0;
99             if (numSpaces) {
100                 add = padding/numSpaces;
101                 padding -= add;
102                 --numSpaces;
103             }
104             components->append(TextRunComponent(1, run.rtl(), &font.font(), offset));
105             offset += add + letterSpacing + components->last().width;
106             start = 1;
107 //         qDebug() << "space at 0" << offset;
108         } else if (smallCaps)
109             f = (QChar::category(run[0]) == QChar::Letter_Lowercase ? &font.scFont() : &font.font());
110 
111         for (int i = 1; i < run.length(); ++i) {
112             uint ch = run[i];
113             if (QChar(ch).isHighSurrogate() && QChar(run[i-1]).isLowSurrogate())
114                 ch = QChar::surrogateToUcs4(ch, run[i-1]);
115             if (QChar(ch).isLowSurrogate() || QChar::category(ch) == QChar::Mark_NonSpacing)
116                 continue;
117             if (Font::treatAsSpace(run[i])) {
118                 int add = 0;
119 //                 qDebug() << "    treatAsSpace:" << i << start;
120                 if (i - start > 0) {
121                     components->append(TextRunComponent(run.characters() + start, i - start,
122                                                         run.rtl(),
123                                                         f, offset, f == &font.scFont()));
124                     offset += components->last().width + letterSpacing;
125 //                     qDebug() << "   appending(1) " << components->last().string << components->last().width;
126                 }
127                 if (numSpaces) {
128                     add = padding/numSpaces;
129                     padding -= add;
130                     --numSpaces;
131                 }
132                 components->append(TextRunComponent(1, run.rtl(), &font.font(), offset));
133                 offset += wordSpacing + add + components->last().width + letterSpacing;
134                 start = i + 1;
135                 continue;
136             } else if (!letterSpacing) {
137 //                 qDebug() << i << char(run[i]) << (QChar::category(ch) == QChar::Letter_Lowercase) <<
138 //                     QFontInfo(*f).pointSizeF();
139                 if (QChar::category(ch) == QChar::Letter_Lowercase) {
140                     if (f == &font.scFont())
141                         continue;
142                 } else {
143                     if (f == &font.font())
144                         continue;
145                 }
146             }
147             if (i - start > 0) {
148                 components->append(TextRunComponent(run.characters() + start, i - start,
149                                                     run.rtl(),
150                                                     f, offset, f == &font.scFont()));
151                 offset += components->last().width + letterSpacing;
152 //                 qDebug() << "   appending(2) " << components->last().string << components->last().width;
153             }
154             if (smallCaps)
155                 f = (QChar::category(ch) == QChar::Letter_Lowercase ? &font.scFont() : &font.font());
156             start = i;
157         }
158         if (run.length() - start > 0) {
159             components->append(TextRunComponent(run.characters() + start, run.length() - start,
160                                                 run.rtl(),
161                                                 f, offset, f == &font.scFont()));
162             offset += components->last().width;
163 //             qDebug() << "   appending(3) " << components->last().string << components->last().width;
164         }
165         offset += letterSpacing;
166     } else {
167         int start = 0;
168         for (int i = 0; i < run.length(); ++i) {
169             if (Font::treatAsSpace(run[i])) {
170                 if (i - start > 0) {
171                     components->append(TextRunComponent(run.characters() + start, i - start,
172                                                         run.rtl(),
173                                                         f, offset));
174                     offset += components->last().width;
175                 }
176                 int add = 0;
177                 if (numSpaces) {
178                     add = padding/numSpaces;
179                     padding -= add;
180                     --numSpaces;
181                 }
182                 components->append(TextRunComponent(1, run.rtl(), &font.font(), offset));
183                 offset += add + components->last().width;
184                 if (i)
185                     offset += wordSpacing;
186                 start = i + 1;
187             }
188         }
189         if (run.length() - start > 0) {
190             components->append(TextRunComponent(run.characters() + start, run.length() - start,
191                                                 run.rtl(),
192                                                 f, offset));
193             offset += components->last().width;
194         }
195     }
196     return offset;
197 }
198 
drawComplexText(GraphicsContext * ctx,const TextRun & run,const FloatPoint & point,int from,int to) const199 void Font::drawComplexText(GraphicsContext* ctx, const TextRun& run, const FloatPoint& point, int from, int to) const
200 {
201     if (to < 0)
202         to = run.length();
203 
204     QPainter *p = ctx->platformContext();
205     Color color = ctx->fillColor();
206     p->setPen(QColor(color));
207 
208     Vector<TextRunComponent, 1024> components;
209     int w = generateComponents(&components, *this, run);
210 
211     if (from > 0 || to < run.length()) {
212         FloatRect clip = selectionRectForComplexText(run,
213                                               IntPoint(qRound(point.x()), qRound(point.y())),
214                                               QFontMetrics(font()).height(), from, to);
215         QRectF rect(clip.x(), clip.y() - ascent(), clip.width(), clip.height());
216         p->save();
217         p->setClipRect(rect.toRect());
218     }
219 
220     if (run.rtl()) {
221         for (int i = 0; i < components.size(); ++i) {
222             if (!components.at(i).isSpace()) {
223                 p->setFont(*components.at(i).font);
224                 QPointF pt(point.x() + w - components.at(i).offset - components.at(i).width, point.y());
225                 p->drawText(pt, components.at(i).string);
226             }
227         }
228     } else {
229         for (int i = 0; i < components.size(); ++i) {
230             if (!components.at(i).isSpace()) {
231                 p->setFont(*components.at(i).font);
232                 QPointF pt(point.x() + components.at(i).offset, point.y());
233                 p->drawText(pt, components.at(i).string);
234             }
235         }
236     }
237     if (from > 0 || to < run.length())
238         p->restore();
239 }
240 
floatWidthForComplexText(const TextRun & run) const241 float Font::floatWidthForComplexText(const TextRun& run) const
242 {
243     Vector<TextRunComponent, 1024> components;
244     int w = generateComponents(&components, *this, run);
245 
246     return w;
247 }
248 
offsetForPositionForComplexText(const TextRun & run,int position,bool includePartialGlyphs) const249 int Font::offsetForPositionForComplexText(const TextRun& run, int position, bool includePartialGlyphs) const
250 {
251     Vector<TextRunComponent, 1024> components;
252     int w = generateComponents(&components, *this, run);
253 
254     int offset = 0;
255     if (run.rtl()) {
256         for (int i = 0; i < components.size(); ++i) {
257             int xe = w - components.at(i).offset;
258             int xs = xe - components.at(i).width;
259             if (position >= xs) {
260                 QTextLayout layout(components.at(i).string, *components.at(i).font);
261                 layout.beginLayout();
262                 QTextLine l = layout.createLine();
263                 if (!l.isValid())
264                     return offset;
265 
266                 l.setLineWidth(INT_MAX / 256);
267                 layout.endLayout();
268 
269                 if (position - xs >= l.width())
270                     return offset;
271                 int cursor = l.xToCursor(position - xs);
272                 if (cursor > 1)
273                     --cursor;
274                 return offset + cursor;
275             } else
276                 offset += components.at(i).string.length() - 1;
277         }
278     } else {
279         for (int i = 0; i < components.size(); ++i) {
280             int xs = components.at(i).offset;
281             int xe = xs + components.at(i).width;
282             if (position <= xe) {
283                 QTextLayout layout(components.at(i).string, *components.at(i).font);
284                 layout.beginLayout();
285                 QTextLine l = layout.createLine();
286                 if (!l.isValid())
287                     return offset;
288 
289                 l.setLineWidth(INT_MAX / 256);
290                 layout.endLayout();
291 
292                 if (position - xs >= l.width())
293                     return offset + components.at(i).string.length() - 1;
294                 int cursor = l.xToCursor(position - xs);
295                 if (cursor > 1)
296                     --cursor;
297                 return offset + cursor;
298             } else
299                 offset += components.at(i).string.length() - 1;
300         }
301     }
302     return run.length();
303 }
304 
cursorToX(const Vector<TextRunComponent,1024> & components,int width,bool rtl,int cursor)305 static float cursorToX(const Vector<TextRunComponent, 1024>& components, int width, bool rtl, int cursor)
306 {
307     int start = 0;
308     for (int i = 0; i < components.size(); ++i) {
309         if (start + components.at(i).string.length() - 1 < cursor) {
310             start += components.at(i).string.length() - 1;
311             continue;
312         }
313         int xs = components.at(i).offset;
314         if (rtl)
315             xs = width - xs - components.at(i).width;
316         QTextLayout layout(components.at(i).string, *components.at(i).font);
317         layout.beginLayout();
318         QTextLine l = layout.createLine();
319         if (!l.isValid())
320             return 0;
321 
322         l.setLineWidth(INT_MAX / 256);
323         layout.endLayout();
324 
325         return xs + l.cursorToX(cursor - start + 1);
326     }
327     return width;
328 }
329 
selectionRectForComplexText(const TextRun & run,const IntPoint & pt,int h,int from,int to) const330 FloatRect Font::selectionRectForComplexText(const TextRun& run, const IntPoint& pt,
331                                      int h, int from, int to) const
332 {
333     Vector<TextRunComponent, 1024> components;
334     int w = generateComponents(&components, *this, run);
335 
336     if (from == 0 && to == run.length())
337         return FloatRect(pt.x(), pt.y(), w, h);
338 
339     float x1 = cursorToX(components, w, run.rtl(), from);
340     float x2 = cursorToX(components, w, run.rtl(), to);
341     if (x2 < x1)
342         qSwap(x1, x2);
343 
344     return FloatRect(pt.x() + x1, pt.y(), x2 - x1, h);
345 }
346 
lineGap() const347 int Font::lineGap() const
348 {
349     return QFontMetrics(m_font).leading();
350 }
351 
352 }
353 
354 #endif
355