2 Copyright (C) 2007 Trolltech ASA
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.
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.
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.
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.
24 #include "FontDescription.h"
25 #include "FontSelector.h"
26 #include "FontStyle.h"
28 #include "GraphicsContext.h"
29 #include <QTextLayout>
31 #include <QFontMetrics>
33 #include <qalgorithms.h>
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);
44 inline bool isSpace() const { return spaces != 0; }
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)
60 string = string.toUpper();
61 string.prepend(rtl ? QChar(0x202e) : QChar(0x202d));
62 width = QFontMetrics(*font).width(string);
65 TextRunComponent::TextRunComponent(int s, bool rtl, const QFont *f, int o)
66 : string(s, QLatin1Char(' '))
71 string.prepend(rtl ? QChar(0x202e) : QChar(0x202d));
72 width = spaces * QFontMetrics(*font).width(QLatin1Char(' '));
82 QFontMetrics metrics(m_font);
83 m_spaceWidth = metrics.width(QLatin1Char(' '));
84 qreal pointsize = m_font.pointSizeF();
86 m_scFont.setPointSizeF(pointsize*0.7);
88 m_scFont.setPixelSize(qRound(m_font.pixelSize()*.7));
91 Font::Font(const FontDescription& description, short letterSpacing, short wordSpacing)
92 : m_fontDescription(description)
93 , m_letterSpacing(letterSpacing)
94 , m_wordSpacing(wordSpacing)
96 const FontFamily* family = &description.family();
99 familyName += family->family();
100 family = family->next();
102 familyName += QLatin1Char(',');
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);
112 m_font.setWeight(description.weight());
114 QFontMetrics metrics = QFontMetrics(m_font);
115 m_spaceWidth = metrics.width(QLatin1Char(' '));
117 m_scFont.setPixelSize(qRound(description.computedSize()*.7));
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)
134 Font& Font::operator=(const Font& other)
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;
145 bool Font::operator==(const Font& other) const
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;
155 void Font::update(PassRefPtr<FontSelector>) const
157 // don't think we need this
160 static int generateComponents(Vector<TextRunComponent, 1024>* components, const Font &font, const TextRun &run, const FontStyle &style)
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();
169 for (int i = 0; i < run.length(); i++)
170 if (Font::treatAsSpace(run[i]))
175 const QFont *f = &font.font();
176 if (letterSpacing || smallCaps) {
177 // need to draw every letter on it's own
179 if (Font::treatAsSpace(run[0])) {
182 add = padding/numSpaces;
186 components->append(TextRunComponent(1, style.rtl(), &font.font(), offset));
187 offset += add + letterSpacing + components->last().width;
189 // qDebug() << "space at 0" << offset;
190 } else if (smallCaps) {
191 f = (QChar::category(run[0]) == QChar::Letter_Lowercase ? &font.scFont() : &font.font());
193 for (int i = 1; i < run.length(); ++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)
199 if (Font::treatAsSpace(run[i])) {
201 // qDebug() << " treatAsSpace:" << i << start;
203 components->append(TextRunComponent(run.characters() + start, i - start,
205 f, offset, f == &font.scFont()));
206 offset += components->last().width + letterSpacing;
207 // qDebug() << " appending(1) " << components->last().string << components->last().width;
210 add = padding/numSpaces;
214 components->append(TextRunComponent(1, style.rtl(), &font.font(), offset));
215 offset += wordSpacing + add + components->last().width + letterSpacing;
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())
225 if (f == &font.font())
230 components->append(TextRunComponent(run.characters() + start, i - start,
232 f, offset, f == &font.scFont()));
233 offset += components->last().width + letterSpacing;
234 // qDebug() << " appending(2) " << components->last().string << components->last().width;
237 f = (QChar::category(ch) == QChar::Letter_Lowercase ? &font.scFont() : &font.font());
240 if (run.length() - start > 0) {
241 components->append(TextRunComponent(run.characters() + start, run.length() - start,
243 f, offset, f == &font.scFont()));
244 offset += components->last().width;
245 // qDebug() << " appending(3) " << components->last().string << components->last().width;
247 offset += letterSpacing;
250 for (int i = 0; i < run.length(); ++i) {
251 if (Font::treatAsSpace(run[i])) {
253 components->append(TextRunComponent(run.characters() + start, i - start,
256 offset += components->last().width;
260 add = padding/numSpaces;
264 components->append(TextRunComponent(1, style.rtl(), &font.font(), offset));
265 offset += add + components->last().width;
267 offset += wordSpacing;
271 if (run.length() - start > 0) {
272 components->append(TextRunComponent(run.characters() + start, run.length() - start,
275 offset += components->last().width;
281 void Font::drawText(GraphicsContext* ctx, const TextRun& run, const FontStyle& style, const FloatPoint& point, int from, int to) const
285 QPainter *p = ctx->platformContext();
286 Color color = ctx->fillColor();
287 p->setPen(QColor(color));
289 Vector<TextRunComponent, 1024> components;
290 int w = generateComponents(&components, *this, run, style);
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());
298 p->setClipRect(rect.toRect());
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);
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);
318 if (from > 0 || to < run.length())
322 int Font::width(const TextRun& run, const FontStyle& style) const
324 Vector<TextRunComponent, 1024> components;
325 int w = generateComponents(&components, *this, run, style);
327 // qDebug() << " width=" << w;
331 int Font::width(const TextRun& run) const
333 return width(run, FontStyle());
336 float Font::floatWidth(const TextRun& run, const FontStyle& style) const
338 return width(run, style);
341 float Font::floatWidth(const TextRun& run) const
346 int Font::offsetForPosition(const TextRun& run, const FontStyle& style, int position, bool includePartialGlyphs) const
348 Vector<TextRunComponent, 1024> components;
349 int w = generateComponents(&components, *this, run, style);
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();
363 l.setLineWidth(INT_MAX/256);
366 if (position - xs >= l.width())
368 int cursor = l.xToCursor(position - xs);
371 return offset + cursor;
373 offset += components.at(i).string.length() - 1;
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();
387 l.setLineWidth(INT_MAX/256);
390 if (position - xs >= l.width())
391 return offset + components.at(i).string.length() - 1;
392 int cursor = l.xToCursor(position - xs);
395 return offset + cursor;
397 offset += components.at(i).string.length() - 1;
404 static float cursorToX(const Vector<TextRunComponent, 1024>& components, int width,
405 const FontStyle& style, int cursor)
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;
413 int xs = components.at(i).offset;
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();
422 l.setLineWidth(INT_MAX/256);
425 return xs + l.cursorToX(cursor - start + 1);
430 FloatRect Font::selectionRectForText(const TextRun& run, const FontStyle& style, const IntPoint& pt,
431 int h, int from, int to) const
433 Vector<TextRunComponent, 1024> components;
434 int w = generateComponents(&components, *this, run, style);
436 if (from == 0 && to == run.length())
437 return FloatRect(pt.x(), pt.y(), w, h);
439 float x1 = cursorToX(components, w, style, from);
440 float x2 = cursorToX(components, w, style, to);
444 return FloatRect(pt.x() + x1, pt.y(), x2 - x1, h);
447 bool Font::isFixedPitch() const
449 return QFontInfo(m_font).fixedPitch();
452 // Metrics that we query the FontFallbackList for.
453 int Font::ascent() const
455 return QFontMetrics(m_font).ascent();
458 int Font::descent() const
460 return QFontMetrics(m_font).descent();
463 int Font::lineSpacing() const
465 return QFontMetrics(m_font).lineSpacing();
468 float Font::xHeight() const
470 return QFontMetrics(m_font).xHeight();
473 unsigned Font::unitsPerEm() const
478 int Font::spaceWidth() const