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
MainWindow(const QString & customFont)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
on_actionAdd_Custom_Font_triggered()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
on_selectAll_clicked()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
on_deselectAll_clicked()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
on_invertSelection_clicked()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
fontChanged()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
on_browsePath_clicked()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
on_browseSampleFile_clicked()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
on_generate_clicked()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
on_sampleFile_editingFinished()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
populateCharacterRanges()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
addCustomFont(const QString & fontFile)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