1 /*
2     SPDX-FileCopyrightText: 2013 Akarsh Simha <akarsh.simha@kdemail.net>
3 
4     SPDX-License-Identifier: GPL-2.0-or-later
5 */
6 
7 /* Project Includes */
8 #include "imageexporter.h"
9 #include "kstars.h"
10 #include "skyqpainter.h"
11 #include "skymap.h"
12 
13 #include <KJob>
14 #include <KIO/StoredTransferJob>
15 
16 /* Qt Includes */
17 #include <QTemporaryFile>
18 #include <QStatusBar>
19 #include <QSvgGenerator>
20 
ImageExporter(QObject * parent)21 ImageExporter::ImageExporter(QObject *parent) : QObject(parent), m_includeLegend(false), m_Size(nullptr)
22 {
23     m_Legend = new Legend;
24 
25     // set font for legend labels
26     m_Legend->setFont(QFont("Courier New", 8));
27 
28     // set up the default alpha
29     setLegendAlpha(160);
30 }
31 
exportSvg(const QString & fileName)32 void ImageExporter::exportSvg(const QString &fileName)
33 {
34     SkyMap *map = SkyMap::Instance();
35 
36     // export as SVG
37     QSvgGenerator svgGenerator;
38     svgGenerator.setFileName(fileName);
39     svgGenerator.setTitle(i18n("KStars Exported Sky Image"));
40     svgGenerator.setDescription(i18n("KStars Exported Sky Image"));
41     svgGenerator.setSize(QSize(map->width(), map->height()));
42     svgGenerator.setResolution(qMax(map->logicalDpiX(), map->logicalDpiY()));
43     svgGenerator.setViewBox(QRect(0, 0, map->width(), map->height()));
44 
45     SkyQPainter painter(KStars::Instance(), &svgGenerator);
46     painter.begin();
47 
48     map->exportSkyImage(&painter);
49 
50     if (m_includeLegend)
51     {
52         addLegend(&painter);
53     }
54 
55     painter.end();
56 }
57 
exportRasterGraphics(const QString & fileName)58 bool ImageExporter::exportRasterGraphics(const QString &fileName)
59 {
60     //Determine desired image format from filename extension
61     QString ext = fileName.mid(fileName.lastIndexOf(".") + 1);
62 
63     // export as raster graphics
64     const char *format = "PNG";
65 
66     if (ext.toLower() == "png")
67     {
68         format = "PNG";
69     }
70     else if (ext.toLower() == "jpg" || ext.toLower() == "jpeg")
71     {
72         format = "JPG";
73     }
74     else if (ext.toLower() == "gif")
75     {
76         format = "GIF";
77     }
78     else if (ext.toLower() == "pnm")
79     {
80         format = "PNM";
81     }
82     else if (ext.toLower() == "bmp")
83     {
84         format = "BMP";
85     }
86     else
87     {
88         qWarning() << "Could not parse image format of" << fileName << "assuming PNG";
89     }
90 
91     SkyMap *map = SkyMap::Instance();
92 
93     int width, height;
94     if (m_Size)
95     {
96         width  = m_Size->width();
97         height = m_Size->height();
98     }
99     else
100     {
101         width  = map->width();
102         height = map->height();
103     }
104 
105     QPixmap skyimage(map->width(), map->height());
106     QPixmap outimage(width, height);
107     outimage.fill();
108 
109     map->exportSkyImage(&skyimage);
110     qApp->processEvents();
111 
112     //skyImage is the size of the sky map.  The requested image size is w x h.
113     //If w x h is smaller than the skymap, then we simply crop the image.
114     //If w x h is larger than the skymap, pad the skymap image with a white border.
115     if (width == map->width() && height == map->height())
116     {
117         outimage = skyimage.copy();
118     }
119 
120     else
121     {
122         int dx(0), dy(0), sx(0), sy(0);
123         int sw(map->width()), sh(map->height());
124 
125         if (width > map->width())
126         {
127             dx = (width - map->width()) / 2;
128         }
129 
130         else
131         {
132             sx = (map->width() - width) / 2;
133             sw = width;
134         }
135 
136         if (height > map->height())
137         {
138             dy = (height - map->height()) / 2;
139         }
140 
141         else
142         {
143             sy = (map->height() - height) / 2;
144             sh = height;
145         }
146 
147         QPainter p;
148         p.begin(&outimage);
149         p.fillRect(outimage.rect(), QBrush(Qt::white));
150         p.drawImage(dx, dy, skyimage.toImage(), sx, sy, sw, sh);
151         p.end();
152     }
153 
154     if (m_includeLegend)
155     {
156         addLegend(&outimage);
157     }
158 
159     if (!outimage.save(fileName, format))
160     {
161         m_lastErrorMessage = i18n("Error: Unable to save image: %1", fileName);
162         qDebug() << m_lastErrorMessage;
163         return false;
164     }
165 
166     else
167     {
168         KStars::Instance()->statusBar()->showMessage(i18n("Saved image to %1", fileName));
169         return true;
170     }
171 }
addLegend(SkyQPainter * painter)172 void ImageExporter::addLegend(SkyQPainter *painter)
173 {
174     m_Legend->paintLegend(painter);
175 }
176 
addLegend(QPaintDevice * pd)177 void ImageExporter::addLegend(QPaintDevice *pd)
178 {
179     SkyQPainter painter(KStars::Instance(), pd);
180     painter.begin();
181 
182     addLegend(&painter);
183 
184     painter.end();
185 }
186 
exportImage(QString url)187 bool ImageExporter::exportImage(QString url)
188 {
189     QUrl fileURL = QUrl::fromUserInput(url);
190 
191     m_lastErrorMessage = QString();
192     if (fileURL.isValid())
193     {
194         QTemporaryFile tmpfile;
195         QString fname;
196         bool isLocalFile = fileURL.isLocalFile();
197 
198         if (isLocalFile)
199         {
200             fname = fileURL.toLocalFile();
201         }
202 
203         else
204         {
205             tmpfile.open();
206             fname = tmpfile.fileName();
207         }
208 
209         //Determine desired image format from filename extension
210         QString ext = fname.mid(fname.lastIndexOf(".") + 1);
211         if (ext.toLower() == "svg")
212         {
213             exportSvg(fname);
214         }
215 
216         else
217         {
218             return exportRasterGraphics(fname);
219         }
220 
221         if (!isLocalFile)
222         {
223             //attempt to upload image to remote location
224             KIO::StoredTransferJob *put_job = KIO::storedHttpPost(&tmpfile, fileURL, -1);
225             //if(!KIO::NetAccess::upload(tmpfile.fileName(), fileURL, m_KStars))
226             if (put_job->exec() == false)
227             {
228                 m_lastErrorMessage = i18n("Could not upload image to remote location: %1", fileURL.url());
229                 qWarning() << m_lastErrorMessage;
230                 return false;
231             }
232         }
233         return true;
234     }
235     m_lastErrorMessage = i18n("Could not export image: URL %1 invalid", fileURL.url());
236     qWarning() << m_lastErrorMessage;
237     return false;
238 }
239 
setLegendProperties(Legend::LEGEND_TYPE type,Legend::LEGEND_ORIENTATION orientation,Legend::LEGEND_POSITION position,int alpha,bool include)240 void ImageExporter::setLegendProperties(Legend::LEGEND_TYPE type, Legend::LEGEND_ORIENTATION orientation,
241                                         Legend::LEGEND_POSITION position, int alpha, bool include)
242 {
243     // set background color (alpha)
244     setLegendAlpha(alpha);
245     // set legend orientation
246     m_Legend->setOrientation(orientation);
247 
248     // set legend type
249     m_Legend->setType(type);
250 
251     // set legend position
252     m_Legend->setPosition(position);
253 
254     m_includeLegend = include;
255 }
256 
~ImageExporter()257 ImageExporter::~ImageExporter()
258 {
259     delete m_Legend;
260 }
261 
setRasterOutputSize(const QSize * size)262 void ImageExporter::setRasterOutputSize(const QSize *size)
263 {
264     if (size)
265         m_Size = new QSize(*size); // make a copy, so it's safe if the original gets deleted
266     else
267         m_Size = nullptr;
268 }
269 
setLegendAlpha(int alpha)270 void ImageExporter::setLegendAlpha(int alpha)
271 {
272     Q_ASSERT(alpha >= 0 && alpha <= 255);
273     Q_ASSERT(m_Legend);
274     QColor bgColor = m_Legend->getBgColor();
275     bgColor.setAlpha(alpha);
276     m_Legend->setBgColor(bgColor);
277 }
278