Coverage Report

Created: 2025-09-27 07:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/poppler/cpp/poppler-image.cpp
Line
Count
Source
1
/*
2
 * Copyright (C) 2010-2011, Pino Toscano <pino@kde.org>
3
 * Copyright (C) 2013 Adrian Johnson <ajohnson@redneon.com>
4
 * Copyright (C) 2017-2019, 2021, 2024, Albert Astals Cid <aacid@kde.org>
5
 * Copyright (C) 2017, Jeroen Ooms <jeroenooms@gmail.com>
6
 * Copyright (C) 2018, Zsombor Hollay-Horvath <hollay.horvath@gmail.com>
7
 * Copyright (C) 2018, Adam Reichold <adam.reichold@t-online.de>
8
 * Copyright (C) 2024, g10 Code GmbH, Author: Sune Stolborg Vuorela <sune@vuorela.dk>
9
 *
10
 * This program is free software; you can redistribute it and/or modify
11
 * it under the terms of the GNU General Public License as published by
12
 * the Free Software Foundation; either version 2, or (at your option)
13
 * any later version.
14
 *
15
 * This program is distributed in the hope that it will be useful,
16
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
 * GNU General Public License for more details.
19
 *
20
 * You should have received a copy of the GNU General Public License
21
 * along with this program; if not, write to the Free Software
22
 * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
23
 */
24
25
/**
26
 \file poppler-image.h
27
 */
28
#include "poppler-image.h"
29
30
#include "poppler-image-private.h"
31
32
#include <config.h>
33
#include "goo/ImgWriter.h"
34
#if defined(ENABLE_LIBPNG)
35
#    include "goo/PNGWriter.h"
36
#endif
37
#if defined(ENABLE_LIBJPEG)
38
#    include "goo/JpegWriter.h"
39
#endif
40
#if defined(ENABLE_LIBTIFF)
41
#    include "goo/TiffWriter.h"
42
#endif
43
#include "goo/NetPBMWriter.h"
44
45
#include <cstdlib>
46
#include <cstring>
47
#include <algorithm>
48
#include <memory>
49
#include <vector>
50
51
namespace {
52
53
struct FileCloser
54
{
55
0
    inline explicit FileCloser(FILE *ff) : f(ff) { }
56
0
    inline ~FileCloser() { (void)close(); }
57
    FileCloser(const FileCloser &) = delete;
58
    FileCloser &operator=(const FileCloser &) = delete;
59
    inline bool close()
60
0
    {
61
0
        if (f) {
62
0
            const int c = fclose(f);
63
0
            f = nullptr;
64
0
            return c == 0;
65
0
        }
66
0
        return true;
67
0
    }
68
69
    FILE *f;
70
};
71
72
int calc_bytes_per_row(int width, poppler::image::format_enum format)
73
172k
{
74
172k
    switch (format) {
75
0
    case poppler::image::format_invalid:
76
0
        return 0;
77
0
    case poppler::image::format_mono:
78
0
        return (width + 7) >> 3;
79
0
    case poppler::image::format_gray8:
80
0
        return (width + 3) >> 2 << 2;
81
0
    case poppler::image::format_rgb24:
82
0
    case poppler::image::format_bgr24:
83
0
        return (width * 3 + 3) >> 2 << 2;
84
172k
    case poppler::image::format_argb32:
85
172k
        return width * 4;
86
172k
    }
87
0
    return 0;
88
172k
}
89
90
NetPBMWriter::Format pnm_format(poppler::image::format_enum format)
91
0
{
92
0
    switch (format) {
93
0
    case poppler::image::format_invalid: // unused, anyway
94
0
    case poppler::image::format_mono:
95
0
        return NetPBMWriter::MONOCHROME;
96
0
    case poppler::image::format_gray8:
97
0
    case poppler::image::format_rgb24:
98
0
    case poppler::image::format_bgr24:
99
0
    case poppler::image::format_argb32:
100
0
        return NetPBMWriter::RGB;
101
0
    }
102
0
    return NetPBMWriter::RGB;
103
0
}
104
105
}
106
107
using namespace poppler;
108
109
172k
image_private::image_private(int iwidth, int iheight, image::format_enum iformat) : ref(1), data(nullptr), width(iwidth), height(iheight), bytes_per_row(0), bytes_num(0), format(iformat), own_data(true) { }
110
111
image_private::~image_private()
112
172k
{
113
172k
    if (own_data) {
114
86.4k
        std::free(data);
115
86.4k
    }
116
172k
}
117
118
image_private *image_private::create_data(int width, int height, image::format_enum format)
119
86.4k
{
120
86.4k
    if (width <= 0 || height <= 0) {
121
0
        return nullptr;
122
0
    }
123
124
86.4k
    int bpr = calc_bytes_per_row(width, format);
125
86.4k
    if (bpr <= 0) {
126
0
        return nullptr;
127
0
    }
128
129
86.4k
    auto d = std::make_unique<image_private>(width, height, format);
130
86.4k
    d->bytes_num = bpr * height;
131
86.4k
    d->data = reinterpret_cast<char *>(std::malloc(d->bytes_num));
132
86.4k
    if (!d->data) {
133
0
        return nullptr;
134
0
    }
135
86.4k
    d->own_data = true;
136
86.4k
    d->bytes_per_row = bpr;
137
138
86.4k
    return d.release();
139
86.4k
}
140
141
image_private *image_private::create_data(char *data, int width, int height, image::format_enum format)
142
86.4k
{
143
86.4k
    if (width <= 0 || height <= 0 || !data) {
144
0
        return nullptr;
145
0
    }
146
147
86.4k
    int bpr = calc_bytes_per_row(width, format);
148
86.4k
    if (bpr <= 0) {
149
0
        return nullptr;
150
0
    }
151
152
86.4k
    image_private *d = new image_private(width, height, format);
153
86.4k
    d->bytes_num = bpr * height;
154
86.4k
    d->data = data;
155
86.4k
    d->own_data = false;
156
86.4k
    d->bytes_per_row = bpr;
157
158
86.4k
    return d;
159
86.4k
}
160
161
/**
162
 \class poppler::image poppler-image.h "poppler/cpp/poppler-image.h"
163
164
 A simple representation of image, with direct access to the data.
165
166
 This class uses implicit sharing for the internal data, so it can be used as
167
 value class. This also means any non-const operation will make sure that the
168
 data used by current instance is not shared with other instances (ie
169
 \em detaching), copying the shared data.
170
171
 \since 0.16
172
 */
