1 /*
2 For general Scribus (>=1.3.2) copyright and licensing information please refer
3 to the COPYING file provided with the program. Following this notice may exist
4 a copyright and/or license notice that predates the release of Scribus 1.3.2
5 for which a new license (GPL+exception) is in place.
6 */
7 #include "objimageexport.h"
8
9 #include <QImageWriter>
10 #include <structmember.h>
11 #include <QFileInfo>
12 #include <vector>
13
14 #include "cmdutil.h"
15 #include "scpage.h"
16 #include "scribuscore.h"
17 #include "scribusdoc.h"
18 #include "scribusview.h"
19
20 typedef struct
21 {
22 PyObject_HEAD
23 PyObject *name; // string - filename
24 PyObject *type; // string - image type (PNG, JPEG etc.)
25 PyObject *allTypes; // list - available types
26 int dpi; // DPI of the bitmap
27 int scale; // how is bitmap scaled 100 = 100%
28 int quality; // quality/compression <1; 100>
29 int transparentBkgnd; // background transparency
30 } ImageExport;
31
ImageExport_dealloc(ImageExport * self)32 static void ImageExport_dealloc(ImageExport* self)
33 {
34 Py_XDECREF(self->name);
35 Py_XDECREF(self->type);
36 Py_XDECREF(self->allTypes);
37 Py_TYPE(self)->tp_free((PyObject *)self);
38 }
39
ImageExport_new(PyTypeObject * type,PyObject *,PyObject *)40 static PyObject * ImageExport_new(PyTypeObject *type, PyObject * /*args*/, PyObject * /*kwds*/)
41 {
42 if (!checkHaveDocument())
43 return nullptr;
44
45 ImageExport *self;
46 self = (ImageExport *)type->tp_alloc(type, 0);
47 if (self != nullptr) {
48 self->name = PyUnicode_FromString("ImageExport.png");
49 self->type = PyUnicode_FromString("PNG");
50 self->allTypes = PyList_New(0);
51 self->dpi = 72;
52 self->scale = 100;
53 self->quality = 100;
54 self->transparentBkgnd = 0;
55 }
56 return (PyObject *) self;
57 }
58
ImageExport_init(ImageExport *,PyObject *,PyObject *)59 static int ImageExport_init(ImageExport * /*self*/, PyObject * /*args*/, PyObject * /*kwds*/)
60 {
61 return 0;
62 }
63
64 static PyMemberDef ImageExport_members[] = {
65 {const_cast<char*>("dpi"), T_INT, offsetof(ImageExport, dpi), 0, imgexp_dpi__doc__},
66 {const_cast<char*>("scale"), T_INT, offsetof(ImageExport, scale), 0, imgexp_scale__doc__},
67 {const_cast<char*>("quality"), T_INT, offsetof(ImageExport, quality), 0, imgexp_quality__doc__},
68 {const_cast<char*>("transparentBkgnd"), T_INT, offsetof(ImageExport, transparentBkgnd), 0, imgexp_transparentBkgnd__doc__},
69 {nullptr, 0, 0, 0, nullptr} // sentinel
70 };
71
ImageExport_getName(ImageExport * self,void *)72 static PyObject *ImageExport_getName(ImageExport *self, void * /*closure*/)
73 {
74 Py_INCREF(self->name);
75 return self->name;
76 }
77
ImageExport_setName(ImageExport * self,PyObject * value,void *)78 static int ImageExport_setName(ImageExport *self, PyObject *value, void * /*closure*/)
79 {
80 if (!PyUnicode_Check(value)) {
81 PyErr_SetString(PyExc_TypeError, QObject::tr("The filename must be a string.", "python error").toLocal8Bit().constData());
82 return -1;
83 }
84 if (PyUnicode_GET_LENGTH(value) < 1)
85 {
86 PyErr_SetString(PyExc_TypeError, QObject::tr("The filename should not be empty string.", "python error").toLocal8Bit().constData());
87 return -1;
88 }
89 Py_DECREF(self->name);
90 Py_INCREF(value);
91 self->name = value;
92 return 0;
93 }
94
ImageExport_getType(ImageExport * self,void *)95 static PyObject *ImageExport_getType(ImageExport *self, void * /*closure*/)
96 {
97 Py_INCREF(self->type);
98 return self->type;
99 }
100
ImageExport_setType(ImageExport * self,PyObject * value,void *)101 static int ImageExport_setType(ImageExport *self, PyObject *value, void * /*closure*/)
102 {
103 if (value == nullptr) {
104 PyErr_SetString(PyExc_TypeError, QObject::tr("Cannot delete image type settings.", "python error").toLocal8Bit().constData());
105 return -1;
106 }
107 if (!PyUnicode_Check(value)) {
108 PyErr_SetString(PyExc_TypeError, QObject::tr("The image type must be a string.", "python error").toLocal8Bit().constData());
109 return -1;
110 }
111 Py_DECREF(self->type);
112 Py_INCREF(value);
113 self->type = value;
114 return 0;
115 }
116
ImageExport_getAllTypes(ImageExport *,void *)117 static PyObject *ImageExport_getAllTypes(ImageExport * /*self*/, void * /*closure*/)
118 {
119 PyObject *l;
120 int pos = 0;
121 QList<QByteArray> list = QImageWriter::supportedImageFormats();
122 l = PyList_New(list.count());
123 for (QList<QByteArray>::Iterator it = list.begin(); it != list.end(); ++it)
124 {
125 PyList_SetItem(l, pos, PyUnicode_FromString(QString((*it)).toLatin1().constData()));
126 ++pos;
127 }
128 return l;
129 }
130
ImageExport_setAllTypes(ImageExport *,PyObject *,void *)131 static int ImageExport_setAllTypes(ImageExport * /*self*/, PyObject * /*value*/, void * /*closure*/)
132 {
133 PyErr_SetString(PyExc_ValueError, QObject::tr("'allTypes' attribute is READ-ONLY", "python error").toLocal8Bit().constData());
134 return -1;
135 }
136
137 static PyGetSetDef ImageExport_getseters [] = {
138 {const_cast<char*>("name"), (getter)ImageExport_getName, (setter)ImageExport_setName, imgexp_filename__doc__, nullptr},
139 {const_cast<char*>("type"), (getter)ImageExport_getType, (setter)ImageExport_setType, imgexp_type__doc__, nullptr},
140 {const_cast<char*>("allTypes"), (getter)ImageExport_getAllTypes, (setter)ImageExport_setAllTypes, imgexp_alltypes__doc__, nullptr},
141 {nullptr, nullptr, nullptr, nullptr, nullptr} // sentinel
142 };
143
ImageExport_save(ImageExport * self)144 static PyObject *ImageExport_save(ImageExport *self)
145 {
146 if (!checkHaveDocument())
147 return nullptr;
148 ScribusDoc* doc = ScCore->primaryMainWindow()->doc;
149 ScribusView*view = ScCore->primaryMainWindow()->view;
150
151 /* a little magic here - I need to compute the "maxGr" value...
152 * We need to know the right size of the page for landscape,
153 * portrait and user defined sizes.
154 */
155 double pixmapSize = (doc->pageHeight() > doc->pageWidth()) ? doc->pageHeight() : doc->pageWidth();
156 PageToPixmapFlags flags = Pixmap_DrawBackground;
157 if (self->transparentBkgnd)
158 flags &= ~Pixmap_DrawBackground;
159 QImage im = view->PageToPixmap(doc->currentPage()->pageNr(), qRound(pixmapSize * self->scale * (self->dpi / 72.0) / 100.0), flags);
160 int dpi = qRound(100.0 / 2.54 * self->dpi);
161 im.setDotsPerMeterY(dpi);
162 im.setDotsPerMeterX(dpi);
163
164 QString imgFileName = PyUnicode_asQString(self->name);
165 if (!im.save(imgFileName, PyUnicode_AsUTF8(self->type)))
166 {
167 PyErr_SetString(ScribusException, QObject::tr("Failed to export image", "python error").toLocal8Bit().constData());
168 return nullptr;
169 }
170 // Py_INCREF(Py_True); // return True not None for backward compat
171 // return Py_True;
172 // Py_RETURN_TRUE;
173 return PyBool_FromLong(static_cast<long>(true));
174 }
175
ImageExport_saveAs(ImageExport * self,PyObject * args)176 static PyObject *ImageExport_saveAs(ImageExport *self, PyObject *args)
177 {
178 char* value;
179 if (!checkHaveDocument())
180 return nullptr;
181 if (!PyArg_ParseTuple(args, const_cast<char*>("es"), "utf-8", &value))
182 return nullptr;
183
184 ScribusDoc* doc = ScCore->primaryMainWindow()->doc;
185 ScribusView*view = ScCore->primaryMainWindow()->view;
186
187 /* a little magic here - I need to compute the "maxGr" value...
188 * We need to know the right size of the page for landscape,
189 * portrait and user defined sizes.
190 */
191 double pixmapSize = (doc->pageHeight() > doc->pageWidth()) ? doc->pageHeight() : doc->pageWidth();
192 PageToPixmapFlags flags = Pixmap_DrawBackground;
193 if (self->transparentBkgnd)
194 flags &= ~Pixmap_DrawBackground;
195 QImage im = view->PageToPixmap(doc->currentPage()->pageNr(), qRound(pixmapSize * self->scale * (self->dpi / 72.0) / 100.0), flags);
196 int dpi = qRound(100.0 / 2.54 * self->dpi);
197 im.setDotsPerMeterY(dpi);
198 im.setDotsPerMeterX(dpi);
199
200 QString outputFileName = QString::fromUtf8(value);
201 if (!im.save(outputFileName, PyUnicode_AsUTF8(self->type)))
202 {
203 PyErr_SetString(ScribusException, QObject::tr("Failed to export image", "python error").toLocal8Bit().constData());
204 return nullptr;
205 }
206 // Py_INCREF(Py_True); // return True not None for backward compat
207 // return Py_True;
208 // Py_RETURN_TRUE;
209 return PyBool_FromLong(static_cast<long>(true));
210 }
211
212 static PyMethodDef ImageExport_methods[] = {
213 {const_cast<char*>("save"), (PyCFunction)ImageExport_save, METH_NOARGS, imgexp_save__doc__},
214 {const_cast<char*>("saveAs"), (PyCFunction)ImageExport_saveAs, METH_VARARGS, imgexp_saveas__doc__},
215 {nullptr, (PyCFunction)(nullptr), 0, nullptr} // sentinel
216 };
217
218 PyTypeObject ImageExport_Type = {
219 PyVarObject_HEAD_INIT(nullptr, 0) // PyObject_VAR_HEAD
220 const_cast<char*>("scribus.ImageExport"), // char *tp_name; /* For printing, in format "<module>.<name>" */
221 sizeof(ImageExport), // int tp_basicsize, /* For allocation */
222 0, // int tp_itemsize; /* For allocation */
223
224 /* Methods to implement standard operations */
225
226 (destructor) ImageExport_dealloc, // destructor tp_dealloc;
227 #if PY_VERSION_HEX >= 0x03080000
228 0, // Py_ssize_t tp_vectorcall_offset
229 #else
230 nullptr, // printfunc tp_print;
231 #endif
232 nullptr, // getattrfunc tp_getattr;
233 nullptr, // setattrfunc tp_setattr;
234 nullptr, // cmpfunc tp_as_async;
235 nullptr, // reprfunc tp_repr;
236
237 /* Method suites for standard classes */
238
239 nullptr, // PyNumberMethods *tp_as_number;
240 nullptr, // PySequenceMethods *tp_as_sequence;
241 nullptr, // PyMappingMethods *tp_as_mapping;
242
243 /* More standard operations (here for binary compatibility) */
244
245 nullptr, // hashfunc tp_hash;
246 nullptr, // ternaryfunc tp_call;
247 nullptr, // reprfunc tp_str;
248 nullptr, // getattrofunc tp_getattro;
249 nullptr, // setattrofunc tp_setattro;
250
251 /* Functions to access object as input/output buffer */
252 nullptr, // PyBufferProcs *tp_as_buffer;
253
254 /* Flags to define presence of optional/expanded features */
255 Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, // long tp_flags;
256
257 imgexp__doc__, // char *tp_doc; /* Documentation string */
258
259 /* Assigned meaning in release 2.0 */
260 /* call function for all accessible objects */
261 nullptr, // traverseproc tp_traverse;
262
263 /* delete references to contained objects */
264 nullptr, // inquiry tp_clear;
265
266 /* Assigned meaning in release 2.1 */
267 /* rich comparisons */
268 nullptr, // richcmpfunc tp_richcompare;
269
270 /* weak reference enabler */
271 0, // long tp_weaklistoffset;
272
273 /* Added in release 2.2 */
274 /* Iterators */
275 nullptr, // getiterfunc tp_iter;
276 nullptr, // iternextfunc tp_iternext;
277
278 /* Attribute descriptor and subclassing stuff */
279 ImageExport_methods, // struct PyMethodDef *tp_methods;
280 ImageExport_members, // struct PyMemberDef *tp_members;
281 ImageExport_getseters, // struct PyGetSetDef *tp_getset;
282 nullptr, // struct _typeobject *tp_base;
283 nullptr, // PyObject *tp_dict;
284 nullptr, // descrgetfunc tp_descr_get;
285 nullptr, // descrsetfunc tp_descr_set;
286 0, // long tp_dictoffset;
287 (initproc)ImageExport_init, // initproc tp_init;
288 nullptr, // allocfunc tp_alloc;
289 ImageExport_new, // newfunc tp_new;
290 nullptr, // freefunc tp_free; /* Low-level free-memory routine */
291 nullptr, // inquiry tp_is_gc; /* For PyObject_IS_GC */
292 nullptr, // PyObject *tp_bases;
293 nullptr, // PyObject *tp_mro; /* method resolution order */
294 nullptr, // PyObject *tp_cache;
295 nullptr, // PyObject *tp_subclasses;
296 nullptr, // PyObject *tp_weaklist;
297 nullptr, // destructor tp_del;
298 0, // unsigned int tp_version_tag;
299 0, // destructor tp_finalize;
300 #if PY_VERSION_HEX >= 0x03080000
301 nullptr, // tp_vectorcall
302 #endif
303 #if PY_VERSION_HEX >= 0x03080000 && PY_VERSION_HEX < 0x03090000
304 nullptr, //deprecated tp_print
305 #endif
306
307 #ifdef COUNT_ALLOCS
308 /* these must be last and never explicitly initialized */
309 // int tp_allocs;
310 // int tp_frees;
311 // int tp_maxalloc;
312 // struct _typeobject *tp_prev;
313 // struct _typeobject *tp_next;
314 #endif
315 };
316