Coverage Report

Created: 2026-03-31 07:41

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, 2025, 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 ENABLE_LIBPNG
35
#    include "goo/PNGWriter.h"
36
#endif
37
#if ENABLE_LIBJPEG
38
#    include "goo/JpegWriter.h"
39
#endif
40
#if 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
    explicit FileCloser(FILE *ff) : f(ff) { }
56
0
    ~FileCloser() { (void)close(); }
57
    FileCloser(const FileCloser &) = delete;
58
    FileCloser &operator=(const FileCloser &) = delete;
59
    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
160k
{
74
160k
    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
160k
    case poppler::image::format_argb32:
85
160k
        return width * 4;
86
160k
    }
87
0
    return 0;
88
160k
}
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
160k
image_private::image_private(int iwidth, int iheight, image::format_enum iformat) : width(iwidth), height(iheight), format(iformat) { }
110
111
image_private::~image_private()
112
160k
{
113
160k
    if (own_data) {
114
80.1k
        std::free(data);
115
80.1k
    }
116
160k
}
117
118
image_private *image_private::create_data(int width, int height, image::format_enum format)
119
80.1k
{
120
80.1k
    if (width <= 0 || height <= 0) {
121
0
        return nullptr;
122
0
    }
123
124
80.1k
    int bpr = calc_bytes_per_row(width, format);
125
80.1k
    if (bpr <= 0) {
126
0
        return nullptr;
127
0
    }
128
129
80.1k
    auto d = std::make_unique<image_private>(width, height, format);
130
80.1k
    d->bytes_num = bpr * height;
131
80.1k
    d->data = reinterpret_cast<char *>(std::malloc(d->bytes_num));
132
80.1k
    if (!d->data) {
133
0
        return nullptr;
134
0
    }
135
80.1k
    d->own_data = true;
136
80.1k
    d->bytes_per_row = bpr;
137
138
80.1k
    return d.release();
139
80.1k
}
140
141
image_private *image_private::create_data(char *data, int width, int height, image::format_enum format)
142
80.1k
{
143
80.1k
    if (width <= 0 || height <= 0 || !data) {
144
0
        return nullptr;
145
0
    }
146
147
80.1k
    int bpr = calc_bytes_per_row(width, format);
148
80.1k
    if (bpr <= 0) {
149
0
        return nullptr;
150
0
    }
151
152
80.1k
    auto *d = new image_private(width, height, format);
153
80.1k
    d->bytes_num = bpr * height;
154
80.1k
    d->data = data;
155
80.1k
    d->own_data = false;
156
80.1k
    d->bytes_per_row = bpr;
157
158
80.1k
    return d;
159
80.1k
}
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
80.1k
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
80.1k
image::image(const image &img) : d(img.d)
216
80.1k
{
217
80.1k
    if (d) {
218
80.1k
        ++d->ref;
219
80.1k
    }
220
80.1k
}
221
222
/**
223
 Destructor.
224
 */
225
image::~image()
226
160k
{
227
160k
    if (d && !--d->ref) {
228
160k
        delete d;
229
160k
    }
230
160k
}
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
80.1k
{
310
80.1k
    image img(*this);
311
80.1k
    img.detach();
312
80.1k
    return img;
313
80.1k
}
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 (fmt == "png") {
345
0
#if ENABLE_LIBPNG
346
0
        w = std::make_unique<PNGWriter>();
347
0
#endif
348
0
    } else if (fmt == "jpeg" || fmt == "jpg") {
349
#if ENABLE_LIBJPEG
350
        w = std::make_unique<JpegWriter>();
351
#endif
352
0
    } else if (fmt == "tiff") {
353
#if ENABLE_LIBTIFF
354
        w = std::make_unique<TiffWriter>();
355
#endif
356
0
    } else if (fmt == "pnm") {
357
0
        w = std::make_unique<NetPBMWriter>(pnm_format(d->format));
358
0
    }
359
0
    if (!w) {
360
0
        return false;
361
0
    }
362
0
    FILE *f = fopen(file_name.c_str(), "wb");
363
0
    if (!f) {
364
0
        return false;
365
0
    }
366
0
    const FileCloser fc(f);
367
0
    if (!w->init(f, d->width, d->height, actual_dpi, actual_dpi)) {
368
0
        return false;
369
0
    }