173
174
/**
175
 \enum poppler::image::format_enum
176
177
 The possible formats for an image.
178
179
 format_gray8 and format_bgr24 were introduced in poppler 0.65.
180
*/
181
182
/**
183
 Construct an invalid image.
184
 */
185
0
image::image() : d(nullptr) { }
186
187
/**
188
 Construct a new image.
189
190
 It allocates the storage needed for the image data; if the allocation fails,
191
 the image is an invalid one.
192
193
 \param iwidth the width for the image
194
 \param iheight the height for the image
195
 \param iformat the format for the bits of the image
196
 */
197
0
image::image(int iwidth, int iheight, image::format_enum iformat) : d(image_private::create_data(iwidth, iheight, iformat)) { }
198
199
/**
200
 Construct a new image.
201
202
 It uses the provide data buffer for the image, so you \b must ensure it
203
 remains valid for the whole lifetime of the image.
204
205
 \param idata the buffer to use for the image
206
 \param iwidth the width for the image
207
 \param iheight the height for the image
208
 \param iformat the format for the bits of the image
209
 */
210
86.4k
image::image(char *idata, int iwidth, int iheight, image::format_enum iformat) : d(image_private::create_data(idata, iwidth, iheight, iformat)) { }
211
212
/**
213
 Copy constructor.
214
 */
215
86.4k
image::image(const image &img) : d(img.d)
216
86.4k
{
217
86.4k
    if (d) {
218
86.4k
        ++d->ref;
219
86.4k
    }
220
86.4k
}
221
222
/**
223
 Destructor.
224
 */
225
image::~image()
226
172k
{
227
172k
    if (d && !--d->ref) {
228
172k
        delete d;
229
172k
    }
230
172k
}
231
232
/**
233
 Image validity check.
234
235
 \returns whether the image is valid.
236
 */
237
bool image::is_valid() const
238
0
{
239
0
    return d && d->format != format_invalid;
240
0
}
241
242
/**
243
 \returns the format of the image
244
 */
245
image::format_enum image::format() const
246
0
{
247
0
    return d ? d->format : format_invalid;
248
0
}
249
250
/**
251
 \returns whether the width of the image
252
 */
