| 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 examples of the Qt Toolkit. |
| 7 | ** |
| 8 | ** $QT_BEGIN_LICENSE:BSD$ |
| 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 | ** BSD License Usage |
| 18 | ** Alternatively, you may use this file under the terms of the BSD license |
| 19 | ** as follows: |
| 20 | ** |
| 21 | ** "Redistribution and use in source and binary forms, with or without |
| 22 | ** modification, are permitted provided that the following conditions are |
| 23 | ** met: |
| 24 | ** * Redistributions of source code must retain the above copyright |
| 25 | ** notice, this list of conditions and the following disclaimer. |
| 26 | ** * Redistributions in binary form must reproduce the above copyright |
| 27 | ** notice, this list of conditions and the following disclaimer in |
| 28 | ** the documentation and/or other materials provided with the |
| 29 | ** distribution. |
| 30 | ** * Neither the name of The Qt Company Ltd nor the names of its |
| 31 | ** contributors may be used to endorse or promote products derived |
| 32 | ** from this software without specific prior written permission. |
| 33 | ** |
| 34 | ** |
| 35 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 36 | ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 37 | ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| 38 | ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| 39 | ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 40 | ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 41 | ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 42 | ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 43 | ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 44 | ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 45 | ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." |
| 46 | ** |
| 47 | ** $QT_END_LICENSE$ |
| 48 | ** |
| 49 | ****************************************************************************/ |
| 50 | |
| 51 | #include <QtWidgets> |
| 52 | #if defined(QT_PRINTSUPPORT_LIB) |
| 53 | #include <QtPrintSupport/qtprintsupportglobal.h> |
| 54 | #if QT_CONFIG(printdialog) |
| 55 | #include <QPrinter> |
| 56 | #include <QPrintDialog> |
| 57 | #if QT_CONFIG(printpreviewdialog) |
| 58 | #include <QPrintPreviewDialog> |
| 59 | #endif |
| 60 | #endif |
| 61 | #endif |
| 62 | |
| 63 | #include "mainwindow.h" |
| 64 | |
| 65 | MainWindow::MainWindow(QWidget *parent) |
| 66 | : QMainWindow(parent) |
| 67 | { |
| 68 | setupUi(this); |
| 69 | |
| 70 | sampleSizes << 32 << 24 << 16 << 14 << 12 << 8 << 4 << 2 << 1; |
| 71 | markedCount = 0; |
| 72 | setupFontTree(); |
| 73 | |
| 74 | connect(sender: quitAction, signal: &QAction::triggered, |
| 75 | qApp, slot: &QApplication::quit); |
| 76 | connect(sender: fontTree, signal: &QTreeWidget::currentItemChanged, |
| 77 | receiver: this, slot: &MainWindow::showFont); |
| 78 | connect(sender: fontTree, signal: &QTreeWidget::itemChanged, |
| 79 | receiver: this, slot: &MainWindow::updateStyles); |
| 80 | |
| 81 | fontTree->topLevelItem(index: 0)->setSelected(true); |
| 82 | showFont(item: fontTree->topLevelItem(index: 0)); |
| 83 | } |
| 84 | |
| 85 | void MainWindow::setupFontTree() |
| 86 | { |
| 87 | QFontDatabase database; |
| 88 | fontTree->setColumnCount(1); |
| 89 | fontTree->setHeaderLabels({ tr(s: "Font" ) }); |
| 90 | |
| 91 | const QStringList fontFamilies = database.families(); |
| 92 | for (const QString &family : fontFamilies) { |
| 93 | const QStringList styles = database.styles(family); |
| 94 | if (styles.isEmpty()) |
| 95 | continue; |
| 96 | |
| 97 | QTreeWidgetItem *familyItem = new QTreeWidgetItem(fontTree); |
| 98 | familyItem->setText(column: 0, atext: family); |
| 99 | familyItem->setCheckState(column: 0, state: Qt::Unchecked); |
| 100 | familyItem->setFlags(familyItem->flags() | Qt::ItemIsAutoTristate); |
| 101 | |
| 102 | for (const QString &style : styles) { |
| 103 | QTreeWidgetItem *styleItem = new QTreeWidgetItem(familyItem); |
| 104 | styleItem->setText(column: 0, atext: style); |
| 105 | styleItem->setCheckState(column: 0, state: Qt::Unchecked); |
| 106 | styleItem->setData(column: 0, role: Qt::UserRole, value: QVariant(database.weight(family, style))); |
| 107 | styleItem->setData(column: 0, role: Qt::UserRole + 1, value: QVariant(database.italic(family, style))); |
| 108 | } |
| 109 | } |
| 110 | } |
| 111 | |
| 112 | void MainWindow::on_clearAction_triggered() |
| 113 | { |
| 114 | const QList<QTreeWidgetItem *> items = fontTree->selectedItems(); |
| 115 | for (QTreeWidgetItem *item : items) |
| 116 | item->setSelected(false); |
| 117 | fontTree->currentItem()->setSelected(true); |
| 118 | } |
| 119 | |
| 120 | void MainWindow::on_markAction_triggered() |
| 121 | { |
| 122 | markUnmarkFonts(state: Qt::Checked); |
| 123 | } |
| 124 | |
| 125 | void MainWindow::on_unmarkAction_triggered() |
| 126 | { |
| 127 | markUnmarkFonts(state: Qt::Unchecked); |
| 128 | } |
| 129 | |
| 130 | void MainWindow::markUnmarkFonts(Qt::CheckState state) |
| 131 | { |
| 132 | const QList<QTreeWidgetItem *> items = fontTree->selectedItems(); |
| 133 | for (QTreeWidgetItem *item : items) { |
| 134 | if (item->checkState(column: 0) != state) |
| 135 | item->setCheckState(column: 0, state); |
| 136 | } |
| 137 | } |
| 138 | |
| 139 | void MainWindow::showFont(QTreeWidgetItem *item) |
| 140 | { |
| 141 | if (!item) |
| 142 | return; |
| 143 | |
| 144 | QString family; |
| 145 | QString style; |
| 146 | int weight; |
| 147 | bool italic; |
| 148 | |
| 149 | if (item->parent()) { |
| 150 | family = item->parent()->text(column: 0); |
| 151 | style = item->text(column: 0); |
| 152 | weight = item->data(column: 0, role: Qt::UserRole).toInt(); |
| 153 | italic = item->data(column: 0, role: Qt::UserRole + 1).toBool(); |
| 154 | } else { |
| 155 | family = item->text(column: 0); |
| 156 | style = item->child(index: 0)->text(column: 0); |
| 157 | weight = item->child(index: 0)->data(column: 0, role: Qt::UserRole).toInt(); |
| 158 | italic = item->child(index: 0)->data(column: 0, role: Qt::UserRole + 1).toBool(); |
| 159 | } |
| 160 | |
| 161 | QString oldText = textEdit->toPlainText().trimmed(); |
| 162 | bool modified = textEdit->document()->isModified(); |
| 163 | textEdit->clear(); |
| 164 | QFont font(family, 32, weight, italic); |
| 165 | font.setStyleName(style); |
| 166 | textEdit->document()->setDefaultFont(font); |
| 167 | |
| 168 | QTextCursor cursor = textEdit->textCursor(); |
| 169 | QTextBlockFormat blockFormat; |
| 170 | blockFormat.setAlignment(Qt::AlignCenter); |
| 171 | cursor.insertBlock(format: blockFormat); |
| 172 | |
| 173 | if (modified) |
| 174 | cursor.insertText(text: QString(oldText)); |
| 175 | else |
| 176 | cursor.insertText(text: QString("%1 %2" ).arg(a: family).arg(a: style)); |
| 177 | |
| 178 | textEdit->document()->setModified(modified); |
| 179 | } |
| 180 | |
| 181 | void MainWindow::updateStyles(QTreeWidgetItem *item, int column) |
| 182 | { |
| 183 | if (!item || column != 0) |
| 184 | return; |
| 185 | |
| 186 | Qt::CheckState state = item->checkState(column: 0); |
| 187 | QTreeWidgetItem *parent = item->parent(); |
| 188 | |
| 189 | if (parent) { |
| 190 | // Only count style items. |
| 191 | if (state == Qt::Checked) |
| 192 | ++markedCount; |
| 193 | else |
| 194 | --markedCount; |
| 195 | } |
| 196 | |
| 197 | printAction->setEnabled(markedCount > 0); |
| 198 | printPreviewAction->setEnabled(markedCount > 0); |
| 199 | } |
| 200 | |
| 201 | QMap<QString, StyleItems> MainWindow::currentPageMap() |
| 202 | { |
| 203 | QMap<QString, StyleItems> pageMap; |
| 204 | |
| 205 | for (int row = 0; row < fontTree->topLevelItemCount(); ++row) { |
| 206 | QTreeWidgetItem *familyItem = fontTree->topLevelItem(index: row); |
| 207 | QString family; |
| 208 | |
| 209 | if (familyItem->checkState(column: 0) == Qt::Checked) { |
| 210 | family = familyItem->text(column: 0); |
| 211 | pageMap[family] = StyleItems(); |
| 212 | } |
| 213 | |
| 214 | for (int childRow = 0; childRow < familyItem->childCount(); ++childRow) { |
| 215 | QTreeWidgetItem *styleItem = familyItem->child(index: childRow); |
| 216 | if (styleItem->checkState(column: 0) == Qt::Checked) |
| 217 | pageMap[family].append(t: styleItem); |
| 218 | } |
| 219 | } |
| 220 | |
| 221 | return pageMap; |
| 222 | } |
| 223 | |
| 224 | void MainWindow::on_printAction_triggered() |
| 225 | { |
| 226 | #if defined(QT_PRINTSUPPORT_LIB) && QT_CONFIG(printdialog) |
| 227 | pageMap = currentPageMap(); |
| 228 | |
| 229 | if (pageMap.count() == 0) |
| 230 | return; |
| 231 | |
| 232 | QPrinter printer(QPrinter::HighResolution); |
| 233 | QPrintDialog dialog(&printer, this); |
| 234 | if (dialog.exec() != QDialog::Accepted) |
| 235 | return; |
| 236 | |
| 237 | int from = printer.fromPage(); |
| 238 | int to = printer.toPage(); |
| 239 | if (from <= 0 && to <= 0) |
| 240 | printer.setFromTo(fromPage: 1, toPage: pageMap.keys().count()); |
| 241 | |
| 242 | printDocument(printer: &printer); |
| 243 | #endif |
| 244 | } |
| 245 | |
| 246 | void MainWindow::printDocument(QPrinter *printer) |
| 247 | { |
| 248 | #if defined(QT_PRINTSUPPORT_LIB) && QT_CONFIG(printdialog) |
| 249 | printer->setFromTo(fromPage: 1, toPage: pageMap.count()); |
| 250 | |
| 251 | QProgressDialog progress(tr(s: "Preparing font samples..." ), tr(s: "&Cancel" ), |
| 252 | 0, pageMap.count(), this); |
| 253 | progress.setWindowModality(Qt::ApplicationModal); |
| 254 | progress.setWindowTitle(tr(s: "Font Sampler" )); |
| 255 | progress.setMinimum(printer->fromPage() - 1); |
| 256 | progress.setMaximum(printer->toPage()); |
| 257 | |
| 258 | QPainter painter; |
| 259 | painter.begin(printer); |
| 260 | bool firstPage = true; |
| 261 | |
| 262 | for (int page = printer->fromPage(); page <= printer->toPage(); ++page) { |
| 263 | |
| 264 | if (!firstPage) |
| 265 | printer->newPage(); |
| 266 | |
| 267 | qApp->processEvents(); |
| 268 | if (progress.wasCanceled()) |
| 269 | break; |
| 270 | |
| 271 | printPage(index: page - 1, painter: &painter, printer); |
| 272 | progress.setValue(page); |
| 273 | firstPage = false; |
| 274 | } |
| 275 | |
| 276 | painter.end(); |
| 277 | #endif |
| 278 | } |
| 279 | |
| 280 | void MainWindow::on_printPreviewAction_triggered() |
| 281 | { |
| 282 | #if defined(QT_PRINTSUPPORT_LIB) && QT_CONFIG(printpreviewdialog) |
| 283 | pageMap = currentPageMap(); |
| 284 | |
| 285 | if (pageMap.count() == 0) |
| 286 | return; |
| 287 | |
| 288 | QPrinter printer(QPrinter::HighResolution); |
| 289 | QPrintPreviewDialog preview(&printer, this); |
| 290 | connect(sender: &preview, signal: &QPrintPreviewDialog::paintRequested, |
| 291 | receiver: this, slot: &MainWindow::printDocument); |
| 292 | preview.exec(); |
| 293 | #endif |
| 294 | } |
| 295 | |
| 296 | void MainWindow::printPage(int index, QPainter *painter, QPrinter *printer) |
| 297 | { |
| 298 | #if defined(QT_PRINTSUPPORT_LIB) && QT_CONFIG(printdialog) |
| 299 | const QString family = (pageMap.begin() + index).key(); |
| 300 | const StyleItems items = pageMap.value(akey: family); |
| 301 | |
| 302 | // Find the dimensions of the text on each page. |
| 303 | qreal width = 0.0; |
| 304 | qreal height = 0.0; |
| 305 | for (const QTreeWidgetItem *item : items) { |
| 306 | QString style = item->text(column: 0); |
| 307 | int weight = item->data(column: 0, role: Qt::UserRole).toInt(); |
| 308 | bool italic = item->data(column: 0, role: Qt::UserRole + 1).toBool(); |
| 309 | |
| 310 | // Calculate the maximum width and total height of the text. |
| 311 | for (int size : qAsConst(t&: sampleSizes)) { |
| 312 | QFont font(family, size, weight, italic); |
| 313 | font.setStyleName(style); |
| 314 | font = QFont(font, painter->device()); |
| 315 | QFontMetricsF fontMetrics(font); |
| 316 | QRectF rect = fontMetrics.boundingRect( |
| 317 | string: QString("%1 %2" ).arg(a: family).arg(a: style)); |
| 318 | width = qMax(a: rect.width(), b: width); |
| 319 | height += rect.height(); |
| 320 | } |
| 321 | } |
| 322 | |
| 323 | qreal xScale = printer->pageRect().width() / width; |
| 324 | qreal yScale = printer->pageRect().height() / height; |
| 325 | qreal scale = qMin(a: xScale, b: yScale); |
| 326 | |
| 327 | qreal remainingHeight = printer->pageRect().height()/scale - height; |
| 328 | qreal spaceHeight = (remainingHeight / 4.0) / (items.count() + 1); |
| 329 | qreal interLineHeight = (remainingHeight / 4.0) / (sampleSizes.count() * items.count()); |
| 330 | |
| 331 | painter->save(); |
| 332 | painter->translate(dx: printer->pageRect().width() / 2.0, dy: printer->pageRect().height() / 2.0); |
| 333 | painter->scale(sx: scale, sy: scale); |
| 334 | painter->setBrush(QBrush(Qt::black)); |
| 335 | |
| 336 | qreal x = -width / 2.0; |
| 337 | qreal y = -height / 2.0 - remainingHeight / 4.0 + spaceHeight; |
| 338 | |
| 339 | for (const QTreeWidgetItem *item : items) { |
| 340 | QString style = item->text(column: 0); |
| 341 | int weight = item->data(column: 0, role: Qt::UserRole).toInt(); |
| 342 | bool italic = item->data(column: 0, role: Qt::UserRole + 1).toBool(); |
| 343 | |
| 344 | // Draw each line of text. |
| 345 | for (int size : qAsConst(t&: sampleSizes)) { |
| 346 | QFont font(family, size, weight, italic); |
| 347 | font.setStyleName(style); |
| 348 | font = QFont(font, painter->device()); |
| 349 | QFontMetricsF fontMetrics(font); |
| 350 | QRectF rect = fontMetrics.boundingRect(string: QString("%1 %2" ).arg( |
| 351 | a: font.family()).arg(a: style)); |
| 352 | y += rect.height(); |
| 353 | painter->setFont(font); |
| 354 | painter->drawText(p: QPointF(x, y), s: QString("%1 %2" ).arg(a: family).arg(a: style)); |
| 355 | y += interLineHeight; |
| 356 | } |
| 357 | y += spaceHeight; |
| 358 | } |
| 359 | |
| 360 | painter->restore(); |
| 361 | #endif |
| 362 | } |
| 363 | |