1 /**************************************************************************** 2 ** 3 ** Copyright (C) 2016 The Qt Company Ltd. 4 ** Contact: https://www.qt.io/licensing/ 5 ** 6 ** This file is part of the tools applications of the Qt Toolkit. 7 ** 8 ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ 9 ** Commercial License Usage 10 ** Licensees holding valid commercial Qt licenses may use this file in 11 ** accordance with the commercial license agreement provided with the 12 ** Software or, alternatively, in accordance with the terms contained in 13 ** a written agreement between you and The Qt Company. For licensing terms 14 ** and conditions see https://www.qt.io/terms-conditions. For further 15 ** information use the contact form at https://www.qt.io/contact-us. 16 ** 17 ** GNU General Public License Usage 18 ** Alternatively, this file may be used under the terms of the GNU 19 ** General Public License version 3 as published by the Free Software 20 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT 21 ** included in the packaging of this file. Please review the following 22 ** information to ensure the GNU General Public License requirements will 23 ** be met: https://www.gnu.org/licenses/gpl-3.0.html. 24 ** 25 ** $QT_END_LICENSE$ 26 ** 27 ****************************************************************************/ 28 29 #include "mainwindow.h" 30 31 #include <QFontComboBox> 32 #include <QFontDatabase> 33 #include <QFileDialog> 34 #include <QMessageBox> 35 #include <QListWidget> 36 #include <QDebug> 37 #include <QShortcut> 38 #include <QCompleter> 39 #include <QDirModel> 40 #include <QRegularExpression> 41 #include <QTextCodec> 42 43 QT_BEGIN_NAMESPACE 44 45 MainWindow::MainWindow(const QString &customFont) 46 { 47 setupUi(this); 48 pixelSize->setValue(QFontInfo(QFont()).pixelSize()); 49 populateCharacterRanges(); 50 51 { 52 weightCombo->addItem(QLatin1String("Light"), QVariant(int(QFont::Light))); 53 const int normalIdx = weightCombo->count(); 54 weightCombo->addItem(QLatin1String("Normal"), QVariant(int(QFont::Normal))); 55 weightCombo->addItem(QLatin1String("DemiBold"), QVariant(int(QFont::DemiBold))); 56 weightCombo->addItem(QLatin1String("Bold"), QVariant(int(QFont::Bold))); 57 weightCombo->addItem(QLatin1String("Black"), QVariant(int(QFont::Black))); 58 59 weightCombo->setCurrentIndex(normalIdx); 60 } 61 62 QShortcut *sc = new QShortcut(Qt::ControlModifier + Qt::Key_A, this); 63 connect(sc, SIGNAL(activated()), 64 this, SLOT(on_selectAll_clicked())); 65 sc = new QShortcut(Qt::ControlModifier + Qt::Key_D, this); 66 connect(sc, SIGNAL(activated()), 67 this, SLOT(on_deselectAll_clicked())); 68 sc = new QShortcut(Qt::ControlModifier + Qt::Key_I, this); 69 connect(sc, SIGNAL(activated()), 70 this, SLOT(on_invertSelection_clicked())); 71 72 QCompleter *completer = new QCompleter(this); 73 completer->setModel(new QDirModel(this)); 74 path->setCompleter(completer); 75 path->setText(QDir::currentPath()); 76 77 completer = new QCompleter(this); 78 completer->setModel(new QDirModel(this)); 79 sampleFile->setCompleter(completer); 80 charCount->setText(QString()); 81 82 if (!customFont.isEmpty()) 83 addCustomFont(customFont); 84 85 fontChanged(); 86 87 connect(fontComboBox, SIGNAL(currentFontChanged(QFont)), 88 this, SLOT(fontChanged())); 89 connect(pixelSize, SIGNAL(valueChanged(int)), 90 this, SLOT(fontChanged())); 91 connect(italic, SIGNAL(stateChanged(int)), 92 this, SLOT(fontChanged())); 93 connect(weightCombo, SIGNAL(currentIndexChanged(int)), 94 this, SLOT(fontChanged())); 95 } 96 97 void MainWindow::on_actionAdd_Custom_Font_triggered() 98 { 99 QString fontFile = QFileDialog::getOpenFileName(this, tr("Add Custom Font")); 100 if (fontFile.isEmpty()) 101 return; 102 addCustomFont(fontFile); 103 } 104 105 void MainWindow::on_selectAll_clicked() 106 { 107 for (int i = 0; i < characterRangeView->count(); ++i) 108 characterRangeView->item(i)->setCheckState(Qt::Checked); 109 } 110 111 void MainWindow::on_deselectAll_clicked() 112 { 113 for (int i = 0; i < characterRangeView->count(); ++i) 114 characterRangeView->item(i)->setCheckState(Qt::Unchecked); 115 } 116 117 void MainWindow::on_invertSelection_clicked() 118 { 119 for (int i = 0; i < characterRangeView->count(); ++i) { 120 QListWidgetItem *item = characterRangeView->item(i); 121 if (item->checkState() == Qt::Checked) 122 item->setCheckState(Qt::Unchecked); 123 else 124 item->setCheckState(Qt::Checked); 125 } 126 } 127 128 void MainWindow::fontChanged() 129 { 130 QFont f = preview->font(); 131 f.setStyleStrategy(QFont::NoFontMerging); 132 f.setPixelSize(pixelSize->value()); 133 f.setFamily(fontComboBox->currentFont().family()); 134 f.setItalic(italic->isChecked()); 135 f.setWeight(weightCombo->itemData(weightCombo->currentIndex()).toInt()); 136 137 if (!preview->isModified()) { 138 QFontDatabase db; 139 QFontDatabase::WritingSystem ws = db.writingSystems(f.family()).value(0, QFontDatabase::Any); 140 QString sample = db.writingSystemSample(ws); 141 preview->setText(sample); 142 preview->setModified(false); 143 } 144 145 fileName->setText(QPF::fileNameForFont(f)); 146 147 preview->setFont(f); 148 } 149 150 void MainWindow::on_browsePath_clicked() 151 { 152 QString dir = QFileDialog::getExistingDirectory(this, tr("Select Directory")); 153 if (!dir.isEmpty()) 154 path->setText(dir); 155 } 156 157 void MainWindow::on_browseSampleFile_clicked() 158 { 159 QString dir = QFileDialog::getOpenFileName(this, tr("Select Sample File")); 160 if (!dir.isEmpty()) { 161 sampleFile->setText(dir); 162 on_sampleFile_editingFinished(); 163 } 164 } 165 166 void MainWindow::on_generate_clicked() 167 { 168 QFile f(path->text() + QDir::separator() + fileName->text()); 169 if (f.exists()) { 170 if (QMessageBox::warning(this, QString(), 171 tr("%1 already exists.\nDo you want to replace it?").arg(f.fileName()), 172 QMessageBox::Yes | QMessageBox::No, QMessageBox::No) 173 != QMessageBox::Yes) { 174 statusBar()->showMessage(tr("Pre-rendering aborted...")); 175 return; 176 } 177 } 178 179 QList<QPF::CharacterRange> ranges; 180 181 if (chooseFromSampleFile->isChecked()) { 182 ranges = sampleFileRanges; 183 } else if (chooseFromCodePoints->isChecked()) { 184 ranges.clear(); 185 for (int i = 0; i < characterRangeView->count(); ++i) { 186 QListWidgetItem *item = characterRangeView->item(i); 187 if (item->checkState() != Qt::Checked) 188 continue; 189 190 QPF::CharacterRange range = qvariant_cast<QPF::CharacterRange>(item->data(Qt::UserRole)); 191 ranges.append(range); 192 } 193 } 194 195 const int generationOptions = QPF::IncludeCMap | QPF::RenderGlyphs; 196 QByteArray qpf = QPF::generate(preview->font(), generationOptions, ranges); 197 f.open(QIODevice::WriteOnly | QIODevice::Truncate); 198 f.write(qpf); 199 f.close(); 200 201 statusBar()->showMessage(tr("Font successfully pre-rendered to %1").arg(fileName->text())); 202 } 203 204 void MainWindow::on_sampleFile_editingFinished() 205 { 206 sampleFileRanges.clear(); 207 QFile f(sampleFile->text()); 208 if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) { 209 sampleFileRanges.append(QPF::CharacterRange()); // default = all 210 return; 211 } 212 QTextStream stream(&f); 213 stream.setCodec(QTextCodec::codecForName("utf-8")); 214 stream.setAutoDetectUnicode(true); 215 QString text = stream.readAll(); 216 217 QSet<QChar> coverage; 218 for (int i = 0; i < text.length(); ++i) 219 coverage.insert(text.at(i)); 220 221 QList<QChar> sortedCoverage = QList<QChar>::fromSet(coverage); 222 std::sort(sortedCoverage.begin(), sortedCoverage.end()); 223 // play simple :) 224 for (QChar ch : qAsConst(sortedCoverage)) { 225 QPF::CharacterRange r; 226 r.start = ch.unicode(); 227 r.end = r.start + 1; 228 sampleFileRanges.append(r); 229 } 230 231 charCount->setText(tr("(%1 unique characters found)").arg(sortedCoverage.count())); 232 } 233 234 void MainWindow::populateCharacterRanges() 235 { 236 QFile f(":/Blocks.txt"); 237 if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) 238 return; 239 240 QRegularExpression rangeExpr( 241 QRegularExpression::anchoredPattern("([0-9a-f]+)\\.\\.([0-9a-f]+); (.+)"), 242 QRegularExpression::CaseInsensitiveOption 243 ); 244 245 QString ellipsis(QChar(0x2026)); 246 if (!characterRangeView->fontMetrics().inFont(ellipsis.at(0))) 247 ellipsis = QLatin1String("..."); 248 249 while (!f.atEnd()) { 250 QString line = QString::fromLatin1(f.readLine()); 251 252 if (line.endsWith(QLatin1Char('\n'))) 253 line.chop(1); 254 if (line.endsWith(QLatin1Char('\r'))) 255 line.chop(1); 256 257 line = line.trimmed(); 258 259 if (line.isEmpty() || line.startsWith(QLatin1Char('#'))) 260 continue; 261 262 QRegularExpressionMatch match = rangeExpr.match(line); 263 if (!match.hasMatch()) 264 continue; 265 266 QPF::CharacterRange range; 267 268 bool ok = false; 269 range.start = match.captured(1).toUInt(&ok, /*base*/16); 270 if (!ok) 271 continue; 272 range.end = match.captured(2).toUInt(&ok, /*base*/16); 273 if (!ok) 274 continue; 275 276 if (range.start >= 0xffff || range.end >= 0xffff) 277 continue; 278 279 QString description = match.captured(3); 280 281 QListWidgetItem *item = new QListWidgetItem(characterRangeView); 282 QString text = description; 283 text.append(QLatin1String(" (")); 284 text.append(match.captured(1)); 285 text.append(ellipsis); 286 text.append(match.captured(2)); 287 text.append(QLatin1String(")")); 288 item->setText(text); 289 item->setCheckState(Qt::Checked); 290 291 item->setData(Qt::UserRole, QVariant::fromValue(range)); 292 } 293 } 294 295 void MainWindow::addCustomFont(const QString &fontFile) 296 { 297 int id = QFontDatabase::addApplicationFont(fontFile); 298 if (id < 0) { 299 QMessageBox::warning(this, tr("Error Adding Custom Font"), 300 tr("The custom font %s could not be loaded.").arg(fontFile)); 301 return; 302 } 303 const QStringList families = QFontDatabase::applicationFontFamilies(id); 304 if (families.isEmpty()) { 305 QMessageBox::warning(this, tr("Error Adding Custom Font"), 306 tr("The custom font %s provides no font families.").arg(fontFile)); 307 return; 308 } 309 QFont f(families.first()); 310 fontComboBox->setCurrentFont(f); 311 } 312 313 QT_END_NAMESPACE 314