1 /************************************************************************
2 * *
3 * This file is part of Kooka, a scanning/OCR application using *
4 * Qt <http://www.qt.io> and KDE Frameworks <http://www.kde.org>. *
5 * *
6 * Copyright (C) 1999-2016 Klaas Freitag <freitag@suse.de> *
7 * Jonathan Marten <jjm@keelhaul.me.uk> *
8 * *
9 * Kooka is free software; you can redistribute it and/or modify it *
10 * under the terms of the GNU Library General Public License as *
11 * published by the Free Software Foundation and appearing in the *
12 * file COPYING included in the packaging of this file; either *
13 * version 2 of the License, or (at your option) any later version. *
14 * *
15 * As a special exception, permission is given to link this program *
16 * with any version of the KADMOS OCR/ICR engine (a product of *
17 * reRecognition GmbH, Kreuzlingen), and distribute the resulting *
18 * executable without including the source code for KADMOS in the *
19 * source distribution. *
20 * *
21 * This program is distributed in the hope that it will be useful, *
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
24 * GNU General Public License for more details. *
25 * *
26 * You should have received a copy of the GNU General Public *
27 * License along with this program; see the file COPYING. If *
28 * not, see <http://www.gnu.org/licenses/>. *
29 * *
30 ************************************************************************/
31
32 #include "kookaimage.h"
33
34 #include <qdebug.h>
35 #include <qfile.h>
36
37 #include <klocalizedstring.h>
38
39 #include "imageformat.h"
40
41 #ifdef HAVE_TIFF
42 extern "C"
43 {
44 #include <tiffio.h>
45 #include <tiff.h>
46 }
47 #endif
48
49
KookaImage()50 KookaImage::KookaImage()
51 : QImage()
52 {
53 init();
54 }
55
56
KookaImage(const QImage & img)57 KookaImage::KookaImage(const QImage &img)
58 : QImage(img)
59 {
60 init();
61 //qDebug() << "image" << img.size();
62 }
63
64
65
init()66 void KookaImage::init()
67 {
68 m_subImages = 0;
69 m_fileItem = nullptr;
70 m_fileBound = false;
71 }
72
73
operator =(const KookaImage & img)74 KookaImage &KookaImage::operator=(const KookaImage &img)
75 {
76 if (this != &img) { // ignore self-assignment
77 QImage::operator=(img);
78
79 m_subImages = img.m_subImages;
80 m_url = img.m_url;
81 m_fileItem = img.m_fileItem;
82 m_fileBound = img.m_fileBound;
83 }
84
85 return (*this);
86 }
87
fileItem() const88 const KFileItem *KookaImage::fileItem() const
89 {
90 return (m_fileItem);
91 }
92
setFileItem(const KFileItem * fi)93 void KookaImage::setFileItem(const KFileItem *fi)
94 {
95 m_fileItem = fi;
96 }
97
setUrl(const QUrl & url)98 void KookaImage::setUrl(const QUrl &url)
99 {
100 m_url = url;
101 }
102
url() const103 QUrl KookaImage::url() const
104 {
105 return m_url;
106 }
107
isFileBound() const108 bool KookaImage::isFileBound() const
109 {
110 return m_fileBound;
111 }
112
loadFromUrl(const QUrl & url)113 QString KookaImage::loadFromUrl(const QUrl &url)
114 {
115 bool ret = true; // return status
116 bool isTiff = false; // do we have a TIFF file?
117 bool haveTiff = false; // can it be read via TIFF lib?
118
119 if (!url.isLocalFile()) return (i18n("Loading non-local images is not yet implemented"));
120
121 if (url.hasFragment()) // is this a subimage?
122 {
123 int subno = url.fragment().toInt(); // get subimage number
124 if (subno>0) // valid number from fragment
125 { // get local file without fragment
126 const QString fileName = url.adjusted(QUrl::RemoveFragment).toLocalFile();
127 qDebug() << "subimage" << subno << "from" << fileName;
128 loadTiffDir(fileName, subno); // load TIFF subimage
129 return (QString());
130 }
131
132 }
133
134 const QString filename = url.toLocalFile(); // local path of image
135 ImageFormat format = ImageFormat::formatForUrl(url);
136
137 // If the file is TIFF and QImageReader supports TIFF files, 'format' as
138 // returned by ImageFormat::formatForUrl() will be "TIF". If TIFF files
139 // are not supported but the MIME type of the file says that it is TIFF,
140 // then 'format' will be "TIFFLIB". So either of these format names means
141 // that a TIFF file is being loaded.
142 isTiff = format.isTiff();
143 //qDebug() << "Image format to load:" << format << "from file" << filename;
144
145 #ifdef HAVE_TIFF
146 if (isTiff) // if it is TIFF, check
147 { // for multiple images
148 qDebug() << "Checking for multi-page TIFF";
149 TIFF *tif = TIFFOpen(QFile::encodeName(filename).constData(), "r");
150 if (tif!=nullptr)
151 {
152 do
153 {
154 ++m_subImages;
155 } while (TIFFReadDirectory(tif));
156 qDebug() << "found" << m_subImages << "TIFF directories";
157 if (m_subImages>1) haveTiff = true;
158 // This format will have been specially detected
159 // in ImageFormat::formatForMime(), if the file is TIFF
160 // but QImageReader does not have TIFF support.
161 // The TIFF file can still be read by loadTiffDir() below.
162 else if (m_subImages==1 && format==ImageFormat("TIFFLIB")) haveTiff = true;
163 TIFFClose(tif);
164 }
165 }
166 #endif
167 if (!haveTiff) // not TIFF, or not multi-page
168 {
169 ret = QImage::load(filename); // Qt can only read one image
170 if (!ret) return (i18n("Image loading failed"));
171 m_subImages = 0;
172 }
173 #ifdef HAVE_TIFF
174 else // a TIFF file, multi-page or not
175 {
176 loadTiffDir(filename, 0); // read by TIFFlib directly
177 }
178 #endif
179
180 m_url = url; // record image source
181 m_fileBound = true; // note loaded from file
182 return (QString()); // loaded OK
183 }
184
185
loadTiffDir(const QString & filename,int subno)186 QString KookaImage::loadTiffDir(const QString &filename, int subno)
187 {
188 #ifdef HAVE_TIFF
189 // if it is TIFF, check with TIFFlib if it is multiple images
190 qDebug() << "Trying to load TIFF, subimage" << subno;
191 TIFF *tif = TIFFOpen(QFile::encodeName(filename).constData(), "r");
192 if (tif==nullptr) return (i18n("Failed to open TIFF file"));
193
194 if (subno>0) // want a subimage
195 {
196 if (!TIFFSetDirectory(tif, subno-1))
197 {
198 TIFFClose(tif);
199 return (i18n("Failed to read TIFF directory"));
200 }
201 }
202
203 int imgWidth, imgHeight;
204 TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &imgWidth);
205 TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &imgHeight);
206
207 // TODO: load bw-image correctly only 2 bit
208 //
209 // The Qt3 version of this did:
210 //
211 // create( imgWidth, imgHeight, 32 );
212 //
213 // and then read the TIFF image into the image data as returned by bits().
214 //
215 // This is not possible on Qt4, where the size/depth of a QImage is set in
216 // its constructor and cannot be subsequently changed. Instead we read
217 // the TIFF file into a temporary image and then use QImage::operator=
218 // to shallow copy that temporary image into ours. After that temporary
219 // image has been destroyed, we have the only copy of the TIFF read image.
220 //
221 QImage tmpImg(imgWidth, imgHeight, QImage::Format_RGB32);
222 uint32 *data = (uint32 *)(tmpImg.bits());
223 if (TIFFReadRGBAImage(tif, imgWidth, imgHeight, data, 0)) {
224 // Successfully read, now convert
225 tmpImg = tmpImg.rgbSwapped(); // swap red and blue
226 tmpImg = tmpImg.mirrored(); // reverse (it's upside down)
227 } else {
228 TIFFClose(tif);
229 return (i18n("Failed to read TIFF image"));
230 }
231
232 // Fetch the x- and y-resolutions to adjust images
233 float xReso, yReso;
234 bool resosFound = TIFFGetField(tif, TIFFTAG_XRESOLUTION, &xReso) &&
235 TIFFGetField(tif, TIFFTAG_YRESOLUTION, &yReso);
236 //qDebug() << "TIFF image: X-Res" << xReso << "Y-Res" << yReso;
237
238 TIFFClose(tif); // finished with TIFF file
239
240 // Check now if resolution in x- and y-directions differ.
241 // If so, stretch the image accordingly.
242 if (resosFound && xReso != yReso) {
243 if (xReso > yReso) {
244 float yScalefactor = xReso / yReso;
245 //qDebug() << "Different resolution X/Y, rescaling Y with factor" << yScalefactor;
246 /* rescale the image */
247 tmpImg = tmpImg.scaled(imgWidth, int(imgHeight * yScalefactor),
248 Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
249 } else {
250 float xScalefactor = yReso / xReso;
251 //qDebug() << "Different resolution X/Y, rescaling X with factor" << xScalefactor;
252 tmpImg = tmpImg.scaled(int(imgWidth * xScalefactor), imgHeight,
253 Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
254 }
255 }
256
257 QImage::operator=(tmpImg); // copy as our image
258 #else
259 return (i18n("TIFF not supported"));
260 #endif // HAVE_TIFF
261 return (QString()); // TIFF read succeeded
262 }
263
264
subImagesCount() const265 int KookaImage::subImagesCount() const
266 {
267 return (m_subImages);
268 }
269
270
isSubImage() const271 bool KookaImage::isSubImage() const
272 {
273 return (m_url.isValid() && m_url.fragment().toInt()>0);
274 }
275
276
277 #ifdef KDE3
278
279 // TODO: only used in kookaprint, move to there
280 /*
281 * tiling
282 */
cutToTiles(const QSize maxSize,int & rows,int & cols,TileMode)283 int KookaImage::cutToTiles(const QSize maxSize, int &rows, int &cols, TileMode)
284 {
285 QSize imgSize = size();
286
287 int w = imgSize.width();
288 if (w > maxSize.width()) {
289 // image is wider than paper
290 w = maxSize.width();
291 }
292 int h = imgSize.height();
293 if (h > maxSize.height()) {
294 // image is wider than paper
295 h = maxSize.height();
296 }
297
298 int absX = 0; // absolute x position from where to start print
299 int absY = 0; // on the image, left top corner of the part to print
300 rows = 0;
301
302 while (h) { // Loop over height, cut in vertical direction
303 rows++;
304 cols = 0;
305 while (w) { // Loop over width, cut in horizontal direction
306 cols++;
307 m_tileVector.append(QRect(absX, absY, w, h));
308
309 absX += w + 1;
310 w = imgSize.width() - absX;
311
312 // if w < 0, this was the last loop, set w to zero to stop loop
313 if (w < 0) {
314 w = 0;
315 }
316
317 // if > 0 here, a new page is required
318 if (w > 0) {
319 if (w > maxSize.width()) {
320 w = maxSize.width();
321 }
322 }
323 }
324 // Reset the X-values to start on the left border again
325 absX = 0;
326 // start with full width again
327 w = imgSize.width();
328 if (w > maxSize.width()) {
329 w = maxSize.width();
330 }
331
332 absY += h + 1;
333 h = imgSize.height() - absY;
334
335 if (h < 0) {
336 h = 0; // be sure to meet the break condition
337 }
338 if (h > maxSize.height()) {
339 h = maxSize.height(); // limit to page height
340 }
341 }
342 m_tileCols = cols;
343
344 return m_tileVector.count();
345 }
346
getTileRect(int rowPos,int colPos) const347 QRect KookaImage::getTileRect(int rowPos, int colPos) const
348 {
349 int indx = rowPos * m_tileCols + colPos;
350 //qDebug() << "Tile index" << indx;
351 return (m_tileVector[indx]);
352 }
353
354 #endif
355
356
357 // TODO: implement dpiX() and setDpiX()
358 // (more useful than dots per metre!)
359