57b279c9c43866b6e7ccddd795b2ae192837fe14
[WebKit-https.git] / WebCore / platform / graphics / qt / FontQt.cpp
1 /*
2     Copyright (C) 2007 Trolltech ASA
3
4     This library is free software; you can redistribute it and/or
5     modify it under the terms of the GNU Library General Public
6     License as published by the Free Software Foundation; either
7     version 2 of the License, or (at your option) any later version.
8
9     This library is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12     Library General Public License for more details.
13
14     You should have received a copy of the GNU Library General Public License
15     along with this library; see the file COPYING.LIB.  If not, write to
16     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17     Boston, MA 02110-1301, USA.
18
19     This class provides all functionality needed for loading images, style sheets and html
20     pages from the web. It has a memory cache for these objects.
21 */
22 #include "config.h"
23 #include "Font.h"
24 #include "FontDescription.h"
25 #include "FontSelector.h"
26 #include "FontStyle.h"
27
28 #include "GraphicsContext.h"
29 #include <QTextLayout>
30 #include <QPainter>
31 #include <QFontMetrics>
32 #include <QFontInfo>
33 #include <qalgorithms.h>
34 #include <qdebug.h>
35
36 #include <limits.h>
37 namespace WebCore {
38
39 struct TextRunComponent {
40     TextRunComponent() : font(0) {}
41     TextRunComponent(const UChar *start, int length, bool rtl, const QFont *font, int offset, bool sc = false);
42     TextRunComponent(int spaces, bool rtl, const QFont *font, int offset);
43
44     inline bool isSpace() const { return spaces != 0; }
45
46     QString string;
47     const QFont *font;
48     int width;
49     int offset;
50     int spaces;
51 };
52
53 TextRunComponent::TextRunComponent(const UChar *start, int length, bool rtl, const QFont *f, int o, bool sc)
54     : string(reinterpret_cast<const QChar*>(start), length)
55     , font(f)
56     , offset(o)
57     , spaces(0)
58 {
59     if (sc)
60         string = string.toUpper();
61     string.prepend(rtl ? QChar(0x202e) : QChar(0x202d));
62     width = QFontMetrics(*font).width(string);
63 }
64
65 TextRunComponent::TextRunComponent(int s, bool rtl, const QFont *f, int o)
66     : string(s, QLatin1Char(' '))
67     , font(f)
68     , offset(o)
69     , spaces(s)
70 {
71     string.prepend(rtl ? QChar(0x202e) : QChar(0x202d));
72     width = spaces * QFontMetrics(*font).width(QLatin1Char(' '));
73 }
74
75
76 Font::Font()
77     : m_letterSpacing(0)
78     , m_wordSpacing(0)
79     , m_font()
80     , m_scFont()
81 {
82     QFontMetrics metrics(m_font);
83     m_spaceWidth = metrics.width(QLatin1Char(' '));
84     qreal pointsize = m_font.pointSizeF();
85     if (pointsize > 0)
86         m_scFont.setPointSizeF(pointsize*0.7);
87     else
88         m_scFont.setPixelSize(qRound(m_font.pixelSize()*.7));
89 }
90
91 Font::Font(const FontDescription& description, short letterSpacing, short wordSpacing)
92     : m_fontDescription(description)
93     , m_letterSpacing(letterSpacing)
94     , m_wordSpacing(wordSpacing)
95 {
96     const FontFamily* family = &description.family();
97     QString familyName;
98     while (family) {
99         familyName += family->family();
100         family = family->next();
101         if (family)
102             familyName += QLatin1Char(',');
103     }
104
105     m_font.setFamily(familyName);
106     m_font.setPixelSize(qRound(description.computedSize()));
107     m_font.setItalic(description.italic());
108     if (description.bold()) {
109         // Qt's Bold is 75, Webkit is 63.
110         m_font.setWeight(QFont::Bold);
111     } else {
112         m_font.setWeight(description.weight());
113     }
114     QFontMetrics metrics = QFontMetrics(m_font);
115     m_spaceWidth = metrics.width(QLatin1Char(' '));
116     m_scFont = m_font;
117     m_scFont.setPixelSize(qRound(description.computedSize()*.7));
118 }
119
120 Font::~Font()
121 {
122 }
123     
124 Font::Font(const Font& other)
125     : m_fontDescription(other.m_fontDescription)
126     , m_letterSpacing(other.m_letterSpacing)
127     , m_wordSpacing(other.m_wordSpacing)
128     , m_font(other.m_font)
129     , m_scFont(other.m_scFont)
130     , m_spaceWidth(other.m_spaceWidth)
131 {
132 }
133
134 Font& Font::operator=(const Font& other)
135 {
136     m_fontDescription = other.m_fontDescription;
137     m_letterSpacing = other.m_letterSpacing;
138     m_wordSpacing = other.m_wordSpacing;
139     m_font = other.m_font;
140     m_scFont = other.m_scFont;
141     m_spaceWidth = other.m_spaceWidth;
142     return *this;
143 }
144
145 bool Font::operator==(const Font& other) const
146 {
147     return m_fontDescription == other.m_fontDescription
148         && m_letterSpacing == other.m_letterSpacing
149         && m_wordSpacing == other.m_wordSpacing
150         && m_font == other.m_font
151         && m_scFont == other.m_scFont
152         && m_spaceWidth == other.m_spaceWidth;
153 }
154
155 void Font::update(PassRefPtr<FontSelector>) const
156 {
157     // don't think we need this
158 }
159
160 static int generateComponents(Vector<TextRunComponent, 1024>* components, const Font &font, const TextRun &run, const FontStyle &style)
161 {
162 //     qDebug() << "generateComponents" << QString((const QChar *)run.characters(), run.length());
163     int letterSpacing = font.letterSpacing();
164     int wordSpacing = font.wordSpacing();
165     bool smallCaps = font.fontDescription().smallCaps();
166     int padding = style.padding();
167     int numSpaces = 0;
168     if (padding) {
169         for (int i = 0; i < run.length(); i++)
170             if (Font::treatAsSpace(run[i]))
171                 ++numSpaces;      
172     }
173
174     int offset = 0;
175     const QFont *f = &font.font();
176     if (letterSpacing || smallCaps) {
177         // need to draw every letter on it's own
178         int start = 0;
179         if (Font::treatAsSpace(run[0])) {
180             int add = 0;
181             if (numSpaces) {
182                 add = padding/numSpaces;
183                 padding -= add;
184                 --numSpaces;
185             }
186             components->append(TextRunComponent(1, style.rtl(), &font.font(), offset));
187             offset += add + letterSpacing + components->last().width;
188             start = 1;
189 //         qDebug() << "space at 0" << offset;
190         } else if (smallCaps) {
191             f = (QChar::category(run[0]) == QChar::Letter_Lowercase ? &font.scFont() : &font.font());
192         }
193         for (int i = 1; i < run.length(); ++i) {
194             uint ch = run[i];
195             if (QChar(ch).isHighSurrogate() && QChar(run[i-1]).isLowSurrogate())
196                 ch = QChar::surrogateToUcs4(ch, run[i-1]);
197             if (QChar(ch).isLowSurrogate() || QChar::category(ch) == QChar::Mark_NonSpacing)
198                 continue;
199             if (Font::treatAsSpace(run[i])) {
200                 int add = 0;
201 //                 qDebug() << "    treatAsSpace:" << i << start;
202                 if (i - start > 0) {
203                     components->append(TextRunComponent(run.characters() + start, i - start,
204                                                         style.rtl(), 
205                                                         f, offset, f == &font.scFont()));
206                     offset += components->last().width + letterSpacing;
207 //                     qDebug() << "   appending(1) " << components->last().string << components->last().width;
208                 }
209                 if (numSpaces) {
210                     add = padding/numSpaces;
211                     padding -= add;
212                     --numSpaces;
213                 }
214                 components->append(TextRunComponent(1, style.rtl(), &font.font(), offset));
215                 offset += wordSpacing + add + components->last().width + letterSpacing;
216                 start = i + 1;
217                 continue;
218             } else if (!letterSpacing) {
219 //                 qDebug() << i << char(run[i]) << (QChar::category(ch) == QChar::Letter_Lowercase) <<
220 //                     QFontInfo(*f).pointSizeF();
221                 if (QChar::category(ch) == QChar::Letter_Lowercase) {
222                     if (f == &font.scFont())
223                         continue;
224                 } else {
225                     if (f == &font.font())
226                         continue;
227                 }
228             }
229             if (i - start > 0) {
230                 components->append(TextRunComponent(run.characters() + start, i - start,
231                                                     style.rtl(), 
232                                                     f, offset, f == &font.scFont()));
233                 offset += components->last().width + letterSpacing;
234 //                 qDebug() << "   appending(2) " << components->last().string << components->last().width;
235             }
236             if (smallCaps)
237                 f = (QChar::category(ch) == QChar::Letter_Lowercase ? &font.scFont() : &font.font());
238             start = i;
239         }
240         if (run.length() - start > 0) {
241             components->append(TextRunComponent(run.characters() + start, run.length() - start,
242                                                 style.rtl(), 
243                                                 f, offset, f == &font.scFont()));
244             offset += components->last().width;
245 //             qDebug() << "   appending(3) " << components->last().string << components->last().width;
246         }
247         offset += letterSpacing;
248     } else {
249         int start = 0;
250         for (int i = 0; i < run.length(); ++i) {
251             if (Font::treatAsSpace(run[i])) {
252                 if (i - start > 0) {
253                     components->append(TextRunComponent(run.characters() + start, i - start,
254                                                         style.rtl(), 
255                                                         f, offset));
256                     offset += components->last().width;
257                 }
258                 int add = 0;
259                 if (numSpaces) {
260                     add = padding/numSpaces;
261                     padding -= add;
262                     --numSpaces;
263                 }
264                 components->append(TextRunComponent(1, style.rtl(), &font.font(), offset));
265                 offset += add + components->last().width;
266                 if (i)
267                     offset += wordSpacing;
268                 start = i + 1;
269             }
270         }
271         if (run.length() - start > 0) {
272             components->append(TextRunComponent(run.characters() + start, run.length() - start,
273                                                 style.rtl(), 
274                                                 f, offset));
275             offset += components->last().width;
276         }
277     }
278     return offset;
279 }
280
281 void Font::drawText(GraphicsContext* ctx, const TextRun& run, const FontStyle& style, const FloatPoint& point, int from, int to) const
282 {
283     if (to < 0)
284         to = run.length();
285     QPainter *p = ctx->platformContext();
286     Color color = ctx->fillColor();
287     p->setPen(QColor(color));
288
289     Vector<TextRunComponent, 1024> components;
290     int w = generateComponents(&components, *this, run, style);
291
292     if (from > 0 || to < run.length()) {
293         FloatRect clip = selectionRectForText(run, style,
294                                               IntPoint(qRound(point.x()), qRound(point.y())),
295                                               QFontMetrics(m_font).height(), from, to);
296         QRectF rect(clip.x(), clip.y() - ascent(), clip.width(), clip.height());
297         p->save();
298         p->setClipRect(rect.toRect());
299     }
300
301     if (style.rtl()) {
302         for (int i = 0; i < components.size(); ++i) {
303             if (!components.at(i).isSpace()) {
304                 p->setFont(*components.at(i).font);
305                 QPointF pt(point.x() + w - components.at(i).offset - components.at(i).width, point.y());
306                 p->drawText(pt, components.at(i).string);
307             }
308         }
309     } else {
310         for (int i = 0; i < components.size(); ++i) {
311             if (!components.at(i).isSpace()) {
312                 p->setFont(*components.at(i).font);
313                 QPointF pt(point.x() + components.at(i).offset, point.y());
314                 p->drawText(pt, components.at(i).string);
315             }
316         }
317     }
318     if (from > 0 || to < run.length())
319         p->restore();
320 }
321
322 int Font::width(const TextRun& run, const FontStyle& style) const
323 {
324     Vector<TextRunComponent, 1024> components;
325     int w = generateComponents(&components, *this, run, style);
326
327 //     qDebug() << "     width=" << w;
328     return w;
329 }
330
331 int Font::width(const TextRun& run) const
332 {
333     return width(run, FontStyle());
334 }
335
336 float Font::floatWidth(const TextRun& run, const FontStyle& style) const
337 {
338     return width(run, style);
339 }
340
341 float Font::floatWidth(const TextRun& run) const
342 {
343     return width(run);
344 }
345
346 int Font::offsetForPosition(const TextRun& run, const FontStyle& style, int position, bool includePartialGlyphs) const
347 {
348     Vector<TextRunComponent, 1024> components;
349     int w = generateComponents(&components, *this, run, style);
350
351     int offset = 0;
352     if (style.rtl()) {
353         for (int i = 0; i < components.size(); ++i) {
354             int xe = w - components.at(i).offset;
355             int xs = xe - components.at(i).width;
356             if (position >= xs) {
357                 QTextLayout layout(components.at(i).string, *components.at(i).font);
358                 layout.beginLayout();
359                 QTextLine l = layout.createLine();
360                 if (!l.isValid()) 
361                     return offset;
362                 
363                 l.setLineWidth(INT_MAX/256);
364                 layout.endLayout();
365
366                 if (position - xs >= l.width())
367                     return offset;
368                 int cursor = l.xToCursor(position - xs);
369                 if (cursor > 1)
370                     --cursor;
371                 return offset + cursor;
372             } else {
373                 offset += components.at(i).string.length() - 1;
374             }
375         }
376     } else {
377         for (int i = 0; i < components.size(); ++i) {
378             int xs = components.at(i).offset;
379             int xe = xs + components.at(i).width;
380             if (position <= xe) {
381                 QTextLayout layout(components.at(i).string, *components.at(i).font);
382                 layout.beginLayout();
383                 QTextLine l = layout.createLine();
384                 if (!l.isValid())
385                     return offset;
386                 
387                 l.setLineWidth(INT_MAX/256);
388                 layout.endLayout();
389
390                 if (position - xs >= l.width())
391                     return offset + components.at(i).string.length() - 1;
392                 int cursor = l.xToCursor(position - xs);
393                 if (cursor > 1)
394                     --cursor;
395                 return offset + cursor;
396             } else {
397                 offset += components.at(i).string.length() - 1;
398             }
399         }
400     }
401     return run.length();
402 }
403
404 static float cursorToX(const Vector<TextRunComponent, 1024>& components, int width,
405                      const FontStyle& style, int cursor)
406 {
407     int start = 0;
408     for (int i = 0; i < components.size(); ++i) {
409         if (start + components.at(i).string.length() - 1 < cursor) {
410             start += components.at(i).string.length() - 1;
411             continue;
412         }
413         int xs = components.at(i).offset;
414         if (style.rtl())
415             xs = width - xs - components.at(i).width;
416         QTextLayout layout(components.at(i).string, *components.at(i).font);
417         layout.beginLayout();
418         QTextLine l = layout.createLine();
419         if (!l.isValid())
420             return 0;
421         
422         l.setLineWidth(INT_MAX/256);
423         layout.endLayout();
424         
425         return xs + l.cursorToX(cursor - start + 1);
426     }
427     return width;
428 }
429
430 FloatRect Font::selectionRectForText(const TextRun& run, const FontStyle& style, const IntPoint& pt,
431                                      int h, int from, int to) const
432 {
433     Vector<TextRunComponent, 1024> components;
434     int w = generateComponents(&components, *this, run, style);
435
436     if (from == 0 && to == run.length())
437         return FloatRect(pt.x(), pt.y(), w, h);
438
439     float x1 = cursorToX(components, w, style, from);
440     float x2 = cursorToX(components, w, style, to);
441     if (x2 < x1)
442         qSwap(x1, x2);
443
444     return FloatRect(pt.x() + x1, pt.y(), x2 - x1, h);
445 }
446
447 bool Font::isFixedPitch() const
448 {
449     return QFontInfo(m_font).fixedPitch();
450 }
451
452 // Metrics that we query the FontFallbackList for.
453 int Font::ascent() const
454 {
455     return QFontMetrics(m_font).ascent();
456 }
457
458 int Font::descent() const
459 {
460     return QFontMetrics(m_font).descent();
461 }
462
463 int Font::lineSpacing() const
464 {
465     return QFontMetrics(m_font).lineSpacing();
466 }
467
468 float Font::xHeight() const
469 {
470     return QFontMetrics(m_font).xHeight();
471 }
472
473 unsigned Font::unitsPerEm() const
474 {
475     return 1; // FIXME!
476 }
477
478 int Font::spaceWidth() const
479 {
480     return m_spaceWidth;
481 }
482
483 }