File indexing completed on 2024-05-12 16:06:48

0001 /*
0002     SPDX-FileCopyrightText: 2007-2008 Albert Astals Cid <aacid@kde.org>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "generator_ghostview.h"
0008 
0009 #include <math.h>
0010 
0011 #include <QFile>
0012 #include <QPainter>
0013 #include <QPixmap>
0014 #include <QPrinter>
0015 #include <QSize>
0016 
0017 #include <KAboutData>
0018 #include <KConfigDialog>
0019 #include <KLocalizedString>
0020 #include <QDebug>
0021 #include <QDir>
0022 #include <QMimeDatabase>
0023 #include <QMimeType>
0024 #include <QTemporaryFile>
0025 
0026 #include <core/document.h>
0027 #include <core/fileprinter.h>
0028 #include <core/page.h>
0029 #include <core/utils.h>
0030 
0031 #include "gssettings.h"
0032 #include "ui_gssettingswidget.h"
0033 
0034 #include "rendererthread.h"
0035 #include "spectre_debug.h"
0036 
0037 OKULAR_EXPORT_PLUGIN(GSGenerator, "libokularGenerator_ghostview.json")
0038 
0039 GSGenerator::GSGenerator(QObject *parent, const QVariantList &args)
0040     : Okular::Generator(parent, args)
0041     , m_internalDocument(nullptr)
0042     , m_request(nullptr)
0043 {
0044     setFeature(PrintPostscript);
0045     setFeature(PrintToFile);
0046 
0047     GSRendererThread *renderer = GSRendererThread::getCreateRenderer();
0048     if (!renderer->isRunning()) {
0049         renderer->start();
0050     }
0051     connect(renderer, &GSRendererThread::imageDone, this, &GSGenerator::slotImageGenerated, Qt::QueuedConnection);
0052 }
0053 
0054 GSGenerator::~GSGenerator()
0055 {
0056 }
0057 
0058 bool GSGenerator::reparseConfig()
0059 {
0060     bool changed = false;
0061     if (m_internalDocument) {
0062 #define SET_HINT(hintname, hintdefvalue, hintvar)                                                                                                                                                                                              \
0063     {                                                                                                                                                                                                                                          \
0064         bool newhint = documentMetaData(hintname, hintdefvalue).toBool();                                                                                                                                                                      \
0065         if (newhint != cache_##hintvar) {                                                                                                                                                                                                      \
0066             cache_##hintvar = newhint;                                                                                                                                                                                                         \
0067             changed = true;                                                                                                                                                                                                                    \
0068         }                                                                                                                                                                                                                                      \
0069     }
0070         SET_HINT(GraphicsAntialiasMetaData, true, AAgfx)
0071         SET_HINT(TextAntialiasMetaData, true, AAtext)
0072 #undef SET_HINT
0073     }
0074     return changed;
0075 }
0076 
0077 void GSGenerator::addPages(KConfigDialog *dlg)
0078 {
0079     Ui_GSSettingsWidget gsw;
0080     QWidget *w = new QWidget(dlg);
0081     gsw.setupUi(w);
0082     dlg->addPage(w, GSSettings::self(), i18n("Ghostscript"), QStringLiteral("okular-gv"), i18n("Ghostscript Backend Configuration"));
0083 }
0084 
0085 Okular::Document::PrintError GSGenerator::print(QPrinter &printer)
0086 {
0087     // Create tempfile to write to
0088     QTemporaryFile tf(QDir::tempPath() + QLatin1String("/okular_XXXXXX.ps"));
0089 
0090     // Get list of pages to print
0091     QList<int> pageList = Okular::FilePrinter::pageList(printer, spectre_document_get_n_pages(m_internalDocument), document()->currentPage() + 1, document()->bookmarkedPageList());
0092 
0093     // Default to Postscript export, but if printing to PDF use that instead
0094     SpectreExporterFormat exportFormat = SPECTRE_EXPORTER_FORMAT_PS;
0095     if (printer.outputFileName().right(3) == QLatin1String("pdf")) {
0096         exportFormat = SPECTRE_EXPORTER_FORMAT_PDF;
0097         tf.setFileTemplate(QDir::tempPath() + QLatin1String("/okular_XXXXXX.pdf"));
0098     }
0099 
0100     if (!tf.open()) {
0101         return Okular::Document::TemporaryFileOpenPrintError;
0102     }
0103 
0104     SpectreExporter *exporter = spectre_exporter_new(m_internalDocument, exportFormat);
0105     SpectreStatus exportStatus = spectre_exporter_begin(exporter, tf.fileName().toLatin1().constData());
0106 
0107     int i = 0;
0108     while (i < pageList.count() && exportStatus == SPECTRE_STATUS_SUCCESS) {
0109         exportStatus = spectre_exporter_do_page(exporter, pageList.at(i) - 1);
0110         i++;
0111     }
0112 
0113     SpectreStatus endStatus = SPECTRE_STATUS_EXPORTER_ERROR;
0114     if (exportStatus == SPECTRE_STATUS_SUCCESS) {
0115         endStatus = spectre_exporter_end(exporter);
0116     }
0117 
0118     spectre_exporter_free(exporter);
0119 
0120     const QString printFileName = tf.fileName();
0121     tf.close();
0122 
0123     if (exportStatus == SPECTRE_STATUS_SUCCESS && endStatus == SPECTRE_STATUS_SUCCESS) {
0124         tf.setAutoRemove(false);
0125         return Okular::FilePrinter::printFile(printer, printFileName, document()->orientation(), Okular::FilePrinter::SystemDeletesFiles, Okular::FilePrinter::ApplicationSelectsPages, document()->bookmarkedPageRange());
0126     }
0127 
0128     return Okular::Document::UnknownPrintError;
0129 }
0130 
0131 bool GSGenerator::loadDocument(const QString &fileName, QVector<Okular::Page *> &pagesVector)
0132 {
0133     cache_AAtext = documentMetaData(TextAntialiasMetaData, true).toBool();
0134     cache_AAgfx = documentMetaData(GraphicsAntialiasMetaData, true).toBool();
0135 
0136     m_internalDocument = spectre_document_new();
0137     spectre_document_load(m_internalDocument, QFile::encodeName(fileName).constData());
0138     const SpectreStatus loadStatus = spectre_document_status(m_internalDocument);
0139     if (loadStatus != SPECTRE_STATUS_SUCCESS) {
0140         qCDebug(OkularSpectreDebug) << "ERR:" << spectre_status_to_string(loadStatus);
0141         spectre_document_free(m_internalDocument);
0142         m_internalDocument = nullptr;
0143         return false;
0144     }
0145     pagesVector.resize(spectre_document_get_n_pages(m_internalDocument));
0146     qCDebug(OkularSpectreDebug) << "Page count:" << pagesVector.count();
0147     return loadPages(pagesVector);
0148 }
0149 
0150 bool GSGenerator::doCloseDocument()
0151 {
0152     spectre_document_free(m_internalDocument);
0153     m_internalDocument = nullptr;
0154 
0155     return true;
0156 }
0157 
0158 void GSGenerator::slotImageGenerated(QImage *img, Okular::PixmapRequest *request)
0159 {
0160     // This can happen as GSInterpreterCMD is a singleton and on creation signals all the slots
0161     // of all the generators attached to it
0162     if (request != m_request) {
0163         return;
0164     }
0165 
0166     if (!request->page()->isBoundingBoxKnown()) {
0167         updatePageBoundingBox(request->page()->number(), Okular::Utils::imageBoundingBox(img));
0168     }
0169 
0170     m_request = nullptr;
0171     QPixmap *pix = new QPixmap(QPixmap::fromImage(*img));
0172     delete img;
0173     request->page()->setPixmap(request->observer(), pix);
0174     signalPixmapRequestDone(request);
0175 }
0176 
0177 bool GSGenerator::loadPages(QVector<Okular::Page *> &pagesVector)
0178 {
0179     for (uint i = 0; i < spectre_document_get_n_pages(m_internalDocument); i++) {
0180         SpectrePage *page;
0181         int width = 0, height = 0;
0182         SpectreOrientation pageOrientation = SPECTRE_ORIENTATION_PORTRAIT;
0183         page = spectre_document_get_page(m_internalDocument, i);
0184         if (spectre_document_status(m_internalDocument)) {
0185             qCDebug(OkularSpectreDebug) << "Error getting page" << i << spectre_status_to_string(spectre_document_status(m_internalDocument));
0186         } else {
0187             spectre_page_get_size(page, &width, &height);
0188             pageOrientation = spectre_page_get_orientation(page);
0189         }
0190         spectre_page_free(page);
0191         if (pageOrientation % 2 == 1) {
0192             std::swap(width, height);
0193         }
0194         pagesVector[i] = new Okular::Page(i, width, height, orientation(pageOrientation));
0195     }
0196     return pagesVector.count() > 0;
0197 }
0198 
0199 void GSGenerator::generatePixmap(Okular::PixmapRequest *req)
0200 {
0201     qCDebug(OkularSpectreDebug) << "receiving" << *req;
0202 
0203     SpectrePage *page = spectre_document_get_page(m_internalDocument, req->pageNumber());
0204 
0205     GSRendererThread *renderer = GSRendererThread::getCreateRenderer();
0206 
0207     GSRendererThreadRequest gsreq(this);
0208     gsreq.spectrePage = page;
0209     gsreq.platformFonts = GSSettings::platformFonts();
0210     int graphicsAA = 1;
0211     int textAA = 1;
0212     if (cache_AAgfx) {
0213         graphicsAA = 4;
0214     }
0215     if (cache_AAtext) {
0216         textAA = 4;
0217     }
0218     gsreq.textAAbits = textAA;
0219     gsreq.graphicsAAbits = graphicsAA;
0220 
0221     gsreq.orientation = req->page()->orientation();
0222     if (req->page()->rotation() == Okular::Rotation90 || req->page()->rotation() == Okular::Rotation270) {
0223         gsreq.magnify = qMax((double)req->height() / req->page()->width(), (double)req->width() / req->page()->height());
0224     } else {
0225         gsreq.magnify = qMax((double)req->width() / req->page()->width(), (double)req->height() / req->page()->height());
0226     }
0227     gsreq.request = req;
0228     m_request = req;
0229     renderer->addRequest(gsreq);
0230 }
0231 
0232 bool GSGenerator::canGeneratePixmap() const
0233 {
0234     return !m_request;
0235 }
0236 
0237 Okular::DocumentInfo GSGenerator::generateDocumentInfo(const QSet<Okular::DocumentInfo::Key> &keys) const
0238 {
0239     Okular::DocumentInfo docInfo;
0240     if (keys.contains(Okular::DocumentInfo::Title)) {
0241         docInfo.set(Okular::DocumentInfo::Title, QString::fromUtf8(spectre_document_get_title(m_internalDocument)));
0242     }
0243     if (keys.contains(Okular::DocumentInfo::Author)) {
0244         docInfo.set(Okular::DocumentInfo::Author, QString::fromUtf8(spectre_document_get_for(m_internalDocument)));
0245     }
0246     if (keys.contains(Okular::DocumentInfo::Creator)) {
0247         docInfo.set(Okular::DocumentInfo::Creator, QString::fromUtf8(spectre_document_get_creator(m_internalDocument)));
0248     }
0249     if (keys.contains(Okular::DocumentInfo::CreationDate)) {
0250         docInfo.set(Okular::DocumentInfo::CreationDate, QString::fromUtf8(spectre_document_get_creation_date(m_internalDocument)));
0251     }
0252     if (keys.contains(Okular::DocumentInfo::CustomKeys)) {
0253         docInfo.set(QStringLiteral("dscversion"), QString::fromUtf8(spectre_document_get_format(m_internalDocument)), i18n("Document version"));
0254     }
0255 
0256     if (keys.contains(Okular::DocumentInfo::MimeType)) {
0257         int languageLevel = spectre_document_get_language_level(m_internalDocument);
0258         if (languageLevel > 0) {
0259             docInfo.set(QStringLiteral("langlevel"), QString::number(languageLevel), i18n("Language Level"));
0260         }
0261         if (spectre_document_is_eps(m_internalDocument)) {
0262             docInfo.set(Okular::DocumentInfo::MimeType, QStringLiteral("image/x-eps"));
0263         } else {
0264             docInfo.set(Okular::DocumentInfo::MimeType, QStringLiteral("application/postscript"));
0265         }
0266     }
0267 
0268     if (keys.contains(Okular::DocumentInfo::Pages)) {
0269         docInfo.set(Okular::DocumentInfo::Pages, QString::number(spectre_document_get_n_pages(m_internalDocument)));
0270     }
0271 
0272     return docInfo;
0273 }
0274 
0275 Okular::Rotation GSGenerator::orientation(SpectreOrientation pageOrientation) const
0276 {
0277     switch (pageOrientation) {
0278     case SPECTRE_ORIENTATION_PORTRAIT:
0279         return Okular::Rotation0;
0280     case SPECTRE_ORIENTATION_LANDSCAPE:
0281         return Okular::Rotation90;
0282     case SPECTRE_ORIENTATION_REVERSE_PORTRAIT:
0283         return Okular::Rotation180;
0284     case SPECTRE_ORIENTATION_REVERSE_LANDSCAPE:
0285         return Okular::Rotation270;
0286     }
0287     // get rid of warnings, should never happen
0288     return Okular::Rotation0;
0289 }
0290 
0291 QVariant GSGenerator::metaData(const QString &key, const QVariant &option) const
0292 {
0293     Q_UNUSED(option)
0294     if (key == QLatin1String("DocumentTitle")) {
0295         const char *title = spectre_document_get_title(m_internalDocument);
0296         if (title) {
0297             return QString::fromLatin1(title);
0298         }
0299     }
0300     return QVariant();
0301 }
0302 
0303 #include "generator_ghostview.moc"