1//========================================================================
2//
3// PNGWriter.cc
4//
5// This file is licensed under the GPLv2 or later
6//
7// Copyright (C) 2009 Warren Toomey <wkt@tuhs.org>
8// Copyright (C) 2009 Shen Liang <shenzhuxi@gmail.com>
9// Copyright (C) 2009, 2011-2023 Albert Astals Cid <aacid@kde.org>
10// Copyright (C) 2009 Stefan Thomas <thomas@eload24.com>
11// Copyright (C) 2010, 2011, 2013, 2017 Adrian Johnson <ajohnson@redneon.com>
12// Copyright (C) 2011 Thomas Klausner <wiz@danbala.tuwien.ac.at>
13// Copyright (C) 2012 Pino Toscano <pino@kde.org>
14//
15//========================================================================
16
17#include "PNGWriter.h"
18
19#ifdef ENABLE_LIBPNG
20
21# include <zlib.h>
22# include <cstdlib>
23# include <cstring>
24
25# include "poppler/Error.h"
26# include "goo/gmem.h"
27
28# include <png.h>
29
30struct PNGWriterPrivate
31{
32 explicit PNGWriterPrivate(PNGWriter::Format f) : format(f) { }
33
34 PNGWriter::Format format;
35 png_structp png_ptr = nullptr;
36 png_infop info_ptr = nullptr;
37 unsigned char *icc_data = nullptr;
38 int icc_data_size = 0;
39 char *icc_name = nullptr;
40 bool sRGB_profile = false;
41
42 PNGWriterPrivate(const PNGWriterPrivate &) = delete;
43 PNGWriterPrivate &operator=(const PNGWriterPrivate &) = delete;
44};
45
46PNGWriter::PNGWriter(Format formatA)
47{
48 priv = new PNGWriterPrivate(formatA);
49}
50
51PNGWriter::~PNGWriter()
52{
53 /* cleanup heap allocation */
54 png_destroy_write_struct(png_ptr_ptr: &priv->png_ptr, info_ptr_ptr: &priv->info_ptr);
55 if (priv->icc_data) {
56 gfree(p: priv->icc_data);
57 free(ptr: priv->icc_name);
58 }
59
60 delete priv;
61}
62
63void PNGWriter::setICCProfile(const char *name, unsigned char *data, int size)
64{
65 priv->icc_data = (unsigned char *)gmalloc(size);
66 memcpy(dest: priv->icc_data, src: data, n: size);
67 priv->icc_data_size = size;
68 priv->icc_name = strdup(s: name);
69}
70
71void PNGWriter::setSRGBProfile()
72{
73 priv->sRGB_profile = true;
74}
75
76bool PNGWriter::init(FILE *f, int width, int height, double hDPI, double vDPI)
77{
78 /* libpng changed the png_set_iCCP() prototype in 1.5.0 */
79# if PNG_LIBPNG_VER < 10500
80 png_charp icc_data_ptr = (png_charp)priv->icc_data;
81# else
82 png_const_bytep icc_data_ptr = (png_const_bytep)priv->icc_data;
83# endif
84
85 if (hDPI < 0 || vDPI < 0) {
86 error(category: errInternal, pos: -1, msg: "PNGWriter::init: hDPI or vDPI values are invalid {0:f} {1:f}", hDPI, vDPI);
87 return false;
88 }
89
90 const double png_res_x = hDPI / 0.0254;
91 const double png_res_y = vDPI / 0.0254;
92 if (png_res_x > std::numeric_limits<png_uint_32>::max() || png_res_y > std::numeric_limits<png_uint_32>::max()) {
93 error(category: errInternal, pos: -1, msg: "PNGWriter::init: hDPI or vDPI values are invalid {0:f} {1:f}", hDPI, vDPI);
94 return false;
95 }
96
97 /* initialize stuff */
98 priv->png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, error_ptr: nullptr, error_fn: nullptr, warn_fn: nullptr);
99 if (!priv->png_ptr) {
100 error(category: errInternal, pos: -1, msg: "png_create_write_struct failed");
101 return false;
102 }
103
104 priv->info_ptr = png_create_info_struct(png_ptr: priv->png_ptr);
105 if (!priv->info_ptr) {
106 error(category: errInternal, pos: -1, msg: "png_create_info_struct failed");
107 return false;
108 }
109
110 if (setjmp(png_jmpbuf(priv->png_ptr))) {
111 error(category: errInternal, pos: -1, msg: "png_jmpbuf failed");
112 return false;
113 }
114
115 /* write header */
116 png_init_io(png_ptr: priv->png_ptr, fp: f);
117 if (setjmp(png_jmpbuf(priv->png_ptr))) {
118 error(category: errInternal, pos: -1, msg: "Error during writing header");
119 return false;
120 }
121
122 // Set up the type of PNG image and the compression level
123 png_set_compression_level(png_ptr: priv->png_ptr, Z_BEST_COMPRESSION);
124
125 // Silence silly gcc
126 png_byte bit_depth = -1;
127 png_byte color_type = -1;
128 switch (priv->format) {
129 case RGB:
130 bit_depth = 8;
131 color_type = PNG_COLOR_TYPE_RGB;
132 break;
133 case RGB48:
134 bit_depth = 16;
135 color_type = PNG_COLOR_TYPE_RGB;
136 break;
137 case RGBA:
138 bit_depth = 8;
139 color_type = PNG_COLOR_TYPE_RGB_ALPHA;
140 break;
141 case GRAY:
142 bit_depth = 8;
143 color_type = PNG_COLOR_TYPE_GRAY;
144 break;
145 case MONOCHROME:
146 bit_depth = 1;
147 color_type = PNG_COLOR_TYPE_GRAY;
148 break;
149 }
150 png_byte interlace_type = PNG_INTERLACE_NONE;
151
152 png_set_IHDR(png_ptr: priv->png_ptr, info_ptr: priv->info_ptr, width, height, bit_depth, color_type, interlace_method: interlace_type, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
153
154 png_set_pHYs(png_ptr: priv->png_ptr, info_ptr: priv->info_ptr, res_x: static_cast<png_uint_32>(png_res_x), res_y: static_cast<png_uint_32>(png_res_y), PNG_RESOLUTION_METER);
155
156 if (priv->icc_data) {
157 png_set_iCCP(png_ptr: priv->png_ptr, info_ptr: priv->info_ptr, name: priv->icc_name, PNG_COMPRESSION_TYPE_BASE, profile: icc_data_ptr, proflen: priv->icc_data_size);
158 } else if (priv->sRGB_profile) {
159 png_set_sRGB(png_ptr: priv->png_ptr, info_ptr: priv->info_ptr, PNG_sRGB_INTENT_RELATIVE);
160 }
161
162 png_write_info(png_ptr: priv->png_ptr, info_ptr: priv->info_ptr);
163 if (setjmp(png_jmpbuf(priv->png_ptr))) {
164 error(category: errInternal, pos: -1, msg: "error during writing png info bytes");
165 return false;
166 }
167
168 return true;
169}
170
171bool PNGWriter::writePointers(unsigned char **rowPointers, int rowCount)
172{
173 png_write_image(png_ptr: priv->png_ptr, image: rowPointers);
174 /* write bytes */
175 if (setjmp(png_jmpbuf(priv->png_ptr))) {
176 error(category: errInternal, pos: -1, msg: "Error during writing bytes");
177 return false;
178 }
179
180 return true;
181}
182
183bool PNGWriter::writeRow(unsigned char **row)
184{
185 // Write the row to the file
186 png_write_rows(png_ptr: priv->png_ptr, row, num_rows: 1);
187 if (setjmp(png_jmpbuf(priv->png_ptr))) {
188 error(category: errInternal, pos: -1, msg: "error during png row write");
189 return false;
190 }
191
192 return true;
193}
194
195bool PNGWriter::close()
196{
197 /* end write */
198 png_write_end(png_ptr: priv->png_ptr, info_ptr: priv->info_ptr);
199 if (setjmp(png_jmpbuf(priv->png_ptr))) {
200 error(category: errInternal, pos: -1, msg: "Error during end of write");
201 return false;
202 }
203
204 return true;
205}
206
207#endif
208

source code of poppler/goo/PNGWriter.cc