comparison mupdf-source/source/fitz/output-psd.c @ 2:b50eed0cc0ef upstream

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
comparison
equal deleted inserted replaced
1:1d09e1dec1d9 2:b50eed0cc0ef
1 // Copyright (C) 2004-2021 Artifex Software, Inc.
2 //
3 // This file is part of MuPDF.
4 //
5 // MuPDF is free software: you can redistribute it and/or modify it under the
6 // terms of the GNU Affero General Public License as published by the Free
7 // Software Foundation, either version 3 of the License, or (at your option)
8 // any later version.
9 //
10 // MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY
11 // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12 // FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
13 // details.
14 //
15 // You should have received a copy of the GNU Affero General Public License
16 // along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html>
17 //
18 // Alternative licensing terms are available from the licensor.
19 // For commercial licensing, see <https://www.artifex.com/> or contact
20 // Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco,
21 // CA 94129, USA, for further information.
22
23 #include "mupdf/fitz.h"
24
25 #include <string.h>
26
27 void
28 fz_save_pixmap_as_psd(fz_context *ctx, fz_pixmap *pixmap, const char *filename)
29 {
30 fz_output *out = fz_new_output_with_path(ctx, filename, 0);
31 fz_band_writer *writer = NULL;
32
33 fz_var(writer);
34
35 fz_try(ctx)
36 {
37 writer = fz_new_psd_band_writer(ctx, out);
38 fz_write_header(ctx, writer, pixmap->w, pixmap->h, pixmap->n, pixmap->alpha, pixmap->xres, pixmap->yres, 0, pixmap->colorspace, pixmap->seps);
39 fz_write_band(ctx, writer, pixmap->stride, pixmap->h, pixmap->samples);
40 fz_close_band_writer(ctx, writer);
41 fz_close_output(ctx, out);
42 }
43 fz_always(ctx)
44 {
45 fz_drop_band_writer(ctx, writer);
46 fz_drop_output(ctx, out);
47 }
48 fz_catch(ctx)
49 {
50 fz_rethrow(ctx);
51 }
52 }
53
54 void
55 fz_write_pixmap_as_psd(fz_context *ctx, fz_output *out, const fz_pixmap *pixmap)
56 {
57 fz_band_writer *writer;
58
59 if (!out)
60 return;
61
62 writer = fz_new_psd_band_writer(ctx, out);
63
64 fz_try(ctx)
65 {
66 fz_write_header(ctx, writer, pixmap->w, pixmap->h, pixmap->n, pixmap->alpha, pixmap->xres, pixmap->yres, 0, pixmap->colorspace, pixmap->seps);
67 fz_write_band(ctx, writer, pixmap->stride, pixmap->h, pixmap->samples);
68 fz_close_band_writer(ctx, writer);
69 }
70 fz_always(ctx)
71 {
72 fz_drop_band_writer(ctx, writer);
73 }
74 fz_catch(ctx)
75 {
76 fz_rethrow(ctx);
77 }
78 }
79
80 typedef struct psd_band_writer_s
81 {
82 fz_band_writer super;
83 int num_additive;
84 } psd_band_writer;
85
86 static void
87 psd_write_header(fz_context *ctx, fz_band_writer *writer_, fz_colorspace *cs)
88 {
89 psd_band_writer *writer = (psd_band_writer *)(void *)writer_;
90 fz_output *out = writer->super.out;
91 int w = writer->super.w;
92 int h = writer->super.h;
93 int s = writer->super.s;
94 int n = writer->super.n;
95 int c = n - writer->super.alpha - s;
96 fz_separations *seps = writer->super.seps;
97 int i;
98 size_t len;
99 static const char psdsig[12] = { '8', 'B', 'P', 'S', 0, 1, 0, 0, 0, 0, 0, 0 };
100 static const char ressig[4] = { '8', 'B', 'I', 'M' };
101 unsigned char *data;
102 size_t size;
103 fz_colorspace *cs_cmyk = cs;
104
105 #if FZ_ENABLE_ICC
106 size = fz_buffer_storage(ctx, cs->u.icc.buffer, &data);
107 #else
108 size = 0;
109 data = NULL;
110 #endif
111
112 if (cs->n != 4)
113 cs_cmyk = fz_device_cmyk(ctx);
114
115 if (!fz_colorspace_is_subtractive(ctx, cs))
116 writer->num_additive = cs->n;
117
118 /* File Header Section */
119 fz_write_data(ctx, out, psdsig, 12);
120 fz_write_int16_be(ctx, out, n);
121 fz_write_int32_be(ctx, out, h);
122 fz_write_int32_be(ctx, out, w);
123 fz_write_int16_be(ctx, out, 8); /* bits per channel */
124
125 switch (c)
126 {
127 case 0:
128 case 1:
129 fz_write_int16_be(ctx, out, 1); /* Greyscale */
130 break;
131 case 3:
132 fz_write_int16_be(ctx, out, 3); /* RGB */
133 break;
134 case 4:
135 fz_write_int16_be(ctx, out, 4); /* CMYK */
136 break;
137 default:
138 fz_write_int16_be(ctx, out, 7); /* Multichannel */
139 break;
140 }
141
142 /* Color Mode Data Section - empty */
143 fz_write_int32_be(ctx, out, 0);
144
145 /* Image Resources Section - Spot Names, Equivalent colors, resolution, ICC Profile */
146 /* Spot names */
147 len = 0;
148 for (i = 0; i < s; i++)
149 {
150 const char *name = fz_separation_name(ctx, seps, i);
151 char text[32];
152 size_t len2;
153 if (name == NULL)
154 {
155 fz_snprintf(text, sizeof text, "Spot%d", i-4);
156 name = text;
157 }
158 len2 = strlen(name);
159 if (len2 > 255)
160 len2 = 255;
161 len += len2 + 1;
162 }
163
164 /* Write the size of all the following resources */
165 fz_write_int32_be(ctx, out,
166 (s ? 12 + ((len + 1)&~1) : 0) + /* Spot Names */
167 (s ? 12 + (14 * s) : 0) + /* DisplayInfo */
168 28 + /* Resolutions */
169 (size ? (size+19)&~1 : 0)); /* ICC Profile */
170
171 /* Spot names */
172 if (s != 0)
173 {
174 fz_write_data(ctx, out, ressig, 4);
175 fz_write_int16_be(ctx, out, 0x03EE);
176 fz_write_int16_be(ctx, out, 0); /* PString */
177 fz_write_int32_be(ctx, out, (len + 1)&~1);
178 for (i = 0; i < s; i++) {
179 size_t len2;
180 const char *name = fz_separation_name(ctx, seps, i);
181 char text[32];
182 if (name == NULL)
183 {
184 fz_snprintf(text, sizeof text, "Spot%d", i-4);
185 name = text;
186 }
187 len2 = strlen(name);
188 if (len2 > 255)
189 len2 = 255;
190 fz_write_byte(ctx, out, (unsigned char)len2);
191 fz_write_data(ctx, out, name, len2);
192 }
193 if (len & 1)
194 {
195 fz_write_byte(ctx, out, 0);
196 }
197
198 /* DisplayInfo - Colors for each spot channel */
199 fz_write_data(ctx, out, ressig, 4);
200 fz_write_int16_be(ctx, out, 0x03EF);
201 fz_write_int16_be(ctx, out, 0); /* PString */
202 fz_write_int32_be(ctx, out, 14 * s); /* Length */
203 for (i = 0; i < s; i++) {
204 float cmyk[4];
205 fz_separation_equivalent(ctx, seps, i, cs_cmyk, cmyk, NULL, fz_default_color_params);
206 fz_write_int16_be(ctx, out, 02); /* CMYK */
207 /* PhotoShop stores all component values as if they were additive. */
208 fz_write_int16_be(ctx, out, 65535 * (1-cmyk[0]));/* Cyan */
209 fz_write_int16_be(ctx, out, 65535 * (1-cmyk[1]));/* Magenta */
210 fz_write_int16_be(ctx, out, 65535 * (1-cmyk[2]));/* Yellow */
211 fz_write_int16_be(ctx, out, 65535 * (1-cmyk[3]));/* Black */
212 fz_write_int16_be(ctx, out, 0); /* Opacity 0 to 100 */
213 fz_write_byte(ctx, out, 2); /* Don't know */
214 fz_write_byte(ctx, out, 0); /* Padding - Always Zero */
215 }
216 }
217
218 /* ICC Profile - (size + 19)&~1 bytes */
219 if (size != 0)
220 {
221 /* Image Resource block */
222 fz_write_data(ctx, out, ressig, 4);
223 fz_write_int16_be(ctx, out, 0x40f); /* ICC Profile */
224 fz_write_data(ctx, out, "\x07Profile", 8); /* Profile name (must be even!) */
225 fz_write_int32_be(ctx, out, (int)size);
226 fz_write_data(ctx, out, data, size); /* Actual data */
227 if (size & 1)
228 fz_write_byte(ctx, out, 0); /* Pad to even */
229 }
230
231 /* Image resolution - 28 bytes */
232 fz_write_data(ctx, out, ressig, 4);
233 fz_write_int16_be(ctx, out, 0x03ED);
234 fz_write_int16_be(ctx, out, 0); /* PString */
235 fz_write_int32_be(ctx, out, 16); /* Length */
236 /* Resolution is specified as a fixed 16.16 bits */
237 fz_write_int32_be(ctx, out, writer->super.xres);
238 fz_write_int16_be(ctx, out, 1); /* width: 1 --> resolution is pixels per inch */
239 fz_write_int16_be(ctx, out, 1); /* width: 1 --> resolution is pixels per inch */
240 fz_write_int32_be(ctx, out, writer->super.yres);
241 fz_write_int16_be(ctx, out, 1); /* height: 1 --> resolution is pixels per inch */
242 fz_write_int16_be(ctx, out, 1); /* height: 1 --> resolution is pixels per inch */
243
244 /* Layer and Mask Information Section */
245 fz_write_int32_be(ctx, out, 0);
246
247 /* Image Data Section */
248 fz_write_int16_be(ctx, out, 0); /* Raw image data */
249 }
250
251 static void
252 psd_invert_buffer(unsigned char *buffer, int size)
253 {
254 int k;
255
256 for (k = 0; k < size; k++)
257 buffer[k] = 255 - buffer[k];
258 }
259
260 static void
261 psd_write_band(fz_context *ctx, fz_band_writer *writer_, int stride, int band_start, int band_height, const unsigned char *sp)
262 {
263 psd_band_writer *writer = (psd_band_writer *)(void *)writer_;
264 fz_output *out = writer->super.out;
265 int y, x, k, finalband;
266 int w, h, n;
267 unsigned char buffer[256];
268 unsigned char *buffer_end = &buffer[sizeof(buffer)];
269 unsigned char *b;
270 int plane_inc;
271 int line_skip;
272 int num_additive = writer->num_additive;
273
274 if (!out)
275 return;
276
277 w = writer->super.w;
278 h = writer->super.h;
279 n = writer->super.n;
280
281 finalband = (band_start+band_height >= h);
282 if (finalband)
283 band_height = h - band_start;
284
285 plane_inc = w * (h - band_height);
286 line_skip = stride - w*n;
287 b = buffer;
288 if (writer->super.alpha)
289 {
290 const unsigned char *ap = &sp[n-1];
291 for (k = 0; k < n-1; k++)
292 {
293 for (y = 0; y < band_height; y++)
294 {
295 for (x = 0; x < w; x++)
296 {
297 int a = *ap;
298 ap += n;
299 *b++ = a != 0 ? (*sp * 255 + 128)/a : 0;
300 sp += n;
301 if (b == buffer_end)
302 {
303 if (k >= num_additive)
304 psd_invert_buffer(buffer, sizeof(buffer));
305 fz_write_data(ctx, out, buffer, sizeof(buffer));
306 b = buffer;
307 }
308 }
309 sp += line_skip;
310 ap += line_skip;
311 }
312 sp -= stride * (ptrdiff_t)band_height - 1;
313 ap -= stride * (ptrdiff_t)band_height;
314 if (b != buffer)
315 {
316 if (k >= num_additive)
317 psd_invert_buffer(buffer, sizeof(buffer));
318 fz_write_data(ctx, out, buffer, b - buffer);
319 b = buffer;
320 }
321 fz_seek_output(ctx, out, plane_inc, SEEK_CUR);
322 }
323 for (y = 0; y < band_height; y++)
324 {
325 for (x = 0; x < w; x++)
326 {
327 *b++ = *sp;
328 sp += n;
329 if (b == buffer_end)
330 {
331 fz_write_data(ctx, out, buffer, sizeof(buffer));
332 b = buffer;
333 }
334 }
335 sp += line_skip;
336 }
337 if (b != buffer)
338 {
339 fz_write_data(ctx, out, buffer, b - buffer);
340 b = buffer;
341 }
342 fz_seek_output(ctx, out, plane_inc, SEEK_CUR);
343 }
344 else
345 {
346 for (k = 0; k < n; k++)
347 {
348 for (y = 0; y < band_height; y++)
349 {
350 for (x = 0; x < w; x++)
351 {
352 *b++ = *sp;
353 sp += n;
354 if (b == buffer_end)
355 {
356 if (k >= num_additive)
357 psd_invert_buffer(buffer, sizeof(buffer));
358 fz_write_data(ctx, out, buffer, sizeof(buffer));
359 b = buffer;
360 }
361 }
362 sp += line_skip;
363 }
364 sp -= stride * (ptrdiff_t)band_height - 1;
365 if (b != buffer)
366 {
367 if (k >= num_additive)
368 psd_invert_buffer(buffer, sizeof(buffer));
369 fz_write_data(ctx, out, buffer, b - buffer);
370 b = buffer;
371 }
372 fz_seek_output(ctx, out, plane_inc, SEEK_CUR);
373 }
374 }
375 fz_seek_output(ctx, out, w * (band_height - h * (int64_t)n), SEEK_CUR);
376 }
377
378 static void
379 psd_write_trailer(fz_context *ctx, fz_band_writer *writer_)
380 {
381 psd_band_writer *writer = (psd_band_writer *)(void *)writer_;
382 fz_output *out = writer->super.out;
383
384 (void)out;
385 (void)writer;
386 }
387
388 static void
389 psd_drop_band_writer(fz_context *ctx, fz_band_writer *writer_)
390 {
391 psd_band_writer *writer = (psd_band_writer *)(void *)writer_;
392
393 (void)writer;
394 }
395
396 fz_band_writer *fz_new_psd_band_writer(fz_context *ctx, fz_output *out)
397 {
398 psd_band_writer *writer = fz_new_band_writer(ctx, psd_band_writer, out);
399
400 writer->super.header = psd_write_header;
401 writer->super.band = psd_write_band;
402 writer->super.trailer = psd_write_trailer;
403 writer->super.drop = psd_drop_band_writer;
404
405 writer->num_additive = 0;
406
407 return &writer->super;
408 }
409
410 static fz_buffer *
411 psd_from_pixmap(fz_context *ctx, fz_pixmap *pix, fz_color_params color_params, int drop)
412 {
413 fz_buffer *buf = NULL;
414 fz_output *out = NULL;
415
416 fz_var(buf);
417 fz_var(out);
418
419 fz_try(ctx)
420 {
421 buf = fz_new_buffer(ctx, 1024);
422 out = fz_new_output_with_buffer(ctx, buf);
423 fz_write_pixmap_as_psd(ctx, out, pix);
424 fz_close_output(ctx, out);
425 }
426 fz_always(ctx)
427 {
428 if (drop)
429 fz_drop_pixmap(ctx, pix);
430 fz_drop_output(ctx, out);
431 }
432 fz_catch(ctx)
433 {
434 fz_drop_buffer(ctx, buf);
435 fz_rethrow(ctx);
436 }
437 return buf;
438 }
439
440 fz_buffer *
441 fz_new_buffer_from_image_as_psd(fz_context *ctx, fz_image *image, fz_color_params color_params)
442 {
443 fz_pixmap *pix = fz_get_pixmap_from_image(ctx, image, NULL, NULL, NULL, NULL);
444 return psd_from_pixmap(ctx, pix, color_params, 1);
445 }
446
447 fz_buffer *
448 fz_new_buffer_from_pixmap_as_psd(fz_context *ctx, fz_pixmap *pix, fz_color_params color_params)
449 {
450 return psd_from_pixmap(ctx, pix, color_params, 0);
451 }