KHtml

font.cpp
1 /**
2  * This file is part of the html renderer for KDE.
3  *
4  * Copyright (C) 1999-2003 Lars Knoll ([email protected])
5  * (C) 1999 Antti Koivisto ([email protected])
6  * (C) 2000 Dirk Mueller ([email protected])
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public License
19  * along with this library; see the file COPYING.LIB. If not, write to
20  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  *
23  */
24 
25 #include "font.h"
26 
27 #include <config-khtml.h>
28 
29 #if HAVE_ALLOCA_H
30 # include <alloca.h>
31 # else
32 # if HAVE_MALLOC_H
33 # include <malloc.h>
34 # else
35 # include <stdlib.h>
36 # endif
37 #endif
38 
39 #include "khtml_debug.h"
40 
41 #include <QHash>
42 #include <QFontDatabase>
43 #include <QtGlobal>
44 
45 // for SVG
46 #include "dom/dom_string.h"
47 
48 namespace khtml
49 {
50 
51 /** closes the current word and returns its width in pixels
52  * @param fm metrics of font to be used
53  * @param str string
54  * @param pos zero-indexed position within @p str upon which all other
55  * indices are based
56  * @param wordStart relative index pointing to the position where the word started
57  * @param wordEnd relative index pointing one position after the word ended
58  * @return the width in pixels. May be 0 if @p wordStart and @p wordEnd were
59  * equal.
60  */
61 static inline int closeWordAndGetWidth(const QFontMetrics &fm, const QChar *str, int pos,
62  int wordStart, int wordEnd)
63 {
64  if (wordEnd <= wordStart) {
65  return 0;
66  }
67 
68  return fm.width(QString::fromRawData(str + pos + wordStart, wordEnd - wordStart));
69 }
70 
71 static inline void drawDirectedText(QPainter *p, Qt::LayoutDirection d,
72  int x, int y, const QString &str)
73 {
74  QString qs = str;
75  // Qt doesn't have a function to force a direction,
76  // so we have to use a the unicode "RTO" character to
77  // (no, setLayoutDirection isn't enough)
78  if (d == Qt::RightToLeft && str[0].direction() == QChar::DirL) {
79  qs.prepend(QChar(0x202E)); // RIGHT-TO-LEFT OVERRIDE
80  } else if (d == Qt::LeftToRight && str[0].direction() == QChar::DirR) {
81  qs.prepend(QChar(0x202D)); // LEFT-TO-RIGHT OVERRIDE
82  }
83 
84  Qt::LayoutDirection saveDir = p->layoutDirection();
85  p->setLayoutDirection(d);
86  // Qt 4 avoids rendering soft-hyphens by default.
87  // Append normal hyphen instead.
88  if (qs.endsWith(QChar(0xad))) {
89  qs.append(QChar('-'));
90  }
91  p->drawText(x, y, qs);
92  p->setLayoutDirection(saveDir);
93 }
94 
95 /** closes the current word and draws it
96  * @param p painter
97  * @param d text direction
98  * @param x current x position, will be inc-/decremented correctly according
99  * to text direction
100  * @param y baseline of text
101  * @param widths list of widths; width of word is expected at position
102  * wordStart
103  * @param str string
104  * @param pos zero-indexed position within @p str upon which all other
105  * indices are based
106  * @param wordStart relative index pointing to the position where the word started,
107  * will be set to wordEnd after function
108  * @param wordEnd relative index pointing one position after the word ended
109  */
111  int &x, int y, const short widths[], const QChar *str, int pos,
112  int &wordStart, int wordEnd)
113 {
114  if (wordEnd <= wordStart) {
115  return;
116  }
117 
118  int width = widths[wordStart];
119  if (d == Qt::RightToLeft) {
120  x -= width;
121  }
122 
123  drawDirectedText(p, d, x, y, QString::fromRawData(str + pos + wordStart, wordEnd - wordStart));
124 
125  if (d != Qt::RightToLeft) {
126  x += width;
127  }
128 
129  wordStart = wordEnd;
130 }
131 
132 void Font::drawText(QPainter *p, int x, int y, const QChar *str, const int slen, int pos, int len,
133  int toAdd, Qt::LayoutDirection d, int from, int to, QColor bg, int uy, int h, int deco) const
134 {
135  if (!str || slen == 0) { // #188910
136  return;
137  }
138 
139  // ### fixme for RTL
140  if (!scFont && !letterSpacing && !wordSpacing && !toAdd && from == -1) {
141  // simply draw it
142  // Due to some unfounded cause QPainter::drawText traverses the
143  // *whole* string when painting, not only the specified
144  // [pos, pos + len) segment. This makes painting *extremely* slow for
145  // long render texts (in the order of several megabytes).
146  // Hence, only hand over the piece of text of the actual inline text box
147  drawDirectedText(p, d, x, y, QString::fromRawData(str + pos, len));
148  } else {
149  if (from < 0) {
150  from = 0;
151  }
152  if (to < 0) {
153  to = len;
154  }
155 
156  int numSpaces = 0;
157  if (toAdd) {
158  for (int i = 0; i < len; ++i)
159  if (str[i + pos].category() == QChar::Separator_Space) {
160  ++numSpaces;
161  }
162  }
163 
164  if (d == Qt::RightToLeft) {
165  const int totWidth = width(str, slen, pos, len, false /*fast algo*/);
166  x += totWidth + toAdd;
167  }
168 
169  // ### sc could be optimized by only painting uppercase letters extra,
170  // and treat the rest WordWise, but I think it's not worth it.
171  // Somebody else may volunteer, and implement this ;-) (LS)
172 
173  // The mode determines whether the text is displayed character by
174  // character, word by word, or as a whole
175  enum { CharacterWise, WordWise, Whole }
176  mode = Whole;
177  if (!letterSpacing && !scFont && (wordSpacing || toAdd > 0)) {
178  mode = WordWise;
179  } else if (letterSpacing || scFont) {
180  mode = CharacterWise;
181  }
182 
183  const QFontMetrics &fm = cfi->fm;
184 
185  if (mode == Whole) { // most likely variant is treated extra
186 
187  if (to < 0) {
188  to = len;
189  }
190  const QString segStr(QString::fromRawData(str + pos + from, to - from));
191  const int preSegmentWidth = fm.width(QString::fromRawData(str + pos, len), from);
192  const int segmentWidth = fm.width(segStr);
193  const int eff_x = d == Qt::RightToLeft ? x - preSegmentWidth - segmentWidth
194  : x + preSegmentWidth;
195 
196  // draw whole string segment, with optional background
197  if (bg.isValid()) {
198  p->fillRect(eff_x, uy, segmentWidth, h, bg);
199  }
200  drawDirectedText(p, d, eff_x, y, segStr);
201  if (deco) {
202  drawDecoration(p, eff_x, uy, y - uy, segmentWidth - 1, h, deco);
203  }
204  return;
205  }
206 
207  const QString qstr = QString::fromRawData(str, slen);
208  QString upper = qstr;
209  QFontMetrics sc_fm = fm;
210  if (scFont) {
211  // draw in small caps
212  upper = qstr.toUpper();
213  sc_fm = QFontMetrics(*scFont);
214  }
215 
216  // We are using two passes. In the first pass, the widths are collected,
217  // and stored. In the second, the actual characters are drawn.
218 
219  // For each letter in the text box, save the width of the character.
220  // When word-wise, only the first letter contains the width, but of the
221  // whole word.
222  short *const widthList = (short *)alloca((to + 1) * sizeof(short));
223 
224  // First pass: gather widths
225  int preSegmentWidth = 0;
226  int segmentWidth = 0;
227  int lastWordBegin = 0;
228  bool onSegment = from == 0;
229  for (int i = 0; i < to; ++i) {
230  if (i == from) {
231  // Also close words on visibility boundary
232  if (mode == WordWise) {
233  const int width = closeWordAndGetWidth(fm, str, pos, lastWordBegin, i);
234 
235  if (lastWordBegin < i) {
236  widthList[lastWordBegin] = (short)width;
237  lastWordBegin = i;
238  preSegmentWidth += width;
239  }
240  }
241  onSegment = true;
242  }
243 
244  const QChar ch = str[pos + i];
245  bool lowercase = (ch.category() == QChar::Letter_Lowercase);
246  bool is_space = (ch.category() == QChar::Separator_Space);
247  int chw = 0;
248  if (letterSpacing) {
249  chw += letterSpacing;
250  }
251  if ((wordSpacing || toAdd) && is_space) {
252  if (mode == WordWise) {
253  const int width = closeWordAndGetWidth(fm, str, pos, lastWordBegin, i);
254  if (lastWordBegin < i) {
255  widthList[lastWordBegin] = (short)width;
256  lastWordBegin = i;
257  (onSegment ? segmentWidth : preSegmentWidth) += width;
258  }
259  ++lastWordBegin; // ignore this space
260  }
261  chw += wordSpacing;
262  if (numSpaces) {
263  const int a = toAdd / numSpaces;
264  chw += a;
265  toAdd -= a;
266  --numSpaces;
267  }
268  }
269  if (is_space || mode == CharacterWise) {
270  chw += lowercase ? sc_fm.charWidth(upper, pos + i) : fm.charWidth(qstr, pos + i);
271  widthList[i] = (short)chw;
272 
273  (onSegment ? segmentWidth : preSegmentWidth) += chw;
274  }
275 
276  }
277 
278  // close last word
279  Q_ASSERT(onSegment);
280  if (mode == WordWise) {
281  const int width = closeWordAndGetWidth(fm, str, pos, lastWordBegin, to);
282  segmentWidth += width;
283  widthList[lastWordBegin] = (short)width;
284  }
285 
286  if (d == Qt::RightToLeft) {
287  x -= preSegmentWidth;
288  } else {
289  x += preSegmentWidth;
290  }
291 
292  const int startx = d == Qt::RightToLeft ? x - segmentWidth : x;
293 
294  // optionally draw background
295  if (bg.isValid()) {
296  p->fillRect(startx, uy, segmentWidth, h, bg);
297  }
298 
299  // second pass: do the actual drawing
300  lastWordBegin = from;
301  for (int i = from; i < to; ++i) {
302  const QChar ch = str[pos + i];
303  bool lowercase = (ch.category() == QChar::Letter_Lowercase);
304  bool is_space = ch.category() == QChar::Separator_Space;
305  if (is_space) {
306  if (mode == WordWise) {
307  closeAndDrawWord(p, d, x, y, widthList, str, pos, lastWordBegin, i);
308  ++lastWordBegin; // jump over space
309  }
310  }
311  if (is_space || mode == CharacterWise) {
312  const int chw = widthList[i];
313  if (d == Qt::RightToLeft) {
314  x -= chw;
315  }
316 
317  if (scFont) {
318  p->setFont(lowercase ? *scFont : cfi->f);
319  }
320 
321  drawDirectedText(p, d, x, y, QString((lowercase ? upper : qstr)[pos + i]));
322 #ifdef __GNUC__
323 #warning "Light bloatery"
324 #endif
325 
326  if (d != Qt::RightToLeft) {
327  x += chw;
328  }
329  }
330 
331  }
332 
333  // don't forget to draw last word
334  if (mode == WordWise) {
335  closeAndDrawWord(p, d, x, y, widthList, str, pos, lastWordBegin, to);
336  }
337 
338  if (deco) {
339  drawDecoration(p, startx, uy, y - uy, segmentWidth - 1, h, deco);
340  }
341 
342  if (scFont) {
343  p->setFont(cfi->f);
344  }
345  }
346 }
347 
348 int Font::width(const QChar *chs, int, int pos, int len, bool fast, int start, int end, int toAdd) const
349 {
350  if (!len) {
351  return 0;
352  }
353  int w = 0;
354 
355  // #### Qt 4 has a major speed regression : QFontMetrics::width() is around 15 times slower than Qt 3's.
356  // This is a great speed bottleneck as we are now spending up to 70% of the layout time in that method
357  // (compared to around 5% before).
358  // It as been reported to TT and acknowledged as issue N138867, but whether they intend to give it some
359  // care in the near future is unclear :-/
360  //
361  // #### Qt 4.4 RC is now *40* times slower than Qt 3.3. This is a complete and utter disaster.
362  // New issue about this as N203591, because the report from 2006 was apparently discarded.
363  //
364  // This issue is now mostly addressed, by first scanning strings for complex/combining unicode characters,
365  // and using the much faster, non-context-aware QFontMetrics::width(QChar) when none has been found.
366  //
367  // ... Even that, however, is ultra-slow with Qt4.5, so now we cache this width information as well.
368  QFontMetrics &fm = cfi->fm;
369  if (scFont) {
370  const QString qstr = QString::fromRawData(chs + pos, len);
371  const QString upper = qstr.toUpper();
372  const QChar *uc = qstr.unicode();
373  const QFontMetrics sc_fm(*scFont);
374  if (fast) {
375  for (int i = 0; i < len; ++i) {
376  if ((uc + i)->category() == QChar::Letter_Lowercase) {
377  w += sc_fm.width(upper[i]);
378  } else {
379  w += cfi->cachedCharWidth(qstr[i]);
380  }
381  }
382  } else {
383  for (int i = 0; i < len; ++i) {
384  if ((uc + i)->category() == QChar::Letter_Lowercase) {
385  w += sc_fm.charWidth(upper, i);
386  } else {
387  w += fm.charWidth(qstr, i);
388  }
389  }
390  }
391  } else {
392  if (fast) {
393  for (int i = 0; i < len; ++i) {
394  w += cfi->cachedCharWidth(chs[i + pos]);
395  }
396  } else {
397  const QString qstr = QString::fromRawData(chs + pos, len);
398  w = fm.width(qstr);
399  }
400  }
401 
402  if (letterSpacing) {
403  w += len * letterSpacing;
404  }
405 
406  if (wordSpacing)
407  // add amount
408  for (int i = 0; i < len; ++i) {
409  if (chs[i + pos].category() == QChar::Separator_Space) {
410  w += wordSpacing;
411  }
412  }
413 
414  if (toAdd) {
415  // first gather count of spaces
416  int numSpaces = 0;
417  for (int i = start; i != end; ++i)
418  if (chs[i].category() == QChar::Separator_Space) {
419  ++numSpaces;
420  }
421  // distribute pixels evenly among spaces, but count only those within
422  // [pos, pos+len)
423  for (int i = start; numSpaces && i != pos + len; i++)
424  if (chs[i].category() == QChar::Separator_Space) {
425  const int a = toAdd / numSpaces;
426  if (i >= pos) {
427  w += a;
428  }
429  toAdd -= a;
430  --numSpaces;
431  }
432  }
433 
434  return w;
435 }
436 
437 int Font::charWidth(const QChar *chs, int slen, int pos, bool fast) const
438 {
439  int w;
440  if (scFont && chs[pos].category() == QChar::Letter_Lowercase) {
441  QString str(chs, slen);
442  str[pos] = chs[pos].toUpper();
443  if (fast) {
444  w = QFontMetrics(*scFont).width(str[pos]);
445  } else {
446  w = QFontMetrics(*scFont).charWidth(str, pos);
447  }
448  } else {
449  if (fast) {
450  w = cfi->cachedCharWidth(chs[pos]);
451  } else {
452  w = cfi->fm.charWidth(QString::fromRawData(chs, slen), pos);
453  }
454  }
455  if (letterSpacing) {
456  w += letterSpacing;
457  }
458 
459  if (wordSpacing && (chs + pos)->category() == QChar::Separator_Space) {
460  w += wordSpacing;
461  }
462  return w;
463 }
464 
465 /**
466  We cache information on fonts, including what sizes they are scalable too,
467  and their metrics since getting that info out of Qt is slow
468 */
469 
470 struct CachedFontFamilyKey { // basically, FontDef minus the size (and for now SC)
471  QString family;
472  int weight;
473  bool italic;
474 
475  CachedFontFamilyKey() {}
476 
477  // resolvedFamily is not actually what it claims to be, as it comes from css style.
478  // e.g. it could be something like "foo,Open Sans,Impact"
479  CachedFontFamilyKey(const QString &resolvedFamily, int weight, bool italic) :
480  family(resolvedFamily), weight(weight), italic(italic)
481  {}
482 
483  bool operator == (const CachedFontFamilyKey &other) const
484  {
485  return (family == other.family) &&
486  (weight == other.weight) &&
487  (italic == other.italic);
488  }
489 };
490 
491 static inline uint qHash(const CachedFontFamilyKey &key)
492 {
493  return ::qHash(key.family) ^ ::qHash(key.weight) ^ ::qHash(key.italic);
494 }
495 
496 class CachedFontFamily
497 {
498 public:
499  CachedFontFamilyKey def;
500 
501  bool scaleable;
502  QList<int> sizes; // if bitmap.
503 
505  // Maps from pixel size.. This is a weak-reference --- the refcount
506  // on the CFI itself determines the lifetime
507 
508  CachedFontInstance *queryFont(int pixelSize);
509  void invalidateAllInstances();
510  void markAllInstancesAsValid();
511 };
512 
514 // This is a hash and not a cache since the top-level info is tiny,
515 // and the actual font instances have precise lifetime
516 
517 void Font::invalidateCachedFontFamily(const QString &familyName) // static
518 {
519  if (!fontCache) {
520  return;
521  }
524  for (i = fontCache->constBegin(); i != end; ++i) {
525  if (i.key().family.contains(familyName, Qt::CaseInsensitive)) {
526  i.value()->invalidateAllInstances();
527  }
528  }
529 }
530 
531 void Font::markAllCachedFontsAsValid() // static
532 {
533  if (!fontCache) {
534  return;
535  }
538  for (i = fontCache->constBegin(); i != end; ++i) {
539  i.value()->markAllInstancesAsValid();
540  }
541 }
542 
543 CachedFontFamily *Font::queryFamily(const QString &name, int weight, bool italic)
544 {
545  if (!fontCache) {
547  }
548 
549  CachedFontFamilyKey key(name, weight, italic);
550 
551  CachedFontFamily *f = fontCache->value(key);
552  if (!f) {
553  // To query the sizes, we seem to have to make a font with right style to produce the stylestring
554  QFont font(name);
555  font.setItalic(italic);
556  font.setWeight(weight);
557  // Use resolved family name to query font database
558  const QFontInfo fontInfo(font);
559  QFontDatabase db;
560  const QString resolvedFamily = fontInfo.family();
561  const QString styleString = db.styleString(fontInfo);
562  f = new CachedFontFamily;
563  f->def = key;
564  f->scaleable = db.isSmoothlyScalable(resolvedFamily, styleString);
565 
566  if (!f->scaleable) {
567  /* Cache size info */
568  f->sizes = db.smoothSizes(resolvedFamily, styleString);
569  }
570 
571  fontCache->insert(key, f);
572  }
573 
574  return f;
575 }
576 
577 CachedFontInstance *CachedFontFamily::queryFont(int pixelSize)
578 {
579  CachedFontInstance *cfi = instances.value(pixelSize);
580  if (!cfi) {
581  cfi = new CachedFontInstance(this, pixelSize);
582  instances.insert(pixelSize, cfi);
583  }
584  return cfi;
585 }
586 
587 void CachedFontFamily::invalidateAllInstances()
588 {
591  for (i = instances.constBegin(); i != end; ++i) {
592  i.value()->invalidate();
593  }
594 }
595 
596 void CachedFontFamily::markAllInstancesAsValid()
597 {
600  for (i = instances.constBegin(); i != end; ++i) {
601  i.value()->invalidated = false;
602  }
603 }
604 
605 CachedFontInstance::CachedFontInstance(CachedFontFamily *p, int sz):
606  f(p->def.family), fm(f), invalidated(false), parent(p), size(sz)
607 {
608  f.setItalic(p->def.italic);
609  f.setWeight(p->def.weight);
610  f.setPixelSize(sz);
611  fm = QFontMetrics(f);
612 
613  // Prepare metrics caches
614  for (int c = 0; c < 256; ++c) {
615  rows[c] = nullptr;
616  }
617 
618  ascent = fm.ascent();
619  descent = fm.descent();
620  height = fm.height();
621  lineSpacing = fm.lineSpacing();
622  xHeight = fm.xHeight();
623 
624  const QChar zeroChar((ushort)48);
625  if (!fm.inFont(zeroChar)) {
626  m_zeroCharWidth = -1;
627  } else {
628  m_zeroCharWidth = (int)cachedCharWidth(zeroChar);
629  }
630 }
631 
632 void CachedFontInstance::invalidate()
633 {
634  QFont nf(f.family());
635  nf.setWeight(f.weight());
636  nf.setItalic(f.italic());
637  nf.setPixelSize(f.pixelSize());
638  f = nf;
639  invalidated = true;
640  fm = QFontMetrics(f);
641 
642  // Cleanup metrics caches
643  for (int c = 0; c < 256; ++c) {
644  delete rows[c];
645  rows[c] = nullptr;
646  }
647 
648  ascent = fm.ascent();
649  descent = fm.descent();
650  height = fm.height();
651  lineSpacing = fm.lineSpacing();
652  xHeight = fm.xHeight();
653 
654  const QChar zeroChar((ushort)48);
655  if (!fm.inFont(zeroChar)) {
656  m_zeroCharWidth = -1;
657  } else {
658  m_zeroCharWidth = (int)cachedCharWidth(zeroChar);
659  }
660 }
661 
662 CachedFontInstance::~CachedFontInstance()
663 {
664  for (int c = 0; c < 256; ++c) {
665  delete rows[c];
666  }
667  parent->instances.remove(size);
668 }
669 
670 unsigned CachedFontInstance::calcAndCacheWidth(unsigned short codePoint)
671 {
672  unsigned rowNum = codePoint >> 8;
673  RowInfo *row = rows[rowNum];
674  if (!row) {
675  row = rows[rowNum] = new RowInfo();
676  }
677 
678  unsigned width = fm.width(QChar(codePoint));
679  return (row->widths[codePoint & 0xFF] = qMin(width, 0xFFu));
680 }
681 
682 void Font::update(int logicalDpiY) const
683 {
684  CachedFontFamily *family = queryFamily(fontDef.family, fontDef.weight, fontDef.italic);
685 
686  int size = fontDef.size;
687 
688  // ok, now some magic to get a nice unscaled font
689  // all other font properties should be set before this one!!!!
690  if (!family->scaleable) {
691  const QList<int> pointSizes = family->sizes;
692  // lets see if we find a nice looking font, which is not too far away
693  // from the requested one.
694  // qCDebug(KHTML_LOG) << "khtml::setFontSize family = " << f.family() << " size requested=" << size;
695  const float toPix = qMax(logicalDpiY, 96) / 72.0f;
696 
697  float diff = 1; // ### 100% deviation
698  float bestSize = 0;
699 
700  QList<int>::ConstIterator it = pointSizes.begin();
701  const QList<int>::ConstIterator itEnd = pointSizes.end();
702 
703  for (; it != itEnd; ++it) {
704  float newDiff = (((*it) * toPix) - float(size)) / float(size);
705  //qCDebug(KHTML_LOG) << "smooth font size: " << *it << " diff=" << newDiff;
706  if (newDiff < 0) {
707  newDiff = -newDiff;
708  }
709  if (newDiff < diff) {
710  diff = newDiff;
711  bestSize = *it;
712  }
713  }
714  //qCDebug(KHTML_LOG) << "best smooth font size: " << bestSize << " diff=" << diff;
715  if (bestSize != 0 && diff < 0.2) { // 20% deviation, otherwise we use a scaled font...
716  size = static_cast<int>(bestSize * toPix);
717  }
718  }
719 
720  // make sure we don't bust up X11
721  // Also, Qt does not support sizing a QFont to zero.
722  size = qMax(1, qMin(255, size));
723 
724 // qDebug("setting font to %s, italic=%d, weight=%d, size=%d", fontDef.family.toLatin1().constData(), fontDef.italic,
725 // fontDef.weight, size );
726 
727  // Now request the font from the family
728  cfi = family->queryFont(size);
729 
730  // small caps
731  delete scFont;
732  scFont = nullptr;
733 
734  if (fontDef.smallCaps) {
735  scFont = new QFont(cfi->f);
736  scFont->setPixelSize(qMax(1, cfi->f.pixelSize() * 7 / 10));
737  }
738 }
739 
740 CachedFontInstance *Font::defaultCFI;
741 
742 void Font::initDefault()
743 {
744  if (defaultCFI) {
745  return;
746  }
747 
748  // Create one for default family. It doesn't matter what size and DPI we use
749  // since this is only used for throwaway computations
750  // ### this may cache an instance we don't need though; but font family
751  // is extremely likely to be used
752  Font f;
753  f.fontDef.size = 16;
754  f.update(96);
755 
756  defaultCFI = f.cfi.get();
757  defaultCFI->ref();
758 }
759 
760 void Font::drawDecoration(QPainter *pt, int _tx, int _ty, int baseline, int width, int height, int deco) const
761 {
762  Q_UNUSED(height);
763 
764  // thick lines on small fonts look ugly
765  const int thickness = cfi->height > 20 ? cfi->fm.lineWidth() : 1;
766  const QBrush brush = pt->pen().color();
767  if (deco & UNDERLINE) {
768  int underlineOffset = (cfi->height + baseline) / 2;
769  if (underlineOffset <= baseline) {
770  underlineOffset = baseline + 1;
771  }
772 
773  pt->fillRect(_tx, _ty + underlineOffset, width + 1, thickness, brush);
774  }
775  if (deco & OVERLINE) {
776  pt->fillRect(_tx, _ty, width + 1, thickness, brush);
777  }
778  if (deco & LINE_THROUGH) {
779  pt->fillRect(_tx, _ty + 2 * baseline / 3, width + 1, thickness, brush);
780  }
781 }
782 
783 // WebCore SVG
784 float Font::floatWidth(QChar *str, int pos, int len, int /*extraCharsAvailable*/, int &charsConsumed, DOM::DOMString &glyphName) const
785 {
786  charsConsumed = len;
787  glyphName = "";
788  // ### see if svg can scan the string (cf. render_text.cpp - isSimpleChar()) to determine if fast algo can be used.
789  return width(str, 0, pos, len, false /*fast algorithm*/);
790 }
791 
792 float Font::floatWidth(QChar *str, int pos, int len) const
793 {
794  // For now, this is slow but correct...
795  // or rather it /would/ be correct if QFontMetricsF had charWidth();
796  // so instead the approximate width is used
797  QFontMetricsF fm(cfi->f);
798  return float(fm.width(QString::fromRawData(str + pos, len)));
799 }
800 
801 }
802 
QString & append(QChar ch)
QHash::iterator insert(const Key &key, const T &value)
QString toUpper() const const
void fillRect(const QRectF &rectangle, const QBrush &brush)
Separator_Space
QString & prepend(QChar ch)
This file is part of the HTML rendering engine for KDE.
bool isSmoothlyScalable(const QString &family, const QString &style) const const
void setLayoutDirection(Qt::LayoutDirection direction)
void setWeight(int weight)
QChar::Category category() const const
LayoutDirection
Qt::LayoutDirection layoutDirection() const const
qreal width(const QString &text) const const
QColor color() const const
void setFont(const QFont &font)
QString fromRawData(const QChar *unicode, int size)
QHash::const_iterator constEnd() const const
CaseInsensitive
void drawText(const QPointF &position, const QString &text)
bool endsWith(const QString &s, Qt::CaseSensitivity cs) const const
This class implements the basic string we use in the DOM.
Definition: dom_string.h:44
QList< int > smoothSizes(const QString &family, const QString &styleName)
int charWidth(const QString &text, int pos) const const
QString styleString(const QFont &font)
QList::iterator end()
const T value(const Key &key) const const
QString family() const const
KCALENDARCORE_EXPORT uint qHash(const KCalendarCore::Period &key)
void setItalic(bool enable)
const QChar * unicode() const const
QHash::const_iterator constBegin() const const
QChar toUpper() const const
static int closeWordAndGetWidth(const QFontMetrics &fm, const QChar *str, int pos, int wordStart, int wordEnd)
closes the current word and returns its width in pixels
Definition: font.cpp:61
int width(const QString &text, int len) const const
const QPen & pen() const const
QList::iterator begin()
static void closeAndDrawWord(QPainter *p, Qt::LayoutDirection d, int &x, int y, const short widths[], const QChar *str, int pos, int &wordStart, int wordEnd)
closes the current word and draws it
Definition: font.cpp:110
bool isValid() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Tue Oct 26 2021 22:48:01 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.