File indexing completed on 2024-04-28 11:20:48

0001 /*
0002     SPDX-License-Identifier: GPL-2.0-or-later
0003     SPDX-FileCopyrightText: 2012 Martin Kuettler <martin.kuettler@gmail.com>
0004 */
0005 
0006 #include "renderer.h"
0007 
0008 #include <QUuid>
0009 #include <QDebug>
0010 #include <QMutex>
0011 
0012 #include <config-cantorlib.h>
0013 
0014 #include <poppler-qt5.h>
0015 #ifdef LIBSPECTRE_FOUND
0016   #include "libspectre/spectre.h"
0017 #endif
0018 
0019 
0020 using namespace Cantor;
0021 
0022 // We need this, because poppler-qt5 not threadsafe before 0.73.0 and 0.73.0 is too new
0023 // and not common widespread in repositories
0024 static QMutex popplerMutex;
0025 
0026 class Cantor::RendererPrivate{
0027   public:
0028     double scale{1};
0029     bool useHighRes{false};
0030 };
0031 
0032 Renderer::Renderer() : d(new RendererPrivate())
0033 {
0034 }
0035 
0036 Renderer::~Renderer()
0037 {
0038     delete d;
0039 }
0040 
0041 void Renderer::setScale(qreal scale)
0042 {
0043     d->scale = scale;
0044 }
0045 
0046 qreal Renderer::scale()
0047 {
0048     return d->scale;
0049 }
0050 
0051 void Renderer::useHighResolution(bool b)
0052 {
0053     d->useHighRes = b;
0054 }
0055 
0056 QTextImageFormat Renderer::render(QTextDocument *document, Method method, const QUrl &url, const QString& uuid)
0057 {
0058     QTextImageFormat format;
0059 
0060     QUrl internal;
0061     internal.setScheme(QLatin1String("internal"));
0062     internal.setPath(uuid);
0063 
0064     QSizeF s = renderToResource(document, method, url, internal);
0065 
0066     if(s.isValid())
0067     {
0068         format.setName(internal.url());
0069         format.setWidth(s.width());
0070         format.setHeight(s.height());
0071     }
0072 
0073     return format;
0074 }
0075 
0076 QTextImageFormat Renderer::render(QTextDocument *document, const Cantor::LatexRenderer* latex)
0077 {
0078     QTextImageFormat format = render(document, Method::EPS, QUrl::fromLocalFile(latex->imagePath()), latex->uuid());
0079 
0080     if (!format.name().isEmpty()) {
0081         format.setProperty(CantorFormula, latex->method());
0082         format.setProperty(ImagePath, latex->imagePath());
0083         format.setProperty(Code, latex->latexCode());
0084     }
0085 
0086     return format;
0087 }
0088 
0089 QSizeF Renderer::renderToResource(QTextDocument *document, Method method, const QUrl &url, const QUrl& internal)
0090 {
0091     QSizeF size;
0092     QImage img = renderToImage(url, method, &size);
0093 
0094     qDebug() << internal;
0095     document->addResource(QTextDocument::ImageResource, internal, QVariant(img) );
0096     return size;
0097 }
0098 
0099 QImage Renderer::epsRenderToImage(const QUrl& url, double scale, bool useHighRes, QSizeF* size, QString* errorReason)
0100 {
0101 #ifdef LIBSPECTRE_FOUND
0102     SpectreDocument* doc = spectre_document_new();
0103     SpectreRenderContext* rc = spectre_render_context_new();
0104 
0105     qDebug() << "rendering eps file: " << url;
0106     QByteArray local_file = url.toLocalFile().toUtf8();
0107     spectre_document_load(doc, local_file.data());
0108 
0109     bool isEps = spectre_document_is_eps(doc);
0110     if (!isEps)
0111     {
0112         if (errorReason)
0113             *errorReason = QString::fromLatin1("Error: spectre document is not eps! It means, that url is invalid");
0114         return QImage();
0115     }
0116 
0117     int wdoc, hdoc;
0118     qreal w, h;
0119     double realScale;
0120     spectre_document_get_page_size(doc, &wdoc, &hdoc);
0121     if(useHighRes) {
0122         realScale = 1.2*4.0; //1.2 scaling factor, to make it look nice, 4x for high resolution
0123         w = 1.2 * wdoc;
0124         h = 1.2 * hdoc;
0125     } else {
0126         realScale=1.8*scale;
0127         w = 1.8 * wdoc;
0128         h = 1.8 * hdoc;
0129     }
0130 
0131     qDebug()<<"scale: "<<realScale;
0132 
0133     qDebug()<<"dimension: "<<w<<"x"<<h;
0134     unsigned char* data;
0135     int rowLength;
0136 
0137     spectre_render_context_set_scale(rc, realScale, realScale);
0138     spectre_document_render_full( doc, rc, &data, &rowLength);
0139 
0140     QImage img(data, wdoc*realScale, hdoc*realScale, rowLength, QImage::Format_RGB32);
0141     spectre_document_free(doc);
0142     spectre_render_context_free(rc);
0143     img = img.convertToFormat(QImage::Format_ARGB32);
0144 
0145     if (size)
0146         *size = QSizeF(w,h);
0147     return img;
0148 #else
0149     if (errorReason)
0150         *errorReason = QString::fromLatin1("Render Eps on Cantor without eps support (libspectre)!");
0151 
0152     Q_UNUSED(url);
0153     Q_UNUSED(scale);
0154     Q_UNUSED(useHighRes);
0155     Q_UNUSED(size);
0156     return QImage();
0157 #endif
0158 }
0159 
0160 QImage Renderer::pdfRenderToImage(const QUrl& url, double scale, bool highResolution, QSizeF* size, QString* errorReason)
0161 {
0162     popplerMutex.lock();
0163     Poppler::Document* document = Poppler::Document::load(url.toLocalFile());
0164     popplerMutex.unlock();
0165     if (document == nullptr)
0166     {
0167         if (errorReason)
0168             *errorReason = QString::fromLatin1("Poppler library have failed to open file % as pdf").arg(url.toLocalFile());
0169         return QImage();
0170     }
0171 
0172     Poppler::Page* pdfPage = document->page(0);
0173     if (pdfPage == nullptr) {
0174         if (errorReason)
0175             *errorReason = QString::fromLatin1("Poppler library failed to access first page of %1 document").arg(url.toLocalFile());
0176 
0177         delete document;
0178         return QImage();
0179     }
0180 
0181     QSize pageSize = pdfPage->pageSize();
0182 
0183     double realScale = 1.7 * 1.8;
0184     qreal w = 1.7 * pageSize.width();
0185     qreal h = 1.7 * pageSize.height();
0186     if(highResolution)
0187         realScale *= 5;
0188     else
0189         realScale *= scale;
0190 
0191 
0192     QImage image = pdfPage->renderToImage(72.0*realScale, 72.0*realScale);
0193 
0194     delete pdfPage;
0195     popplerMutex.lock();
0196     delete document;
0197     popplerMutex.unlock();
0198 
0199     if (image.isNull())
0200     {
0201         if (errorReason)
0202             *errorReason = QString::fromLatin1("Poppler library failed to render pdf %1 to image").arg(url.toLocalFile());
0203 
0204         return image;
0205     }
0206 
0207     // Resize with smooth transformation for more beautiful result
0208     image = image.convertToFormat(QImage::Format_ARGB32).scaled(image.size()/1.8, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
0209 
0210     if (size)
0211         *size = QSizeF(w, h);
0212     return image;
0213 }
0214 
0215 
0216 QImage Renderer::renderToImage(const QUrl& url, Method method, QSizeF* size)
0217 {
0218     switch(method)
0219     {
0220         case Method::PDF:
0221             return pdfRenderToImage(url, d->scale, d->useHighRes, size);
0222 
0223         case Method::EPS:
0224             return epsRenderToImage(url, d->scale, d->useHighRes, size);
0225 
0226         default:
0227             return QImage();
0228     }
0229 }