1 /*
2 SPDX-License-Identifier: GPL-2.0-or-later
3 SPDX-FileCopyrightText: 2012 Martin Kuettler <martin.kuettler@gmail.com>
4 */
5
6 #include "renderer.h"
7
8 #include <QUuid>
9 #include <QDebug>
10 #include <QMutex>
11
12 #include <config-cantorlib.h>
13
14 #include <poppler-qt5.h>
15 #ifdef LIBSPECTRE_FOUND
16 #include "libspectre/spectre.h"
17 #endif
18
19
20 using namespace Cantor;
21
22 // We need this, because poppler-qt5 not threadsafe before 0.73.0 and 0.73.0 is too new
23 // and not common widespread in repositories
24 static QMutex popplerMutex;
25
26 class Cantor::RendererPrivate{
27 public:
28 double scale{1};
29 bool useHighRes{false};
30 };
31
Renderer()32 Renderer::Renderer() : d(new RendererPrivate())
33 {
34 }
35
~Renderer()36 Renderer::~Renderer()
37 {
38 delete d;
39 }
40
setScale(qreal scale)41 void Renderer::setScale(qreal scale)
42 {
43 d->scale = scale;
44 }
45
scale()46 qreal Renderer::scale()
47 {
48 return d->scale;
49 }
50
useHighResolution(bool b)51 void Renderer::useHighResolution(bool b)
52 {
53 d->useHighRes = b;
54 }
55
render(QTextDocument * document,Method method,const QUrl & url,const QString & uuid)56 QTextImageFormat Renderer::render(QTextDocument *document, Method method, const QUrl &url, const QString& uuid)
57 {
58 QTextImageFormat format;
59
60 QUrl internal;
61 internal.setScheme(QLatin1String("internal"));
62 internal.setPath(uuid);
63
64 QSizeF s = renderToResource(document, method, url, internal);
65
66 if(s.isValid())
67 {
68 format.setName(internal.url());
69 format.setWidth(s.width());
70 format.setHeight(s.height());
71 }
72
73 return format;
74 }
75
render(QTextDocument * document,const Cantor::LatexRenderer * latex)76 QTextImageFormat Renderer::render(QTextDocument *document, const Cantor::LatexRenderer* latex)
77 {
78 QTextImageFormat format = render(document, Method::EPS, QUrl::fromLocalFile(latex->imagePath()), latex->uuid());
79
80 if (!format.name().isEmpty()) {
81 format.setProperty(CantorFormula, latex->method());
82 format.setProperty(ImagePath, latex->imagePath());
83 format.setProperty(Code, latex->latexCode());
84 }
85
86 return format;
87 }
88
renderToResource(QTextDocument * document,Method method,const QUrl & url,const QUrl & internal)89 QSizeF Renderer::renderToResource(QTextDocument *document, Method method, const QUrl &url, const QUrl& internal)
90 {
91 QSizeF size;
92 QImage img = renderToImage(url, method, &size);
93
94 qDebug() << internal;
95 document->addResource(QTextDocument::ImageResource, internal, QVariant(img) );
96 return size;
97 }
98
epsRenderToImage(const QUrl & url,double scale,bool useHighRes,QSizeF * size,QString * errorReason)99 QImage Renderer::epsRenderToImage(const QUrl& url, double scale, bool useHighRes, QSizeF* size, QString* errorReason)
100 {
101 #ifdef LIBSPECTRE_FOUND
102 SpectreDocument* doc = spectre_document_new();
103 SpectreRenderContext* rc = spectre_render_context_new();
104
105 qDebug() << "rendering eps file: " << url;
106 QByteArray local_file = url.toLocalFile().toUtf8();
107 spectre_document_load(doc, local_file.data());
108
109 bool isEps = spectre_document_is_eps(doc);
110 if (!isEps)
111 {
112 if (errorReason)
113 *errorReason = QString::fromLatin1("Error: spectre document is not eps! It means, that url is invalid");
114 return QImage();
115 }
116
117 int wdoc, hdoc;
118 qreal w, h;
119 double realScale;
120 spectre_document_get_page_size(doc, &wdoc, &hdoc);
121 if(useHighRes) {
122 realScale = 1.2*4.0; //1.2 scaling factor, to make it look nice, 4x for high resolution
123 w = 1.2 * wdoc;
124 h = 1.2 * hdoc;
125 } else {
126 realScale=1.8*scale;
127 w = 1.8 * wdoc;
128 h = 1.8 * hdoc;
129 }
130
131 qDebug()<<"scale: "<<realScale;
132
133 qDebug()<<"dimension: "<<w<<"x"<<h;
134 unsigned char* data;
135 int rowLength;
136
137 spectre_render_context_set_scale(rc, realScale, realScale);
138 spectre_document_render_full( doc, rc, &data, &rowLength);
139
140 QImage img(data, wdoc*realScale, hdoc*realScale, rowLength, QImage::Format_RGB32);
141 spectre_document_free(doc);
142 spectre_render_context_free(rc);
143 img = img.convertToFormat(QImage::Format_ARGB32);
144
145 if (size)
146 *size = QSizeF(w,h);
147 return img;
148 #else
149 if (errorReason)
150 *errorReason = QString::fromLatin1("Render Eps on Cantor without eps support (libspectre)!");
151
152 Q_UNUSED(url);
153 Q_UNUSED(scale);
154 Q_UNUSED(useHighRes);
155 Q_UNUSED(size);
156 return QImage();
157 #endif
158 }
159
pdfRenderToImage(const QUrl & url,double scale,bool highResolution,QSizeF * size,QString * errorReason)160 QImage Renderer::pdfRenderToImage(const QUrl& url, double scale, bool highResolution, QSizeF* size, QString* errorReason)
161 {
162 popplerMutex.lock();
163 Poppler::Document* document = Poppler::Document::load(url.toLocalFile());
164 popplerMutex.unlock();
165 if (document == nullptr)
166 {
167 if (errorReason)
168 *errorReason = QString::fromLatin1("Poppler library have failed to open file % as pdf").arg(url.toLocalFile());
169 return QImage();
170 }
171
172 Poppler::Page* pdfPage = document->page(0);
173 if (pdfPage == nullptr) {
174 if (errorReason)
175 *errorReason = QString::fromLatin1("Poppler library failed to access first page of %1 document").arg(url.toLocalFile());
176
177 delete document;
178 return QImage();
179 }
180
181 QSize pageSize = pdfPage->pageSize();
182
183 double realScale = 1.7 * 1.8;
184 qreal w = 1.7 * pageSize.width();
185 qreal h = 1.7 * pageSize.height();
186 if(highResolution)
187 realScale *= 5;
188 else
189 realScale *= scale;
190
191
192 QImage image = pdfPage->renderToImage(72.0*realScale, 72.0*realScale);
193
194 delete pdfPage;
195 popplerMutex.lock();
196 delete document;
197 popplerMutex.unlock();
198
199 if (image.isNull())
200 {
201 if (errorReason)
202 *errorReason = QString::fromLatin1("Poppler library failed to render pdf %1 to image").arg(url.toLocalFile());
203
204 return image;
205 }
206
207 // Resize with smooth transformation for more beautiful result
208 image = image.convertToFormat(QImage::Format_ARGB32).scaled(image.size()/1.8, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
209
210 if (size)
211 *size = QSizeF(w, h);
212 return image;
213 }
214
215
renderToImage(const QUrl & url,Method method,QSizeF * size)216 QImage Renderer::renderToImage(const QUrl& url, Method method, QSizeF* size)
217 {
218 switch(method)
219 {
220 case Method::PDF:
221 return pdfRenderToImage(url, d->scale, d->useHighRes, size);
222
223 case Method::EPS:
224 return epsRenderToImage(url, d->scale, d->useHighRes, size);
225
226 default:
227 return QImage();
228 }
229 }
230