1 /*
2 
3 Copyright 2013 Alexander Volkov
4 Copyright 2013 Adam Reichold
5 
6 This file is part of qpdfview.
7 
8 qpdfview is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 2 of the License, or
11 (at your option) any later version.
12 
13 qpdfview is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 GNU General Public License for more details.
17 
18 You should have received a copy of the GNU General Public License
19 along with qpdfview.  If not, see <http://www.gnu.org/licenses/>.
20 
21 */
22 
23 #include "psmodel.h"
24 
25 #include <QFile>
26 #include <QFormLayout>
27 #include <qmath.h>
28 #include <QSettings>
29 #include <QSpinBox>
30 
31 #include <libspectre/spectre-document.h>
32 
33 namespace
34 {
35 
36 using namespace qpdfview;
37 using namespace qpdfview::Model;
38 
39 namespace Defaults
40 {
41 
42 const int graphicsAntialiasBits = 4;
43 const int textAntialiasBits = 2;
44 
45 } // Defaults
46 
47 } // anonymous
48 
49 namespace qpdfview
50 {
51 
52 namespace Model
53 {
54 
PsPage(QMutex * mutex,SpectrePage * page,SpectreRenderContext * renderContext)55 PsPage::PsPage(QMutex* mutex, SpectrePage* page, SpectreRenderContext* renderContext) :
56     m_mutex(mutex),
57     m_page(page),
58     m_renderContext(renderContext)
59 {
60 }
61 
~PsPage()62 PsPage::~PsPage()
63 {
64     spectre_page_free(m_page);
65     m_page = 0;
66 }
67 
size() const68 QSizeF PsPage::size() const
69 {
70     QMutexLocker mutexLocker(m_mutex);
71 
72     int w;
73     int h;
74 
75     spectre_page_get_size(m_page, &w, &h);
76 
77     return QSizeF(w, h);
78 }
79 
render(qreal horizontalResolution,qreal verticalResolution,Rotation rotation,QRect boundingRect) const80 QImage PsPage::render(qreal horizontalResolution, qreal verticalResolution, Rotation rotation, QRect boundingRect) const
81 {
82     QMutexLocker mutexLocker(m_mutex);
83 
84     double xscale;
85     double yscale;
86 
87     switch(rotation)
88     {
89     default:
90     case RotateBy0:
91     case RotateBy180:
92         xscale = horizontalResolution / 72.0;
93         yscale = verticalResolution / 72.0;
94         break;
95     case RotateBy90:
96     case RotateBy270:
97         xscale = verticalResolution / 72.0;
98         yscale = horizontalResolution / 72.0;
99         break;
100     }
101 
102     spectre_render_context_set_scale(m_renderContext, xscale, yscale);
103 
104     switch(rotation)
105     {
106     default:
107     case RotateBy0:
108         spectre_render_context_set_rotation(m_renderContext, 0);
109         break;
110     case RotateBy90:
111         spectre_render_context_set_rotation(m_renderContext, 90);
112         break;
113     case RotateBy180:
114         spectre_render_context_set_rotation(m_renderContext, 180);
115         break;
116     case RotateBy270:
117         spectre_render_context_set_rotation(m_renderContext, 270);
118         break;
119     }
120 
121     int w;
122     int h;
123 
124     spectre_page_get_size(m_page, &w, &h);
125 
126     w = qRound(w * xscale);
127     h = qRound(h * yscale);
128 
129     if(rotation == RotateBy90 || rotation == RotateBy270)
130     {
131         qSwap(w, h);
132     }
133 
134     unsigned char* pageData = 0;
135     int rowLength = 0;
136 
137     spectre_page_render(m_page, m_renderContext, &pageData, &rowLength);
138 
139     if (spectre_page_status(m_page) != SPECTRE_STATUS_SUCCESS)
140     {
141         free(pageData);
142         pageData = 0;
143 
144         return QImage();
145     }
146 
147     QImage auxiliaryImage(pageData, rowLength / 4, h, QImage::Format_RGB32);
148     QImage image(boundingRect.isNull() ? auxiliaryImage.copy(0, 0, w, h) : auxiliaryImage.copy(boundingRect));
149 
150     free(pageData);
151     pageData = 0;
152 
153     return image;
154 }
155 
PsDocument(SpectreDocument * document,SpectreRenderContext * renderContext)156 PsDocument::PsDocument(SpectreDocument* document, SpectreRenderContext* renderContext) :
157     m_mutex(),
158     m_document(document),
159     m_renderContext(renderContext)
160 {
161 }
162 
~PsDocument()163 PsDocument::~PsDocument()
164 {
165     spectre_render_context_free(m_renderContext);
166     m_renderContext = 0;
167 
168     spectre_document_free(m_document);
169     m_document = 0;
170 }
171 
numberOfPages() const172 int PsDocument::numberOfPages() const
173 {
174     QMutexLocker mutexLocker(&m_mutex);
175 
176     return spectre_document_get_n_pages(m_document);
177 }
178 
page(int index) const179 Page* PsDocument::page(int index) const
180 {
181     QMutexLocker mutexLocker(&m_mutex);
182 
183     if(SpectrePage* page = spectre_document_get_page(m_document, index))
184     {
185         return new PsPage(&m_mutex, page, m_renderContext);
186     }
187 
188     return 0;
189 }
190 
saveFilter() const191 QStringList PsDocument::saveFilter() const
192 {
193     QMutexLocker mutexLocker(&m_mutex);
194 
195     if(spectre_document_is_eps(m_document))
196     {
197         return QStringList() << QLatin1String("Encapsulated PostScript (*.eps)");
198     }
199     else
200     {
201         return QStringList() << QLatin1String("PostScript (*.ps)");
202     }
203 }
204 
canSave() const205 bool PsDocument::canSave() const
206 {
207     return true;
208 }
209 
save(const QString & filePath,bool withChanges) const210 bool PsDocument::save(const QString& filePath, bool withChanges) const
211 {
212     Q_UNUSED(withChanges)
213 
214     QMutexLocker mutexLocker(&m_mutex);
215 
216     spectre_document_save(m_document, QFile::encodeName(filePath));
217 
218     return spectre_document_status(m_document) == SPECTRE_STATUS_SUCCESS;
219 }
220 
canBePrintedUsingCUPS() const221 bool PsDocument::canBePrintedUsingCUPS() const
222 {
223     return true;
224 }
225 
properties() const226 Properties PsDocument::properties() const
227 {
228     Properties properties;
229 
230     QMutexLocker mutexLocker(&m_mutex);
231 
232     const QString title = QString::fromLocal8Bit(spectre_document_get_title(m_document));
233     const QString createdFor = QString::fromLocal8Bit(spectre_document_get_for(m_document));
234     const QString creator = QString::fromLocal8Bit(spectre_document_get_creator(m_document));
235     const QString creationDate = QString::fromLocal8Bit(spectre_document_get_creation_date(m_document));
236     const QString format = QString::fromLocal8Bit(spectre_document_get_format(m_document));
237     const QString languageLevel = QString::number(spectre_document_get_language_level(m_document));
238 
239     properties.push_back(qMakePair(tr("Title"), title));
240     properties.push_back(qMakePair(tr("Created for"), createdFor));
241     properties.push_back(qMakePair(tr("Creator"), creator));
242     properties.push_back(qMakePair(tr("Creation date"), creationDate));
243     properties.push_back(qMakePair(tr("Format"), format));
244     properties.push_back(qMakePair(tr("Language level"), languageLevel));
245 
246     return properties;
247 }
248 
249 } // Model
250 
PsSettingsWidget(QSettings * settings,QWidget * parent)251 PsSettingsWidget::PsSettingsWidget(QSettings* settings, QWidget* parent) : SettingsWidget(parent),
252     m_settings(settings)
253 {
254     m_layout = new QFormLayout(this);
255 
256     // graphics antialias bits
257 
258     m_graphicsAntialiasBitsSpinBox = new QSpinBox(this);
259     m_graphicsAntialiasBitsSpinBox->setRange(1, 4);
260     m_graphicsAntialiasBitsSpinBox->setValue(m_settings->value("graphicsAntialiasBits", Defaults::graphicsAntialiasBits).toInt());
261 
262     m_layout->addRow(tr("Graphics antialias bits:"), m_graphicsAntialiasBitsSpinBox);
263 
264     // text antialias bits
265 
266     m_textAntialiasBitsSpinBox = new QSpinBox(this);
267     m_textAntialiasBitsSpinBox->setRange(1, 2);
268     m_textAntialiasBitsSpinBox->setValue(m_settings->value("textAntialiasBits", Defaults::textAntialiasBits).toInt());
269 
270     m_layout->addRow(tr("Text antialias bits:"), m_textAntialiasBitsSpinBox);
271 }
272 
accept()273 void PsSettingsWidget::accept()
274 {
275     m_settings->setValue("graphicsAntialiasBits", m_graphicsAntialiasBitsSpinBox->value());
276     m_settings->setValue("textAntialiasBits", m_textAntialiasBitsSpinBox->value());
277 }
278 
reset()279 void PsSettingsWidget::reset()
280 {
281     m_graphicsAntialiasBitsSpinBox->setValue(Defaults::graphicsAntialiasBits);
282     m_textAntialiasBitsSpinBox->setValue(Defaults::textAntialiasBits);
283 }
284 
PsPlugin(QObject * parent)285 PsPlugin::PsPlugin(QObject* parent) : QObject(parent)
286 {
287     setObjectName("PsPlugin");
288 
289     m_settings = new QSettings("qpdfview", "ps-plugin", this);
290 }
291 
loadDocument(const QString & filePath) const292 Model::Document* PsPlugin::loadDocument(const QString& filePath) const
293 {
294     SpectreDocument* document = spectre_document_new();
295 
296     spectre_document_load(document, QFile::encodeName(filePath));
297 
298     if (spectre_document_status(document) != SPECTRE_STATUS_SUCCESS)
299     {
300         spectre_document_free(document);
301 
302         return 0;
303     }
304 
305     SpectreRenderContext* renderContext = spectre_render_context_new();
306 
307     spectre_render_context_set_antialias_bits(renderContext,
308                                               m_settings->value("graphicsAntialiasBits", Defaults::graphicsAntialiasBits).toInt(),
309                                               m_settings->value("textAntialiasBits", Defaults::textAntialiasBits).toInt());
310 
311     return new Model::PsDocument(document, renderContext);
312 }
313 
createSettingsWidget(QWidget * parent) const314 SettingsWidget* PsPlugin::createSettingsWidget(QWidget* parent) const
315 {
316     return new PsSettingsWidget(m_settings, parent);
317 }
318 
319 } // qpdfview
320 
321 #if QT_VERSION < QT_VERSION_CHECK(5,0,0)
322 
323 Q_EXPORT_PLUGIN2(qpdfview_ps, qpdfview::PsPlugin)
324 
325 #endif // QT_VERSION
326