File indexing completed on 2024-11-10 09:38:52
0001 /** 0002 * This file is part of the html renderer for KDE. 0003 * 0004 * Copyright (C) 1999-2003 Lars Knoll (knoll@kde.org) 0005 * (C) 1999 Antti Koivisto (koivisto@kde.org) 0006 * (C) 2000 Dirk Mueller (mueller@kde.org) 0007 * 0008 * This library is free software; you can redistribute it and/or 0009 * modify it under the terms of the GNU Library General Public 0010 * License as published by the Free Software Foundation; either 0011 * version 2 of the License, or (at your option) any later version. 0012 * 0013 * This library is distributed in the hope that it will be useful, 0014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0016 * Library General Public License for more details. 0017 * 0018 * You should have received a copy of the GNU Library General Public License 0019 * along with this library; see the file COPYING.LIB. If not, write to 0020 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0021 * Boston, MA 02110-1301, USA. 0022 * 0023 */ 0024 0025 #include "font.h" 0026 0027 #include <config-khtml.h> 0028 0029 #if HAVE_ALLOCA_H 0030 # include <alloca.h> 0031 # else 0032 # if HAVE_MALLOC_H 0033 # include <malloc.h> 0034 # else 0035 # include <stdlib.h> 0036 # endif 0037 #endif 0038 0039 #include "khtml_debug.h" 0040 0041 #include <QHash> 0042 #include <QFontDatabase> 0043 #include <QtGlobal> 0044 0045 // for SVG 0046 #include "dom/dom_string.h" 0047 0048 namespace khtml 0049 { 0050 0051 /** closes the current word and returns its width in pixels 0052 * @param fm metrics of font to be used 0053 * @param str string 0054 * @param pos zero-indexed position within @p str upon which all other 0055 * indices are based 0056 * @param wordStart relative index pointing to the position where the word started 0057 * @param wordEnd relative index pointing one position after the word ended 0058 * @return the width in pixels. May be 0 if @p wordStart and @p wordEnd were 0059 * equal. 0060 */ 0061 static inline int closeWordAndGetWidth(const QFontMetrics &fm, const QChar *str, int pos, 0062 int wordStart, int wordEnd) 0063 { 0064 if (wordEnd <= wordStart) { 0065 return 0; 0066 } 0067 0068 return fm.width(QString::fromRawData(str + pos + wordStart, wordEnd - wordStart)); 0069 } 0070 0071 static inline void drawDirectedText(QPainter *p, Qt::LayoutDirection d, 0072 int x, int y, const QString &str) 0073 { 0074 QString qs = str; 0075 // Qt doesn't have a function to force a direction, 0076 // so we have to use a the unicode "RTO" character to 0077 // (no, setLayoutDirection isn't enough) 0078 if (d == Qt::RightToLeft && str[0].direction() == QChar::DirL) { 0079 qs.prepend(QChar(0x202E)); // RIGHT-TO-LEFT OVERRIDE 0080 } else if (d == Qt::LeftToRight && str[0].direction() == QChar::DirR) { 0081 qs.prepend(QChar(0x202D)); // LEFT-TO-RIGHT OVERRIDE 0082 } 0083 0084 Qt::LayoutDirection saveDir = p->layoutDirection(); 0085 p->setLayoutDirection(d); 0086 // Qt 4 avoids rendering soft-hyphens by default. 0087 // Append normal hyphen instead. 0088 if (qs.endsWith(QChar(0xad))) { 0089 qs.append(QChar('-')); 0090 } 0091 p->drawText(x, y, qs); 0092 p->setLayoutDirection(saveDir); 0093 } 0094 0095 /** closes the current word and draws it 0096 * @param p painter 0097 * @param d text direction 0098 * @param x current x position, will be inc-/decremented correctly according 0099 * to text direction 0100 * @param y baseline of text 0101 * @param widths list of widths; width of word is expected at position 0102 * wordStart 0103 * @param str string 0104 * @param pos zero-indexed position within @p str upon which all other 0105 * indices are based 0106 * @param wordStart relative index pointing to the position where the word started, 0107 * will be set to wordEnd after function 0108 * @param wordEnd relative index pointing one position after the word ended 0109 */ 0110 static inline void closeAndDrawWord(QPainter *p, Qt::LayoutDirection d, 0111 int &x, int y, const short widths[], const QChar *str, int pos, 0112 int &wordStart, int wordEnd) 0113 { 0114 if (wordEnd <= wordStart) { 0115 return; 0116 } 0117 0118 int width = widths[wordStart]; 0119 if (d == Qt::RightToLeft) { 0120 x -= width; 0121 } 0122 0123 drawDirectedText(p, d, x, y, QString::fromRawData(str + pos + wordStart, wordEnd - wordStart)); 0124 0125 if (d != Qt::RightToLeft) { 0126 x += width; 0127 } 0128 0129 wordStart = wordEnd; 0130 } 0131 0132 void Font::drawText(QPainter *p, int x, int y, const QChar *str, const int slen, int pos, int len, 0133 int toAdd, Qt::LayoutDirection d, int from, int to, QColor bg, int uy, int h, int deco) const 0134 { 0135 if (!str || slen == 0) { // #188910 0136 return; 0137 } 0138 0139 // ### fixme for RTL 0140 if (!scFont && !letterSpacing && !wordSpacing && !toAdd && from == -1) { 0141 // simply draw it 0142 // Due to some unfounded cause QPainter::drawText traverses the 0143 // *whole* string when painting, not only the specified 0144 // [pos, pos + len) segment. This makes painting *extremely* slow for 0145 // long render texts (in the order of several megabytes). 0146 // Hence, only hand over the piece of text of the actual inline text box 0147 drawDirectedText(p, d, x, y, QString::fromRawData(str + pos, len)); 0148 } else { 0149 if (from < 0) { 0150 from = 0; 0151 } 0152 if (to < 0) { 0153 to = len; 0154 } 0155 0156 int numSpaces = 0; 0157 if (toAdd) { 0158 for (int i = 0; i < len; ++i) 0159 if (str[i + pos].category() == QChar::Separator_Space) { 0160 ++numSpaces; 0161 } 0162 } 0163 0164 if (d == Qt::RightToLeft) { 0165 const int totWidth = width(str, slen, pos, len, false /*fast algo*/); 0166 x += totWidth + toAdd; 0167 } 0168 0169 // ### sc could be optimized by only painting uppercase letters extra, 0170 // and treat the rest WordWise, but I think it's not worth it. 0171 // Somebody else may volunteer, and implement this ;-) (LS) 0172 0173 // The mode determines whether the text is displayed character by 0174 // character, word by word, or as a whole 0175 enum { CharacterWise, WordWise, Whole } 0176 mode = Whole; 0177 if (!letterSpacing && !scFont && (wordSpacing || toAdd > 0)) { 0178 mode = WordWise; 0179 } else if (letterSpacing || scFont) { 0180 mode = CharacterWise; 0181 } 0182 0183 const QFontMetrics &fm = cfi->fm; 0184 0185 if (mode == Whole) { // most likely variant is treated extra 0186 0187 if (to < 0) { 0188 to = len; 0189 } 0190 const QString segStr(QString::fromRawData(str + pos + from, to - from)); 0191 const int preSegmentWidth = fm.width(QString::fromRawData(str + pos, len), from); 0192 const int segmentWidth = fm.width(segStr); 0193 const int eff_x = d == Qt::RightToLeft ? x - preSegmentWidth - segmentWidth 0194 : x + preSegmentWidth; 0195 0196 // draw whole string segment, with optional background 0197 if (bg.isValid()) { 0198 p->fillRect(eff_x, uy, segmentWidth, h, bg); 0199 } 0200 drawDirectedText(p, d, eff_x, y, segStr); 0201 if (deco) { 0202 drawDecoration(p, eff_x, uy, y - uy, segmentWidth - 1, h, deco); 0203 } 0204 return; 0205 } 0206 0207 const QString qstr = QString::fromRawData(str, slen); 0208 QString upper = qstr; 0209 QFontMetrics sc_fm = fm; 0210 if (scFont) { 0211 // draw in small caps 0212 upper = qstr.toUpper(); 0213 sc_fm = QFontMetrics(*scFont); 0214 } 0215 0216 // We are using two passes. In the first pass, the widths are collected, 0217 // and stored. In the second, the actual characters are drawn. 0218 0219 // For each letter in the text box, save the width of the character. 0220 // When word-wise, only the first letter contains the width, but of the 0221 // whole word. 0222 short *const widthList = (short *)alloca((to + 1) * sizeof(short)); 0223 0224 // First pass: gather widths 0225 int preSegmentWidth = 0; 0226 int segmentWidth = 0; 0227 int lastWordBegin = 0; 0228 bool onSegment = from == 0; 0229 for (int i = 0; i < to; ++i) { 0230 if (i == from) { 0231 // Also close words on visibility boundary 0232 if (mode == WordWise) { 0233 const int width = closeWordAndGetWidth(fm, str, pos, lastWordBegin, i); 0234 0235 if (lastWordBegin < i) { 0236 widthList[lastWordBegin] = (short)width; 0237 lastWordBegin = i; 0238 preSegmentWidth += width; 0239 } 0240 } 0241 onSegment = true; 0242 } 0243 0244 const QChar ch = str[pos + i]; 0245 bool lowercase = (ch.category() == QChar::Letter_Lowercase); 0246 bool is_space = (ch.category() == QChar::Separator_Space); 0247 int chw = 0; 0248 if (letterSpacing) { 0249 chw += letterSpacing; 0250 } 0251 if ((wordSpacing || toAdd) && is_space) { 0252 if (mode == WordWise) { 0253 const int width = closeWordAndGetWidth(fm, str, pos, lastWordBegin, i); 0254 if (lastWordBegin < i) { 0255 widthList[lastWordBegin] = (short)width; 0256 lastWordBegin = i; 0257 (onSegment ? segmentWidth : preSegmentWidth) += width; 0258 } 0259 ++lastWordBegin; // ignore this space 0260 } 0261 chw += wordSpacing; 0262 if (numSpaces) { 0263 const int a = toAdd / numSpaces; 0264 chw += a; 0265 toAdd -= a; 0266 --numSpaces; 0267 } 0268 } 0269 if (is_space || mode == CharacterWise) { 0270 chw += lowercase ? sc_fm.charWidth(upper, pos + i) : fm.charWidth(qstr, pos + i); 0271 widthList[i] = (short)chw; 0272 0273 (onSegment ? segmentWidth : preSegmentWidth) += chw; 0274 } 0275 0276 } 0277 0278 // close last word 0279 Q_ASSERT(onSegment); 0280 if (mode == WordWise) { 0281 const int width = closeWordAndGetWidth(fm, str, pos, lastWordBegin, to); 0282 segmentWidth += width; 0283 widthList[lastWordBegin] = (short)width; 0284 } 0285 0286 if (d == Qt::RightToLeft) { 0287 x -= preSegmentWidth; 0288 } else { 0289 x += preSegmentWidth; 0290 } 0291 0292 const int startx = d == Qt::RightToLeft ? x - segmentWidth : x; 0293 0294 // optionally draw background 0295 if (bg.isValid()) { 0296 p->fillRect(startx, uy, segmentWidth, h, bg); 0297 } 0298 0299 // second pass: do the actual drawing 0300 lastWordBegin = from; 0301 for (int i = from; i < to; ++i) { 0302 const QChar ch = str[pos + i]; 0303 bool lowercase = (ch.category() == QChar::Letter_Lowercase); 0304 bool is_space = ch.category() == QChar::Separator_Space; 0305 if (is_space) { 0306 if (mode == WordWise) { 0307 closeAndDrawWord(p, d, x, y, widthList, str, pos, lastWordBegin, i); 0308 ++lastWordBegin; // jump over space 0309 } 0310 } 0311 if (is_space || mode == CharacterWise) { 0312 const int chw = widthList[i]; 0313 if (d == Qt::RightToLeft) { 0314 x -= chw; 0315 } 0316 0317 if (scFont) { 0318 p->setFont(lowercase ? *scFont : cfi->f); 0319 } 0320 0321 drawDirectedText(p, d, x, y, QString((lowercase ? upper : qstr)[pos + i])); 0322 #ifdef __GNUC__ 0323 #warning "Light bloatery" 0324 #endif 0325 0326 if (d != Qt::RightToLeft) { 0327 x += chw; 0328 } 0329 } 0330 0331 } 0332 0333 // don't forget to draw last word 0334 if (mode == WordWise) { 0335 closeAndDrawWord(p, d, x, y, widthList, str, pos, lastWordBegin, to); 0336 } 0337 0338 if (deco) { 0339 drawDecoration(p, startx, uy, y - uy, segmentWidth - 1, h, deco); 0340 } 0341 0342 if (scFont) { 0343 p->setFont(cfi->f); 0344 } 0345 } 0346 } 0347 0348 int Font::width(const QChar *chs, int, int pos, int len, bool fast, int start, int end, int toAdd) const 0349 { 0350 if (!len) { 0351 return 0; 0352 } 0353 int w = 0; 0354 0355 // #### Qt 4 has a major speed regression : QFontMetrics::width() is around 15 times slower than Qt 3's. 0356 // This is a great speed bottleneck as we are now spending up to 70% of the layout time in that method 0357 // (compared to around 5% before). 0358 // It as been reported to TT and acknowledged as issue N138867, but whether they intend to give it some 0359 // care in the near future is unclear :-/ 0360 // 0361 // #### Qt 4.4 RC is now *40* times slower than Qt 3.3. This is a complete and utter disaster. 0362 // New issue about this as N203591, because the report from 2006 was apparently discarded. 0363 // 0364 // This issue is now mostly addressed, by first scanning strings for complex/combining unicode characters, 0365 // and using the much faster, non-context-aware QFontMetrics::width(QChar) when none has been found. 0366 // 0367 // ... Even that, however, is ultra-slow with Qt4.5, so now we cache this width information as well. 0368 QFontMetrics &fm = cfi->fm; 0369 if (scFont) { 0370 const QString qstr = QString::fromRawData(chs + pos, len); 0371 const QString upper = qstr.toUpper(); 0372 const QChar *uc = qstr.unicode(); 0373 const QFontMetrics sc_fm(*scFont); 0374 if (fast) { 0375 for (int i = 0; i < len; ++i) { 0376 if ((uc + i)->category() == QChar::Letter_Lowercase) { 0377 w += sc_fm.width(upper[i]); 0378 } else { 0379 w += cfi->cachedCharWidth(qstr[i]); 0380 } 0381 } 0382 } else { 0383 for (int i = 0; i < len; ++i) { 0384 if ((uc + i)->category() == QChar::Letter_Lowercase) { 0385 w += sc_fm.charWidth(upper, i); 0386 } else { 0387 w += fm.charWidth(qstr, i); 0388 } 0389 } 0390 } 0391 } else { 0392 if (fast) { 0393 for (int i = 0; i < len; ++i) { 0394 w += cfi->cachedCharWidth(chs[i + pos]); 0395 } 0396 } else { 0397 const QString qstr = QString::fromRawData(chs + pos, len); 0398 w = fm.width(qstr); 0399 } 0400 } 0401 0402 if (letterSpacing) { 0403 w += len * letterSpacing; 0404 } 0405 0406 if (wordSpacing) 0407 // add amount 0408 for (int i = 0; i < len; ++i) { 0409 if (chs[i + pos].category() == QChar::Separator_Space) { 0410 w += wordSpacing; 0411 } 0412 } 0413 0414 if (toAdd) { 0415 // first gather count of spaces 0416 int numSpaces = 0; 0417 for (int i = start; i != end; ++i) 0418 if (chs[i].category() == QChar::Separator_Space) { 0419 ++numSpaces; 0420 } 0421 // distribute pixels evenly among spaces, but count only those within 0422 // [pos, pos+len) 0423 for (int i = start; numSpaces && i != pos + len; i++) 0424 if (chs[i].category() == QChar::Separator_Space) { 0425 const int a = toAdd / numSpaces; 0426 if (i >= pos) { 0427 w += a; 0428 } 0429 toAdd -= a; 0430 --numSpaces; 0431 } 0432 } 0433 0434 return w; 0435 } 0436 0437 int Font::charWidth(const QChar *chs, int slen, int pos, bool fast) const 0438 { 0439 int w; 0440 if (scFont && chs[pos].category() == QChar::Letter_Lowercase) { 0441 QString str(chs, slen); 0442 str[pos] = chs[pos].toUpper(); 0443 if (fast) { 0444 w = QFontMetrics(*scFont).width(str[pos]); 0445 } else { 0446 w = QFontMetrics(*scFont).charWidth(str, pos); 0447 } 0448 } else { 0449 if (fast) { 0450 w = cfi->cachedCharWidth(chs[pos]); 0451 } else { 0452 w = cfi->fm.charWidth(QString::fromRawData(chs, slen), pos); 0453 } 0454 } 0455 if (letterSpacing) { 0456 w += letterSpacing; 0457 } 0458 0459 if (wordSpacing && (chs + pos)->category() == QChar::Separator_Space) { 0460 w += wordSpacing; 0461 } 0462 return w; 0463 } 0464 0465 /** 0466 We cache information on fonts, including what sizes they are scalable too, 0467 and their metrics since getting that info out of Qt is slow 0468 */ 0469 0470 struct CachedFontFamilyKey { // basically, FontDef minus the size (and for now SC) 0471 QString family; 0472 int weight; 0473 bool italic; 0474 0475 CachedFontFamilyKey() {} 0476 0477 // resolvedFamily is not actually what it claims to be, as it comes from css style. 0478 // e.g. it could be something like "foo,Open Sans,Impact" 0479 CachedFontFamilyKey(const QString &resolvedFamily, int weight, bool italic) : 0480 family(resolvedFamily), weight(weight), italic(italic) 0481 {} 0482 0483 bool operator == (const CachedFontFamilyKey &other) const 0484 { 0485 return (family == other.family) && 0486 (weight == other.weight) && 0487 (italic == other.italic); 0488 } 0489 }; 0490 0491 static inline uint qHash(const CachedFontFamilyKey &key) 0492 { 0493 return ::qHash(key.family) ^ ::qHash(key.weight) ^ ::qHash(key.italic); 0494 } 0495 0496 class CachedFontFamily 0497 { 0498 public: 0499 CachedFontFamilyKey def; 0500 0501 bool scaleable; 0502 QList<int> sizes; // if bitmap. 0503 0504 QHash<int, CachedFontInstance *> instances; 0505 // Maps from pixel size.. This is a weak-reference --- the refcount 0506 // on the CFI itself determines the lifetime 0507 0508 CachedFontInstance *queryFont(int pixelSize); 0509 void invalidateAllInstances(); 0510 void markAllInstancesAsValid(); 0511 }; 0512 0513 static QHash<CachedFontFamilyKey, CachedFontFamily *> *fontCache; 0514 // This is a hash and not a cache since the top-level info is tiny, 0515 // and the actual font instances have precise lifetime 0516 0517 void Font::invalidateCachedFontFamily(const QString &familyName) // static 0518 { 0519 if (!fontCache) { 0520 return; 0521 } 0522 QHash<CachedFontFamilyKey, CachedFontFamily *>::const_iterator i; 0523 QHash<CachedFontFamilyKey, CachedFontFamily *>::const_iterator end = fontCache->constEnd(); 0524 for (i = fontCache->constBegin(); i != end; ++i) { 0525 if (i.key().family.contains(familyName, Qt::CaseInsensitive)) { 0526 i.value()->invalidateAllInstances(); 0527 } 0528 } 0529 } 0530 0531 void Font::markAllCachedFontsAsValid() // static 0532 { 0533 if (!fontCache) { 0534 return; 0535 } 0536 QHash<CachedFontFamilyKey, CachedFontFamily *>::const_iterator i; 0537 QHash<CachedFontFamilyKey, CachedFontFamily *>::const_iterator end = fontCache->constEnd(); 0538 for (i = fontCache->constBegin(); i != end; ++i) { 0539 i.value()->markAllInstancesAsValid(); 0540 } 0541 } 0542 0543 CachedFontFamily *Font::queryFamily(const QString &name, int weight, bool italic) 0544 { 0545 if (!fontCache) { 0546 fontCache = new QHash<CachedFontFamilyKey, CachedFontFamily *>; 0547 } 0548 0549 CachedFontFamilyKey key(name, weight, italic); 0550 0551 CachedFontFamily *f = fontCache->value(key); 0552 if (!f) { 0553 // To query the sizes, we seem to have to make a font with right style to produce the stylestring 0554 QFont font(name); 0555 font.setItalic(italic); 0556 font.setWeight(weight); 0557 // Use resolved family name to query font database 0558 const QFontInfo fontInfo(font); 0559 QFontDatabase db; 0560 const QString resolvedFamily = fontInfo.family(); 0561 const QString styleString = db.styleString(fontInfo); 0562 f = new CachedFontFamily; 0563 f->def = key; 0564 f->scaleable = db.isSmoothlyScalable(resolvedFamily, styleString); 0565 0566 if (!f->scaleable) { 0567 /* Cache size info */ 0568 f->sizes = db.smoothSizes(resolvedFamily, styleString); 0569 } 0570 0571 fontCache->insert(key, f); 0572 } 0573 0574 return f; 0575 } 0576 0577 CachedFontInstance *CachedFontFamily::queryFont(int pixelSize) 0578 { 0579 CachedFontInstance *cfi = instances.value(pixelSize); 0580 if (!cfi) { 0581 cfi = new CachedFontInstance(this, pixelSize); 0582 instances.insert(pixelSize, cfi); 0583 } 0584 return cfi; 0585 } 0586 0587 void CachedFontFamily::invalidateAllInstances() 0588 { 0589 QHash<int, CachedFontInstance *>::const_iterator i; 0590 QHash<int, CachedFontInstance *>::const_iterator end = instances.constEnd(); 0591 for (i = instances.constBegin(); i != end; ++i) { 0592 i.value()->invalidate(); 0593 } 0594 } 0595 0596 void CachedFontFamily::markAllInstancesAsValid() 0597 { 0598 QHash<int, CachedFontInstance *>::const_iterator i; 0599 QHash<int, CachedFontInstance *>::const_iterator end = instances.constEnd(); 0600 for (i = instances.constBegin(); i != end; ++i) { 0601 i.value()->invalidated = false; 0602 } 0603 } 0604 0605 CachedFontInstance::CachedFontInstance(CachedFontFamily *p, int sz): 0606 f(p->def.family), fm(f), invalidated(false), parent(p), size(sz) 0607 { 0608 f.setItalic(p->def.italic); 0609 f.setWeight(p->def.weight); 0610 f.setPixelSize(sz); 0611 fm = QFontMetrics(f); 0612 0613 // Prepare metrics caches 0614 for (int c = 0; c < 256; ++c) { 0615 rows[c] = nullptr; 0616 } 0617 0618 ascent = fm.ascent(); 0619 descent = fm.descent(); 0620 height = fm.height(); 0621 lineSpacing = fm.lineSpacing(); 0622 xHeight = fm.xHeight(); 0623 0624 const QChar zeroChar((ushort)48); 0625 if (!fm.inFont(zeroChar)) { 0626 m_zeroCharWidth = -1; 0627 } else { 0628 m_zeroCharWidth = (int)cachedCharWidth(zeroChar); 0629 } 0630 } 0631 0632 void CachedFontInstance::invalidate() 0633 { 0634 QFont nf(f.family()); 0635 nf.setWeight(f.weight()); 0636 nf.setItalic(f.italic()); 0637 nf.setPixelSize(f.pixelSize()); 0638 f = nf; 0639 invalidated = true; 0640 fm = QFontMetrics(f); 0641 0642 // Cleanup metrics caches 0643 for (int c = 0; c < 256; ++c) { 0644 delete rows[c]; 0645 rows[c] = nullptr; 0646 } 0647 0648 ascent = fm.ascent(); 0649 descent = fm.descent(); 0650 height = fm.height(); 0651 lineSpacing = fm.lineSpacing(); 0652 xHeight = fm.xHeight(); 0653 0654 const QChar zeroChar((ushort)48); 0655 if (!fm.inFont(zeroChar)) { 0656 m_zeroCharWidth = -1; 0657 } else { 0658 m_zeroCharWidth = (int)cachedCharWidth(zeroChar); 0659 } 0660 } 0661 0662 CachedFontInstance::~CachedFontInstance() 0663 { 0664 for (int c = 0; c < 256; ++c) { 0665 delete rows[c]; 0666 } 0667 parent->instances.remove(size); 0668 } 0669 0670 unsigned CachedFontInstance::calcAndCacheWidth(unsigned short codePoint) 0671 { 0672 unsigned rowNum = codePoint >> 8; 0673 RowInfo *row = rows[rowNum]; 0674 if (!row) { 0675 row = rows[rowNum] = new RowInfo(); 0676 } 0677 0678 unsigned width = fm.width(QChar(codePoint)); 0679 return (row->widths[codePoint & 0xFF] = qMin(width, 0xFFu)); 0680 } 0681 0682 void Font::update(int logicalDpiY) const 0683 { 0684 CachedFontFamily *family = queryFamily(fontDef.family, fontDef.weight, fontDef.italic); 0685 0686 int size = fontDef.size; 0687 0688 // ok, now some magic to get a nice unscaled font 0689 // all other font properties should be set before this one!!!! 0690 if (!family->scaleable) { 0691 const QList<int> pointSizes = family->sizes; 0692 // lets see if we find a nice looking font, which is not too far away 0693 // from the requested one. 0694 // qCDebug(KHTML_LOG) << "khtml::setFontSize family = " << f.family() << " size requested=" << size; 0695 const float toPix = qMax(logicalDpiY, 96) / 72.0f; 0696 0697 float diff = 1; // ### 100% deviation 0698 float bestSize = 0; 0699 0700 QList<int>::ConstIterator it = pointSizes.begin(); 0701 const QList<int>::ConstIterator itEnd = pointSizes.end(); 0702 0703 for (; it != itEnd; ++it) { 0704 float newDiff = (((*it) * toPix) - float(size)) / float(size); 0705 //qCDebug(KHTML_LOG) << "smooth font size: " << *it << " diff=" << newDiff; 0706 if (newDiff < 0) { 0707 newDiff = -newDiff; 0708 } 0709 if (newDiff < diff) { 0710 diff = newDiff; 0711 bestSize = *it; 0712 } 0713 } 0714 //qCDebug(KHTML_LOG) << "best smooth font size: " << bestSize << " diff=" << diff; 0715 if (bestSize != 0 && diff < 0.2) { // 20% deviation, otherwise we use a scaled font... 0716 size = static_cast<int>(bestSize * toPix); 0717 } 0718 } 0719 0720 // make sure we don't bust up X11 0721 // Also, Qt does not support sizing a QFont to zero. 0722 size = qMax(1, qMin(255, size)); 0723 0724 // qDebug("setting font to %s, italic=%d, weight=%d, size=%d", fontDef.family.toLatin1().constData(), fontDef.italic, 0725 // fontDef.weight, size ); 0726 0727 // Now request the font from the family 0728 cfi = family->queryFont(size); 0729 0730 // small caps 0731 delete scFont; 0732 scFont = nullptr; 0733 0734 if (fontDef.smallCaps) { 0735 scFont = new QFont(cfi->f); 0736 scFont->setPixelSize(qMax(1, cfi->f.pixelSize() * 7 / 10)); 0737 } 0738 } 0739 0740 CachedFontInstance *Font::defaultCFI; 0741 0742 void Font::initDefault() 0743 { 0744 if (defaultCFI) { 0745 return; 0746 } 0747 0748 // Create one for default family. It doesn't matter what size and DPI we use 0749 // since this is only used for throwaway computations 0750 // ### this may cache an instance we don't need though; but font family 0751 // is extremely likely to be used 0752 Font f; 0753 f.fontDef.size = 16; 0754 f.update(96); 0755 0756 defaultCFI = f.cfi.get(); 0757 defaultCFI->ref(); 0758 } 0759 0760 void Font::drawDecoration(QPainter *pt, int _tx, int _ty, int baseline, int width, int height, int deco) const 0761 { 0762 Q_UNUSED(height); 0763 0764 // thick lines on small fonts look ugly 0765 const int thickness = cfi->height > 20 ? cfi->fm.lineWidth() : 1; 0766 const QBrush brush = pt->pen().color(); 0767 if (deco & UNDERLINE) { 0768 int underlineOffset = (cfi->height + baseline) / 2; 0769 if (underlineOffset <= baseline) { 0770 underlineOffset = baseline + 1; 0771 } 0772 0773 pt->fillRect(_tx, _ty + underlineOffset, width + 1, thickness, brush); 0774 } 0775 if (deco & OVERLINE) { 0776 pt->fillRect(_tx, _ty, width + 1, thickness, brush); 0777 } 0778 if (deco & LINE_THROUGH) { 0779 pt->fillRect(_tx, _ty + 2 * baseline / 3, width + 1, thickness, brush); 0780 } 0781 } 0782 0783 // WebCore SVG 0784 float Font::floatWidth(QChar *str, int pos, int len, int /*extraCharsAvailable*/, int &charsConsumed, DOM::DOMString &glyphName) const 0785 { 0786 charsConsumed = len; 0787 glyphName = ""; 0788 // ### see if svg can scan the string (cf. render_text.cpp - isSimpleChar()) to determine if fast algo can be used. 0789 return width(str, 0, pos, len, false /*fast algorithm*/); 0790 } 0791 0792 float Font::floatWidth(QChar *str, int pos, int len) const 0793 { 0794 // For now, this is slow but correct... 0795 // or rather it /would/ be correct if QFontMetricsF had charWidth(); 0796 // so instead the approximate width is used 0797 QFontMetricsF fm(cfi->f); 0798 return float(fm.width(QString::fromRawData(str + pos, len))); 0799 } 0800 0801 } 0802