370
0
    switch (d->format) {
371
0
    case format_invalid:
372
0
        return false;
373
0
    case format_mono:
374
0
        return false;
375
0
    case format_gray8: {
376
0
        std::vector<unsigned char> row(3 * d->width);
377
0
        char *hptr = d->data;
378
0
        for (int y = 0; y < d->height; ++y) {
379
0
            unsigned char *rowptr = row.data();
380
0
            for (int x = 0; x < d->width; ++x, rowptr += 3) {
381
0
                rowptr[0] = *reinterpret_cast<unsigned char *>(hptr + x);
382
0
                rowptr[1] = *reinterpret_cast<unsigned char *>(hptr + x);
383
0
                rowptr[2] = *reinterpret_cast<unsigned char *>(hptr + x);
384
0
            }
385
0
            rowptr = row.data();
386
0
            if (!w->writeRow(&rowptr)) {
387
0
                return false;
388
0
            }
389
0
            hptr += d->bytes_per_row;
390
0
        }
391
0
        break;
392
0
    }
393
0
    case format_bgr24: {
394
0
        std::vector<unsigned char> row(3 * d->width);
395
0
        char *hptr = d->data;
396
0
        for (int y = 0; y < d->height; ++y) {
397
0
            unsigned char *rowptr = row.data();
398
0
            for (int x = 0; x < d->width; ++x, rowptr += 3) {
399
0
                rowptr[0] = *reinterpret_cast<unsigned char *>(hptr + x * 3 + 2);
400
0
                rowptr[1] = *reinterpret_cast<unsigned char *>(hptr + x * 3 + 1);
401
0
                rowptr[2] = *reinterpret_cast<unsigned char *>(hptr + x * 3);
402
0
            }
403
0
            rowptr = row.data();
404
0
            if (!w->writeRow(&rowptr)) {
405
0
                return false;
406
0
            }
407
0
            hptr += d->bytes_per_row;
408
0
        }
409
0
        break;
410
0
    }
411
0
    case format_rgb24: {
412
0
        char *hptr = d->data;
413
0
        for (int y = 0; y < d->height; ++y) {
414
0
            if (!w->writeRow(reinterpret_cast<unsigned char **>(&hptr))) {
415
0
                return false;
416
0
            }
417
0
            hptr += d->bytes_per_row;
418
0
        }
419
0
        break;
420
0
    }
421
0
    case format_argb32: {
422
0
        std::vector<unsigned char> row(3 * d->width);
423
0
        char *hptr = d->data;
424
0
        for (int y = 0; y < d->height; ++y) {
425
0
            unsigned char *rowptr = row.data();
426
0
            for (int x = 0; x < d->width; ++x, rowptr += 3) {
427
0
                const unsigned int pixel = *reinterpret_cast<unsigned int *>(hptr + x * 4);
428
0
                rowptr[0] = (pixel >> 16) & 0xff;
429
0
                rowptr[1] = (pixel >> 8) & 0xff;
430
0
                rowptr[2] = pixel & 0xff;
431
0
            }
432
0
            rowptr = row.data();
433
0
            if (!w->writeRow(&rowptr)) {
434
0
                return false;
435
0
            }
436
0
            hptr += d->bytes_per_row;
437
0
        }
438
0
        break;
439
0
    }
440
0
    }
441
442
0
    return w->close();
443
0
}
444
445
/**
446
 \returns a list of the supported image formats
447
 */
448
std::vector<std::string> image::supported_image_formats()
449
0
{
450
0
    std::vector<std::string> formats;
451
0
#if ENABLE_LIBPNG
452
0
    formats.emplace_back("png");
453
0
#endif
454
#if ENABLE_LIBJPEG
455
    formats.emplace_back("jpeg");
456
    formats.emplace_back("jpg");
457
#endif
458
#if ENABLE_LIBTIFF
459
    formats.emplace_back("tiff");
460
#endif
461
0
    formats.emplace_back("pnm");
462
0
    return formats;
463
0
}
464
465
/**
466
 Assignment operator.
467
 */
468
image &image::operator=(const image &img)
469
0
{
470
0
    if (this == &img) {
471
0
        return *this;
472
0
    }
473
474
0
    if (img.d) {
475
0
        ++img.d->ref;
476
0
    }
477
0
    image_private *old_d = d;
478
0
    d = img.d;
479
0
    if (old_d && !--old_d->ref) {
480
0
        delete old_d;
481
0
    }
482
0
    return *this;
483
0
}
484
485
void image::detach()
486
80.1k
{
487
80.1k
    if (d->ref == 1) {
488
0
        return;
489
0
    }
490
491
80.1k
    image_private *old_d = d;
492
80.1k
    d = image_private::create_data(old_d->width, old_d->height, old_d->format);
493
80.1k
    if (d) {
494
80.1k
        std::memcpy(d->data, old_d->data, old_d->bytes_num);
495
80.1k
        --old_d->ref;
496
80.1k
    } else {
497
0
        d = old_d;
498
0
    }
499
80.1k
}