File indexing completed on 2024-05-05 16:10:37

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