Changeset 230 for trunk/src/gui/text/qfontdatabase_pm.cpp
- Timestamp:
- Oct 16, 2009, 3:22:12 PM (16 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/gui/text/qfontdatabase_pm.cpp
r228 r230 46 46 #include "qfontengine_pm_p.h" 47 47 48 #include "qsettings.h" 49 #include "qfileinfo.h" 50 #include "qdatetime.h" 51 #include "qhash.h" 52 48 53 #include <ft2build.h> 49 54 #include FT_FREETYPE_H … … 53 58 54 59 QT_BEGIN_NAMESPACE 60 61 struct FaceData 62 { 63 int index; 64 QString familyName; 65 QtFontStyle::Key styleKey; 66 QList<QFontDatabase::WritingSystem> systems; 67 bool fixedPitch; 68 bool smoothScalable; 69 QList<unsigned short> pixelSizes; 70 }; 71 72 static QDataStream &operator<<(QDataStream &data, const QFontDatabase::WritingSystem &ws) 73 { 74 data << (int)ws; 75 return data; 76 } 77 78 static QDataStream &operator>>(QDataStream &data, QFontDatabase::WritingSystem &ws) 79 { 80 data >> (int&)ws; 81 return data; 82 } 83 84 static QDataStream &operator<<(QDataStream &data, const FaceData &cached) 85 { 86 data << cached.familyName; 87 data << cached.styleKey.style << cached.styleKey.weight 88 << cached.styleKey.stretch; 89 data << cached.systems; 90 data << cached.fixedPitch << cached.smoothScalable; 91 data << cached.pixelSizes; 92 return data; 93 } 94 95 static QDataStream &operator>>(QDataStream &data, FaceData &cached) 96 { 97 data >> cached.familyName; 98 uint style; 99 int weight, stretch; 100 data >> style; cached.styleKey.style = style; 101 data >> weight; cached.styleKey.weight = weight; 102 data >> stretch; cached.styleKey.stretch = stretch; 103 data >> cached.systems; 104 data >> cached.fixedPitch >> cached.smoothScalable; 105 data >> cached.pixelSizes; 106 return data; 107 } 108 109 struct FileData 110 { 111 FileData() : seen(false) {} 112 FileData(const QFileInfo &fi, bool s) : fileInfo(fi), seen(s) {} 113 114 QFileInfo fileInfo; 115 bool seen; 116 }; 117 118 typedef QHash<QString, FileData> FontFileHash; 119 static FontFileHash knownFontFiles; 120 static bool knownFontFilesInitialized = false; 55 121 56 122 static void populateDatabase(const QString& fam) … … 72 138 const QString foundryName; 73 139 74 QList<QByteArray> fontFiles; 140 #ifdef QFONTDATABASE_DEBUG 141 QTime timer; 142 timer.start(); 143 #endif 144 145 QSettings fontCache(QSettings::UserScope, QLatin1String("Trolltech")); 146 fontCache.beginGroup(QLatin1String("Qt/Fonts/Cache 1.0")); 147 148 if (!knownFontFilesInitialized) { 149 // get the initial list of know font files from the cache (necessary to 150 // detect deleted font files) 151 knownFontFilesInitialized = true; 152 QStringList files = fontCache.childGroups(); 153 foreach(QString file, files) { 154 file.replace("|", "/"); 155 knownFontFiles.insert(file, FileData()); 156 // note that QFileInfo is empty so the file will be considered as 157 // NEW which is necessary for the font to get into the database 158 } 159 } else { 160 // reset the 'seen' flag 161 for (FontFileHash::iterator it = knownFontFiles.begin(); 162 it != knownFontFiles.end(); ++it) 163 it.value().seen = false; 164 } 165 166 QList<QFileInfo> fontFiles; 75 167 76 168 ULONG bufSize = 0; … … 101 193 file.append(".PFB"); 102 194 } 103 fontFiles << file; 195 QFileInfo fileInfo(QFile::decodeName(file)); 196 QString fileName = fileInfo.canonicalFilePath().toLower(); 197 if (!fileName.isEmpty()) { // file may have been deleted 198 fileInfo.setFile(fileName); 199 // check the in-process file name cache 200 FileData &cached = knownFontFiles[fileName]; 201 if (cached.fileInfo.filePath().isEmpty() || 202 cached.fileInfo.lastModified() != fileInfo.lastModified() || 203 cached.fileInfo.size() != fileInfo.size()) { 204 // no cache entry or outdated, process it 205 cached.fileInfo = fileInfo; 206 cached.seen = true; 207 fontFiles << fileInfo; 208 FD_DEBUG("populateDatabase: NEW/UPDATED font file %s", 209 qPrintable(fileName)); 210 } else { 211 // just set the 'seen' flag and skip this font 212 // (it's already in the database) 213 knownFontFiles[fileName].seen = true; 214 FD_DEBUG("populateDatabase: UNCHANGED font file %s", 215 qPrintable(fileName)); 216 } 217 } 104 218 } 105 219 } … … 114 228 FT_Library lib = qt_getFreetype(); 115 229 116 foreach(const QByteArray &file, fontFiles) { 117 FT_Face face; 118 FT_Error rc = FT_New_Face(lib, file, -1, &face); 119 120 FD_DEBUG("populateDatabase: Font file %s: FT error %d, has %ld faces", 121 file.constData(), (int) rc, rc ? -1 : face->num_faces); 122 if (rc != 0) 123 continue; 124 125 FT_Long numFaces = face->num_faces; 126 FT_Done_Face(face); 127 128 // go throuhg each face 129 for (FT_Long idx = 0; idx < numFaces; ++idx) { 130 rc = FT_New_Face(lib, file, idx, &face); 131 if (rc != 0) 132 continue; 133 134 QString familyName = QString::fromLatin1(face->family_name); 135 136 // familyName may contain extra spaces (at least this is true for 137 // TNR.PFB that is reported as "Times New Roman ". Trim them. 138 familyName = familyName.trimmed(); 139 140 QtFontStyle::Key styleKey; 141 142 styleKey.style = face->style_flags & FT_STYLE_FLAG_ITALIC ? 143 QFont::StyleItalic : QFont::StyleNormal; 144 145 QList<QFontDatabase::WritingSystem> systems; 146 147 TT_OS2 *os2_table = 0; 148 if (face->face_flags & FT_FACE_FLAG_SFNT) { 149 os2_table = (TT_OS2 *)FT_Get_Sfnt_Table(face, ft_sfnt_os2); 150 } 151 if (os2_table) { 152 // map weight and width values 153 if (os2_table->usWeightClass < 400) 154 styleKey.weight = QFont::Light; 155 else if (os2_table->usWeightClass < 600) 156 styleKey.weight = QFont::Normal; 157 else if (os2_table->usWeightClass < 700) 158 styleKey.weight = QFont::DemiBold; 159 else if (os2_table->usWeightClass < 800) 160 styleKey.weight = QFont::Bold; 161 else 162 styleKey.weight = QFont::Black; 163 164 switch (os2_table->usWidthClass) { 165 case 1: styleKey.stretch = QFont::UltraCondensed; break; 166 case 2: styleKey.stretch = QFont::ExtraCondensed; break; 167 case 3: styleKey.stretch = QFont::Condensed; break; 168 case 4: styleKey.stretch = QFont::SemiCondensed; break; 169 case 5: styleKey.stretch = QFont::Unstretched; break; 170 case 6: styleKey.stretch = QFont::SemiExpanded; break; 171 case 7: styleKey.stretch = QFont::Expanded; break; 172 case 8: styleKey.stretch = QFont::ExtraExpanded; break; 173 case 9: styleKey.stretch = QFont::UltraExpanded; break; 174 default: styleKey.stretch = QFont::Unstretched; break; 230 // go through each font file and get available faces 231 foreach(const QFileInfo &fileInfo, fontFiles) { 232 QString fileKey = fileInfo.absoluteFilePath().toLower(); 233 QByteArray file = QFile::encodeName(fileKey); 234 235 // QSettings uses / for splitting into groups, suppress it 236 fileKey.replace("/", "|"); 237 238 QList<FaceData> cachedFaces; 239 240 // first, look up the cached data 241 fontCache.beginGroup(fileKey); 242 243 if (fontCache.value(QLatin1String("DateTime")).toDateTime() != fileInfo.lastModified() || 244 fontCache.value(QLatin1String("Size")).toUInt() != fileInfo.size()) { 245 // the cache is outdated or doesn't exist, query the font file 246 247 FT_Long numFaces = 0; 248 FT_Face face; 249 250 FT_Error rc = FT_New_Face(lib, file, -1, &face); 251 if (rc == 0) { 252 numFaces = face->num_faces; 253 FT_Done_Face(face); 254 } else { 255 // invalid/unsupported font file, numFaces is left 0 so that 256 // only DateTime and Size will be cached indicating that this 257 // file is not recognized 258 } 259 260 FD_DEBUG("populateDatabase: Font file %s: FT error %d, has %ld faces", 261 file.constData(), (int) rc, numFaces); 262 263 // go throuhg each face 264 for (FT_Long idx = 0; idx < numFaces; ++idx) { 265 rc = FT_New_Face(lib, file, idx, &face); 266 if (rc != 0) 267 continue; 268 269 FaceData cached; 270 271 cached.index = idx; 272 cached.familyName = QString::fromLatin1(face->family_name); 273 274 // familyName may contain extra spaces (at least this is true for 275 // TNR.PFB that is reported as "Times New Roman ". Trim them. 276 cached.familyName = cached.familyName.trimmed(); 277 278 cached.styleKey.style = face->style_flags & FT_STYLE_FLAG_ITALIC ? 279 QFont::StyleItalic : QFont::StyleNormal; 280 281 TT_OS2 *os2_table = 0; 282 if (face->face_flags & FT_FACE_FLAG_SFNT) { 283 os2_table = (TT_OS2 *)FT_Get_Sfnt_Table(face, ft_sfnt_os2); 175 284 } 176 177 quint32 unicodeRange[4] = { 178 os2_table->ulUnicodeRange1, os2_table->ulUnicodeRange2, 179 os2_table->ulUnicodeRange3, os2_table->ulUnicodeRange4 180 }; 181 quint32 codePageRange[2] = { 182 os2_table->ulCodePageRange1, os2_table->ulCodePageRange2 183 }; 184 systems = determineWritingSystemsFromTrueTypeBits(unicodeRange, codePageRange); 185 } else { 186 // we've only got simple weight information and no stretch 187 styleKey.weight = face->style_flags & FT_STYLE_FLAG_BOLD ? 188 QFont::Bold : QFont::Normal; 189 styleKey.stretch = QFont::Unstretched; 190 } 191 192 QtFontFamily *family = privateDb()->family(familyName, true); 285 if (os2_table) { 286 // map weight and width values 287 if (os2_table->usWeightClass < 400) 288 cached.styleKey.weight = QFont::Light; 289 else if (os2_table->usWeightClass < 600) 290 cached.styleKey.weight = QFont::Normal; 291 else if (os2_table->usWeightClass < 700) 292 cached.styleKey.weight = QFont::DemiBold; 293 else if (os2_table->usWeightClass < 800) 294 cached.styleKey.weight = QFont::Bold; 295 else 296 cached.styleKey.weight = QFont::Black; 297 298 switch (os2_table->usWidthClass) { 299 case 1: cached.styleKey.stretch = QFont::UltraCondensed; break; 300 case 2: cached.styleKey.stretch = QFont::ExtraCondensed; break; 301 case 3: cached.styleKey.stretch = QFont::Condensed; break; 302 case 4: cached.styleKey.stretch = QFont::SemiCondensed; break; 303 case 5: cached.styleKey.stretch = QFont::Unstretched; break; 304 case 6: cached.styleKey.stretch = QFont::SemiExpanded; break; 305 case 7: cached.styleKey.stretch = QFont::Expanded; break; 306 case 8: cached.styleKey.stretch = QFont::ExtraExpanded; break; 307 case 9: cached.styleKey.stretch = QFont::UltraExpanded; break; 308 default: cached.styleKey.stretch = QFont::Unstretched; break; 309 } 310 311 quint32 unicodeRange[4] = { 312 os2_table->ulUnicodeRange1, os2_table->ulUnicodeRange2, 313 os2_table->ulUnicodeRange3, os2_table->ulUnicodeRange4 314 }; 315 quint32 codePageRange[2] = { 316 os2_table->ulCodePageRange1, os2_table->ulCodePageRange2 317 }; 318 cached.systems = 319 determineWritingSystemsFromTrueTypeBits(unicodeRange, codePageRange); 320 } else { 321 // we've only got simple weight information and no stretch 322 cached.styleKey.weight = face->style_flags & FT_STYLE_FLAG_BOLD ? 323 QFont::Bold : QFont::Normal; 324 cached.styleKey.stretch = QFont::Unstretched; 325 } 326 327 cached.fixedPitch = face->face_flags & FT_FACE_FLAG_FIXED_WIDTH; 328 329 cached.smoothScalable = face->face_flags & FT_FACE_FLAG_SCALABLE; 330 331 // the font may both be scalable and contain fixed size bitmaps 332 if (face->face_flags & FT_FACE_FLAG_FIXED_SIZES) { 333 for (FT_Int i = 0; i < face->num_fixed_sizes; ++i) { 334 cached.pixelSizes << face->available_sizes[i].height; 335 } 336 } 337 338 cachedFaces << cached; 339 340 FT_Done_Face(face); 341 } 342 343 // store the data into the cache 344 fontCache.setValue(QLatin1String("DateTime"), fileInfo.lastModified()); 345 fontCache.setValue(QLatin1String("Size"), fileInfo.size()); 346 foreach(FaceData cached, cachedFaces) { 347 QByteArray rawData; 348 QDataStream data(&rawData, QIODevice::WriteOnly); 349 data << cached; 350 351 QString face = QString::number(cached.index); 352 fontCache.beginGroup(face); 353 fontCache.setValue(QLatin1String("Info"), rawData); 354 fontCache.endGroup(); 355 } 356 } else { 357 // take the face data from the cache 358 359 QStringList faces = fontCache.childGroups(); 360 361 FD_DEBUG("populateDatabase: Font file %s: IN CACHE, has %d faces", 362 file.constData(), faces.count()); 363 364 foreach(QString face, faces) { 365 bool ok = false; 366 FaceData cached; 367 cached.index = face.toInt(&ok); 368 if (!ok || cached.index < 0) // not a valid index 369 continue; 370 371 fontCache.beginGroup(face); 372 QByteArray rawData = 373 fontCache.value(QLatin1String("Info")).toByteArray(); 374 QDataStream data(rawData); 375 data >> cached; 376 fontCache.endGroup(); 377 378 cachedFaces << cached; 379 } 380 } 381 382 fontCache.endGroup(); 383 384 // go throuhg each cached face and add it to the database 385 foreach(FaceData cached, cachedFaces) { 386 387 QtFontFamily *family = privateDb()->family(cached.familyName, true); 193 388 194 389 // @todo is it possible that the same family is both fixed and not? 195 Q_ASSERT(!family->fixedPitch || face->face_flags & FT_FACE_FLAG_FIXED_WIDTH);196 family->fixedPitch = face->face_flags & FT_FACE_FLAG_FIXED_WIDTH;197 198 if ( systems.isEmpty() ||familyName.compare("Workplace Sans")) {390 Q_ASSERT(!family->fixedPitch || cached.fixedPitch); 391 family->fixedPitch = cached.fixedPitch; 392 393 if (cached.systems.isEmpty() || cached.familyName.compare("Workplace Sans")) { 199 394 // it was hard or impossible to determine the actual writing system 200 395 // of the font (as in case of OS/2 bitmap and PFB fonts for which it is … … 210 405 family->writingSystems[ws] = QtFontFamily::Supported; 211 406 } else { 212 for (int i = 0; i < systems.count(); ++i)213 family->writingSystems[ systems.at(i)] = QtFontFamily::Supported;407 for (int i = 0; i < cached.systems.count(); ++i) 408 family->writingSystems[cached.systems.at(i)] = QtFontFamily::Supported; 214 409 } 215 410 216 411 QtFontFoundry *foundry = family->foundry(foundryName, true); 217 QtFontStyle *style = foundry->style( styleKey, true);412 QtFontStyle *style = foundry->style(cached.styleKey, true); 218 413 219 414 // so far, all recognized fonts are antialiased 220 415 style->antialiased = true; 221 416 222 if ((face->face_flags & FT_FACE_FLAG_SCALABLE) && 223 !style->smoothScalable) { 417 if (cached.smoothScalable && !style->smoothScalable) { 224 418 // add new scalable style only if it hasn't been already added -- 225 419 // the first one of two duplicate (in Qt terms) non-bitmap font … … 229 423 style->pixelSize(SMOOTH_SCALABLE, true); 230 424 size->fileName = file; 231 size->fileIndex = idx; 232 } 233 234 // the font may both be scalable and contain fixed size bitmaps 235 if (face->face_flags & FT_FACE_FLAG_FIXED_SIZES) { 236 for (FT_Int i = 0; i < face->num_fixed_sizes; ++i) { 237 QtFontSize *size = 238 style->pixelSize(face->available_sizes[i].height, true); 239 // the first bitmap style with a given pixel and point size wins 240 if (!size->fileName.isEmpty()) 241 continue; 242 size->fileName = file; 243 size->fileIndex = idx; 244 } 245 } 246 247 FT_Done_Face(face); 248 } 249 } 425 size->fileIndex = cached.index; 426 } 427 428 foreach(unsigned short pixelSize, cached.pixelSizes) { 429 QtFontSize *size = style->pixelSize(pixelSize, true); 430 // the first bitmap style with a given pixel and point size wins 431 if (!size->fileName.isEmpty()) 432 continue; 433 size->fileName = file; 434 size->fileIndex = cached.index; 435 } 436 } 437 } 438 439 // go through the known file list to detect what files have been removed 440 for (FontFileHash::iterator it = knownFontFiles.begin(); 441 it != knownFontFiles.end();) { 442 if (!it.value().seen) { 443 FD_DEBUG("populateDatabase: DELETED font file %s", 444 qPrintable(it.key())); 445 // remove from the both caches 446 QString fileKey = it.key(); 447 fileKey.replace("/", "|"); 448 fontCache.remove(fileKey); 449 it = knownFontFiles.erase(it); 450 // @todo should we remove all references to this file from the 451 // font database? My concern is that this font may be in use by Qt 452 // and its glyphs may be still cached when file deletion happens 453 } else { 454 ++it; 455 } 456 } 457 458 #ifdef QFONTDATABASE_DEBUG 459 FD_DEBUG("populateDatabase: took %d ms", timer.elapsed()); 460 #endif 250 461 } 251 462
Note:
See TracChangeset
for help on using the changeset viewer.