253
int image::width() const
254
0
{
255
0
    return d ? d->width : 0;
256
0
}
257
258
/**
259
 \returns whether the height of the image
260
 */
261
int image::height() const
262
0
{
263
0
    return d ? d->height : 0;
264
0
}
265
266
/**
267
 \returns the number of bytes in each row of the image
268
 */
269
int image::bytes_per_row() const
270
0
{
271
0
    return d ? d->bytes_per_row : 0;
272
0
}
273
274
/**
275
 Access to the image bits.
276
277
 This function will detach and copy the shared data.
278
279
 \returns the pointer to the first pixel
280
 */
281
char *image::data()
282
0
{
283
0
    if (!d) {
284
0
        return nullptr;
285
0
    }
286
287
0
    detach();
288
0
    return d->data;
289
0
}
290
291
/**
292
 Access to the image bits.
293
294
 This function provides const access to the data.
295
296
 \returns the pointer to the first pixel
297
 */
298
const char *image::const_data() const
299
0
{
300
0
    return d ? d->data : nullptr;
301
0
}
302
303
/**
304
 Copies the image (i.e. \em detaches)
305
306
 \returns a new image copied from the current image
307
 */
308
image image::copy() const
309
86.4k
{
310
86.4k
    image img(*this);
311
86.4k
    img.detach();
312
86.4k
    return img;
313
86.4k
}
314
315
/**
316
 Saves the current image to file.
317
318
 Using this function it is possible to save the image to the specified
319
 \p file_name, in the specified \p out_format and with a resolution of the
320
 specified \p dpi.
321
322
 Image formats commonly supported are:
323
 \li PNG: \c png
324
 \li JPEG: \c jpeg, \c jpg
325
 \li TIFF: \c tiff
326
 \li PNM: \c pnm (with Poppler >= 0.18)
327
328
 If an image format is not supported (check the result of
329
 supported_image_formats()), the saving fails.
330
331
 \returns whether the saving succeeded
332
 */
