File indexing completed on 2025-01-12 05:01:59

0001 /*
0002     SPDX-FileCopyrightText: 2003-2007 Craig Drummond <craig@kde.org>
0003     SPDX-License-Identifier: GPL-2.0-or-later
0004 */
0005 
0006 #include "Printer.h"
0007 #include "ActionLabel.h"
0008 #include "Fc.h"
0009 #include "FcEngine.h"
0010 #include "KfiConstants.h"
0011 #include "config-fontinst.h"
0012 #include <KAboutData>
0013 #include <QApplication>
0014 #include <QCloseEvent>
0015 #include <QCommandLineOption>
0016 #include <QCommandLineParser>
0017 #include <QFile>
0018 #include <QFontDatabase>
0019 #include <QFontMetrics>
0020 #include <QFrame>
0021 #include <QGridLayout>
0022 #include <QPainter>
0023 #include <QPrintDialog>
0024 #include <QPrinter>
0025 #include <QProgressBar>
0026 #include <QTextStream>
0027 #include <QWidget>
0028 
0029 #include "config-workspace.h"
0030 
0031 #ifdef HAVE_LOCALE_H
0032 #include <QDialogButtonBox>
0033 #include <QPushButton>
0034 #include <QVBoxLayout>
0035 #include <locale.h>
0036 #endif
0037 #include "CreateParent.h"
0038 
0039 // Enable the following to allow printing of non-installed fonts. Does not seem to work :-(
0040 // #define KFI_PRINT_APP_FONTS
0041 
0042 using namespace KFI;
0043 
0044 static const int constMarginLineBefore = 1;
0045 static const int constMarginLineAfter = 2;
0046 static const int constMarginFont = 4;
0047 
0048 inline bool sufficientSpace(int y, int pageHeight, const QFontMetrics &fm)
0049 {
0050     return (y + constMarginFont + fm.height()) < pageHeight;
0051 }
0052 
0053 static bool sufficientSpace(int y, QPainter *painter, QFont font, const int *sizes, int pageHeight, int size)
0054 {
0055     int titleFontHeight = painter->fontMetrics().height(), required = titleFontHeight + constMarginLineBefore + constMarginLineAfter;
0056 
0057     for (unsigned int s = 0; sizes[s]; ++s) {
0058         font.setPointSize(sizes[s]);
0059         required += QFontMetrics(font, painter->device()).height();
0060         if (sizes[s + 1]) {
0061             required += constMarginFont;
0062         }
0063     }
0064 
0065     if (0 == size) {
0066         font.setPointSize(CFcEngine::constDefaultAlphaSize);
0067         int fontHeight = QFontMetrics(font, painter->device()).height();
0068 
0069         required += (3 * (constMarginFont + fontHeight)) + constMarginLineBefore + constMarginLineAfter;
0070     }
0071     return (y + required) < pageHeight;
0072 }
0073 
0074 static QString usableStr(QFont &font, const QString &str)
0075 {
0076     Q_UNUSED(font)
0077     return str;
0078 }
0079 
0080 static bool hasStr(QFont &font, const QString &str)
0081 {
0082     Q_UNUSED(font)
0083     Q_UNUSED(str)
0084     return true;
0085 }
0086 
0087 static QString previewString(QFont &font, const QString &text, bool onlyDrawChars)
0088 {
0089     Q_UNUSED(font)
0090     Q_UNUSED(onlyDrawChars)
0091     return text;
0092 }
0093 
0094 CPrintThread::CPrintThread(QPrinter *printer, const QList<Misc::TFont> &items, int size, QObject *parent)
0095     : QThread(parent)
0096     , m_printer(printer)
0097     , m_items(items)
0098     , m_size(size)
0099     , m_cancelled(false)
0100 {
0101 }
0102 
0103 CPrintThread::~CPrintThread()
0104 {
0105 }
0106 
0107 void CPrintThread::cancel()
0108 {
0109     m_cancelled = true;
0110 }
0111 
0112 void CPrintThread::run()
0113 {
0114     QPainter painter;
0115     QFont sans("sans", 12, QFont::Bold);
0116     bool changedFontEmbeddingSetting(false);
0117     QString str(CFcEngine(false).getPreviewString());
0118 
0119     if (!m_printer->fontEmbeddingEnabled()) {
0120         m_printer->setFontEmbeddingEnabled(true);
0121         changedFontEmbeddingSetting = true;
0122     }
0123 
0124     m_printer->setResolution(72);
0125     painter.begin(m_printer);
0126 
0127     int pageWidth = painter.device()->width(), pageHeight = painter.device()->height(), y = 0, oneSize[2] = {m_size, 0};
0128     const int *sizes = oneSize;
0129     bool firstFont(true);
0130 
0131     if (0 == m_size) {
0132         sizes = CFcEngine::constScalableSizes;
0133     }
0134 
0135     painter.setClipping(true);
0136     painter.setClipRect(0, 0, pageWidth, pageHeight);
0137 
0138     QList<Misc::TFont>::ConstIterator it(m_items.constBegin()), end(m_items.constEnd());
0139 
0140     for (int i = 0; it != end && !m_cancelled; ++it, ++i) {
0141         QString name(FC::createName((*it).family, (*it).styleInfo));
0142         Q_EMIT progress(i, name);
0143 
0144         unsigned int s = 0;
0145         QFont font;
0146 
0147 #ifdef KFI_PRINT_APP_FONTS
0148         QString family;
0149 
0150         if (-1 != appFont[(*it).family]) {
0151             family = QFontDatabase::applicationFontFamilies(appFont[(*it).family]).first();
0152             font = QFont(family);
0153         }
0154 #else
0155         font = CFcEngine::getQFont((*it).family, (*it).styleInfo, CFcEngine::constDefaultAlphaSize);
0156 #endif
0157         painter.setFont(sans);
0158 
0159         if (!firstFont && !sufficientSpace(y, &painter, font, sizes, pageHeight, m_size)) {
0160             m_printer->newPage();
0161             y = 0;
0162         }
0163         painter.setFont(sans);
0164         y += painter.fontMetrics().height();
0165         painter.drawText(0, y, name);
0166 
0167         y += constMarginLineBefore;
0168         painter.drawLine(0, y, pageWidth, y);
0169         y += constMarginLineAfter;
0170 
0171         bool onlyDrawChars = false;
0172         Qt::TextElideMode em = Qt::LeftToRight == QApplication::layoutDirection() ? Qt::ElideRight : Qt::ElideLeft;
0173 
0174         if (0 == m_size) {
0175             font.setPointSize(CFcEngine::constDefaultAlphaSize);
0176             painter.setFont(font);
0177 
0178             QFontMetrics fm(font, painter.device());
0179             bool lc = hasStr(font, CFcEngine::getLowercaseLetters()), uc = hasStr(font, CFcEngine::getUppercaseLetters());
0180 
0181             onlyDrawChars = !lc && !uc;
0182 
0183             if (lc || uc) {
0184                 y += CFcEngine::constDefaultAlphaSize;
0185             }
0186 
0187             if (lc) {
0188                 painter.drawText(0, y, fm.elidedText(CFcEngine::getLowercaseLetters(), em, pageWidth));
0189                 y += constMarginFont + CFcEngine::constDefaultAlphaSize;
0190             }
0191 
0192             if (uc) {
0193                 painter.drawText(0, y, fm.elidedText(CFcEngine::getUppercaseLetters(), em, pageWidth));
0194                 y += constMarginFont + CFcEngine::constDefaultAlphaSize;
0195             }
0196 
0197             if (lc || uc) {
0198                 QString validPunc(usableStr(font, CFcEngine::getPunctuation()));
0199                 if (validPunc.length() >= (CFcEngine::getPunctuation().length() / 2)) {
0200                     painter.drawText(0, y, fm.elidedText(CFcEngine::getPunctuation(), em, pageWidth));
0201                     y += constMarginFont + constMarginLineBefore;
0202                 }
0203                 painter.drawLine(0, y, pageWidth, y);
0204                 y += constMarginLineAfter;
0205             }
0206         }
0207 
0208         for (; sizes[s]; ++s) {
0209             y += sizes[s];
0210             font.setPointSize(sizes[s]);
0211             painter.setFont(font);
0212 
0213             QFontMetrics fm(font, painter.device());
0214 
0215             if (sufficientSpace(y, pageHeight, fm)) {
0216                 painter.drawText(0, y, fm.elidedText(previewString(font, str, onlyDrawChars), em, pageWidth));
0217                 if (sizes[s + 1]) {
0218                     y += constMarginFont;
0219                 }
0220             } else {
0221                 break;
0222             }
0223         }
0224         y += (s < 1 || sizes[s - 1] < 25 ? 14 : 28);
0225         firstFont = false;
0226     }
0227     Q_EMIT progress(m_items.count(), QString());
0228     painter.end();
0229 
0230     //
0231     // Did we change the users font settings? If so, reset to their previous values...
0232     if (changedFontEmbeddingSetting) {
0233         m_printer->setFontEmbeddingEnabled(false);
0234     }
0235 }
0236 
0237 CPrinter::CPrinter(QWidget *parent)
0238     : QDialog(parent)
0239 {
0240     setWindowTitle(i18n("Print"));
0241 
0242     QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Cancel);
0243     connect(buttonBox, &QDialogButtonBox::rejected, this, &CPrinter::slotCancelClicked);
0244 
0245     QVBoxLayout *mainLayout = new QVBoxLayout;
0246     setLayout(mainLayout);
0247 
0248     QFrame *page = new QFrame(this);
0249     QGridLayout *layout = new QGridLayout(page);
0250     m_statusLabel = new QLabel(page);
0251     m_progress = new QProgressBar(page);
0252     layout->addWidget(m_actionLabel = new CActionLabel(this), 0, 0, 2, 1);
0253     layout->addWidget(m_statusLabel, 0, 1);
0254     layout->addWidget(m_progress, 1, 1);
0255     m_progress->setRange(0, 100);
0256     layout->addItem(new QSpacerItem(0, 0, QSizePolicy::Fixed, QSizePolicy::Expanding), 2, 0);
0257 
0258     mainLayout->addWidget(page);
0259     mainLayout->addWidget(buttonBox);
0260     setMinimumSize(420, 80);
0261 }
0262 
0263 CPrinter::~CPrinter()
0264 {
0265 }
0266 
0267 void CPrinter::print(const QList<Misc::TFont> &items, int size)
0268 {
0269 #ifdef HAVE_LOCALE_H
0270     char *oldLocale = setlocale(LC_NUMERIC, "C");
0271 #endif
0272 
0273     QPrinter printer;
0274     QPrintDialog *dialog = new QPrintDialog(&printer, parentWidget());
0275 
0276     if (dialog->exec()) {
0277         CPrintThread *thread = new CPrintThread(&printer, items, size, this);
0278 
0279         m_progress->setRange(0, items.count());
0280         m_progress->setValue(0);
0281         progress(0, QString());
0282         connect(thread, &CPrintThread::progress, this, &CPrinter::progress);
0283         connect(thread, &QThread::finished, this, &QDialog::accept);
0284         connect(this, &CPrinter::cancelled, thread, &CPrintThread::cancel);
0285         m_actionLabel->startAnimation();
0286         thread->start();
0287         exec();
0288         delete thread;
0289     }
0290 
0291     delete dialog;
0292 
0293 #ifdef HAVE_LOCALE_H
0294     if (oldLocale) {
0295         setlocale(LC_NUMERIC, oldLocale);
0296     }
0297 #endif
0298 }
0299 
0300 void CPrinter::progress(int p, const QString &label)
0301 {
0302     if (!label.isEmpty()) {
0303         m_statusLabel->setText(label);
0304     }
0305     m_progress->setValue(p);
0306 }
0307 
0308 void CPrinter::slotCancelClicked()
0309 {
0310     m_statusLabel->setText(i18n("Canceling…"));
0311     Q_EMIT cancelled();
0312 }
0313 
0314 void CPrinter::closeEvent(QCloseEvent *e)
0315 {
0316     Q_UNUSED(e)
0317     e->ignore();
0318     slotCancelClicked();
0319 }
0320 
0321 int main(int argc, char **argv)
0322 {
0323     QApplication app(argc, argv);
0324 
0325     KLocalizedString::setApplicationDomain(KFI_CATALOGUE);
0326     KAboutData aboutData("kfontprint",
0327                          i18n("Font Printer"),
0328                          WORKSPACE_VERSION_STRING,
0329                          i18n("Simple font printer"),
0330                          KAboutLicense::GPL,
0331                          i18n("(C) Craig Drummond, 2007"));
0332     KAboutData::setApplicationData(aboutData);
0333 
0334     QGuiApplication::setWindowIcon(QIcon::fromTheme("kfontprint"));
0335 
0336     QCommandLineParser parser;
0337     const QCommandLineOption embedOption(QLatin1String("embed"), i18n("Makes the dialog transient for an X app specified by winid"), QLatin1String("winid"));
0338     parser.addOption(embedOption);
0339     const QCommandLineOption sizeOption(QLatin1String("size"), i18n("Size index to print fonts"), QLatin1String("index"));
0340     parser.addOption(sizeOption);
0341     const QCommandLineOption pfontOption(
0342         QLatin1String("pfont"),
0343         i18n("Font to print, specified as \"Family,Style\" where Style is a 24-bit decimal number composed as: <weight><width><slant>"),
0344         QLatin1String("font"));
0345     parser.addOption(pfontOption);
0346     const QCommandLineOption listfileOption(QLatin1String("listfile"), i18n("File containing list of fonts to print"), QLatin1String("file"));
0347     parser.addOption(listfileOption);
0348     const QCommandLineOption deletefileOption(QLatin1String("deletefile"), i18n("Remove file containing list of fonts to print"));
0349     parser.addOption(deletefileOption);
0350 
0351     aboutData.setupCommandLine(&parser);
0352     parser.process(app);
0353     aboutData.processCommandLine(&parser);
0354 
0355     QList<Misc::TFont> fonts;
0356     int size(parser.value(sizeOption).toInt());
0357 
0358     if (size > -1 && size < 256) {
0359         QString listFile(parser.value(listfileOption));
0360 
0361         if (!listFile.isEmpty()) {
0362             QFile f(listFile);
0363 
0364             if (f.open(QIODevice::ReadOnly)) {
0365                 QTextStream str(&f);
0366 
0367                 while (!str.atEnd()) {
0368                     QString family(str.readLine()), style(str.readLine());
0369 
0370                     if (!family.isEmpty() && !style.isEmpty()) {
0371                         fonts.append(Misc::TFont(family, style.toUInt()));
0372                     } else {
0373                         break;
0374                     }
0375                 }
0376                 f.close();
0377             }
0378 
0379             if (parser.isSet(deletefileOption)) {
0380                 ::unlink(listFile.toLocal8Bit().constData());
0381             }
0382         } else {
0383             QStringList fl(parser.values(pfontOption));
0384             QStringList::ConstIterator it(fl.begin()), end(fl.end());
0385 
0386             for (; it != end; ++it) {
0387                 QString f(*it);
0388 
0389                 int commaPos = f.lastIndexOf(',');
0390 
0391                 if (-1 != commaPos) {
0392                     fonts.append(Misc::TFont(f.left(commaPos), f.mid(commaPos + 1).toUInt()));
0393                 }
0394             }
0395         }
0396 
0397         if (!fonts.isEmpty()) {
0398             CPrinter(createParent(parser.value(embedOption).toInt(nullptr, 16))).print(fonts, size);
0399 
0400             return 0;
0401         }
0402     }
0403 
0404     return -1;
0405 }