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