333
bool image::save(const std::string &file_name, const std::string &out_format, int dpi) const
334
0
{
335
0
    if (!is_valid() || file_name.empty() || out_format.empty()) {
336
0
        return false;
337
0
    }
338
339
0
    std::string fmt = out_format;
340
0
    std::ranges::transform(fmt, fmt.begin(), tolower);
341
342
0
    std::unique_ptr<ImgWriter> w;
343
0
    const int actual_dpi = dpi == -1 ? 75 : dpi;
344
0
    if (false) {
345
0
    }
346
0
#if defined(ENABLE_LIBPNG)
347
0
    else if (fmt == "png") {
348
0
        w = std::make_unique<PNGWriter>();
349
0
    }
350
0
#endif
351
#if defined(ENABLE_LIBJPEG)
352
    else if (fmt == "jpeg" || fmt == "jpg") {
353
        w = std::make_unique<JpegWriter>();
354
    }
355
#endif
356
#if defined(ENABLE_LIBTIFF)
357
    else if (fmt == "tiff") {
358
        w = std::make_unique<TiffWriter>();
359
    }
360
#endif
361
0
    else if (fmt == "pnm") {
362
0
        w = std::make_unique<NetPBMWriter>(pnm_format(d->format));
363
0
    }
364
0
    if (!w) {
365
0
        return false;
366
0
    }
367
0
    FILE *f = fopen(file_name.c_str(), "wb");
368
0
    if (!f) {
369
0
        return false;
370
0
    }
371
0
    const FileCloser fc(f);
372
0
    if (!w->init(f, d->width, d->height, actual_dpi, actual_dpi)) {
373
0
        return false;
374
0
    }
375
0
    switch (d->format) {
376
0
    case format_invalid:
377
0
        return false;
378
0
    case format_mono:
379
0
        return false;
380
0
    case format_gray8: {
381
0
        std::vector<unsigned char> row(3 * d->width);
382
0
        char *hptr = d->data;
383
0
        for (int y = 0; y < d->height; ++y) {
384
0
            unsigned char *rowptr = &row[0];
385
0
            for (int x = 0; x < d->width; ++x, rowptr += 3) {
386
0
                rowptr[0] = *reinterpret_cast<unsigned char *>(hptr + x);
387
0
                rowptr[1] = *reinterpret_cast<unsigned char *>(hptr + x);
388
0
                rowptr[2] = *reinterpret_cast<unsigned char *>(hptr + x);
389
0
            }
390
0
            rowptr = &row[0];
391
0
            if (!w->writeRow(&rowptr)) {
392
0
                return false;
393
0
            }
394
0
            hptr += d->bytes_per_row;
395
0
        }
396
0
        break;
397
0
    }
398
0
    case format_bgr24: {
399
0
        std::vector<unsigned char> row(3 * d->width);
400
0
        char *hptr = d->data;
401
0
        for (int y = 0; y < d->height; ++y) {
402
0
            unsigned char *rowptr = &row[0];
403
0
            for (int x = 0; x < d->width; ++x, rowptr += 3) {
404
0
                rowptr[0] = *reinterpret_cast<unsigned char *>(hptr + x * 3 + 2);
405
0
                rowptr[1] = *reinterpret_cast<unsigned char *>(hptr + x * 3 + 1);
406
0
                rowptr[2] = *reinterpret_cast<unsigned char *>(hptr + x * 3);
407
0
            }
408
0
            rowptr = &row[0];
409
0
            if (!w->writeRow(&rowptr)) {
410
0
                return false;
411
0
            }
412
0
            hptr += d->bytes_per_row;
413
0
        }
414
0
        break;
415
0
    }
416
0
    case format_rgb24: {
417
0
        char *hptr = d->data;
418
0
        for (int y = 0; y < d->height; ++y) {
419
0
            if (!w->writeRow(reinterpret_cast<unsigned char **>(&hptr))) {
420
0
                return false;
421
0
            }
422
0
            hptr += d->bytes_per_row;
423
0
        }
424
0
        break;
425
0
    }
426
0
    case format_argb32: {
427
0
        std::vector<unsigned char> row(3 * d->width);
428
0
        char *hptr = d->data;
429
0
        for (int y = 0; y < d->height; ++y) {
430
0
            unsigned char *rowptr = &row[0];
431
0
            for (int x = 0; x < d->width; ++x, rowptr += 3) {
432
0
                const unsigned int pixel = *reinterpret_cast<unsigned int *>(hptr + x * 4);
433
0
                rowptr[0] = (pixel >> 16) & 0xff;
434
0
                rowptr[1] = (pixel >> 8) & 0xff;
435
0
                rowptr[2] = pixel & 0xff;
436
0
            }
437
0
            rowptr = &row[0];
438
0
            if (!w->writeRow(&rowptr)) {
439
0
                return false;
440
0
            }
441
0
            hptr += d->bytes_per_row;
442
0
        }
443
0
        break;
444
0
    }
445
0
    }
446
447
0
    if (!w->close()) {
448
0
        return false;
449
0
    }
450
451
0
    return true;
452
0
}
453
454
/**
455
 \returns a list of the supported image formats
456
 */
457
std::vector<std::string> image::supported_image_formats()
458
0
{
459
0
    std::vector<std::string> formats;
460
0
#if defined(ENABLE_LIBPNG)
461
0
    formats.emplace_back("png");
462
0
#endif
463
#if defined(ENABLE_LIBJPEG)
464
    formats.emplace_back("jpeg");
465
    formats.emplace_back("jpg");
466
#endif
467
#if defined(ENABLE_LIBTIFF)
468
    formats.emplace_back("tiff");
469
#endif
470
0
    formats.emplace_back("pnm");
471
0
    return formats;
472
0
}
473
474
/**
475
 Assignment operator.
476
 */
477
image &image::operator=(const image &img)
478
0
{
479
0
    if (this == &img) {
480
0
        return *this;
481
0
    }
482
483
0
    if (img.d) {
484
0
        ++img.d->ref;
485
0
    }
486
0
    image_private *old_d = d;
487
0
    d = img.d;
488
0
    if (old_d && !--old_d->ref) {
489
0
        delete old_d;
490
0
    }
491
0
    return *this;
492
0
}
493
494
void image::detach()
495
86.4k
{
496
86.4k
    if (d->ref == 1) {
497
0
        return;
498
0
    }
499
500
86.4k
    image_private *old_d = d;
501
86.4k
    d = image_private::create_data(old_d->width, old_d->height, old_d->format);
502
86.4k
    if (d) {
503
86.4k
        std::memcpy(d->data, old_d->data, old_d->bytes_num);
504
86.4k
        --old_d->ref;
505
86.4k
    } else {
506
0
        d = old_d;
507
0
    }
508
86.4k
}