1#include "mupdf/fitz.h"
2
3#include <string.h>
4
5void
6fz_save_pixmap_as_psd(fz_context *ctx, fz_pixmap *pixmap, const char *filename)
7{
8 fz_output *out = fz_new_output_with_path(ctx, filename, 0);
9 fz_band_writer *writer = NULL;
10
11 fz_var(writer);
12
13 fz_try(ctx)
14 {
15 writer = fz_new_psd_band_writer(ctx, out);
16 fz_write_header(ctx, writer, pixmap->w, pixmap->h, pixmap->n, pixmap->alpha, pixmap->xres, pixmap->yres, 0, pixmap->colorspace, pixmap->seps);
17 fz_write_band(ctx, writer, pixmap->stride, pixmap->h, pixmap->samples);
18 fz_close_output(ctx, out);
19 }
20 fz_always(ctx)
21 {
22 fz_drop_band_writer(ctx, writer);
23 fz_drop_output(ctx, out);
24 }
25 fz_catch(ctx)
26 {
27 fz_rethrow(ctx);
28 }
29}
30
31void
32fz_write_pixmap_as_psd(fz_context *ctx, fz_output *out, const fz_pixmap *pixmap)
33{
34 fz_band_writer *writer;
35
36 if (!out)
37 return;
38
39 writer = fz_new_psd_band_writer(ctx, out);
40
41 fz_try(ctx)
42 {
43 fz_write_header(ctx, writer, pixmap->w, pixmap->h, pixmap->n, pixmap->alpha, pixmap->xres, pixmap->yres, 0, pixmap->colorspace, pixmap->seps);
44 fz_write_band(ctx, writer, pixmap->stride, pixmap->h, pixmap->samples);
45 }
46 fz_always(ctx)
47 {
48 fz_drop_band_writer(ctx, writer);
49 }
50 fz_catch(ctx)
51 {
52 fz_rethrow(ctx);
53 }
54}
55
56typedef struct psd_band_writer_s
57{
58 fz_band_writer super;
59 int num_additive;
60} psd_band_writer;
61
62static void
63psd_write_header(fz_context *ctx, fz_band_writer *writer_, fz_colorspace *cs)
64{
65 psd_band_writer *writer = (psd_band_writer *)(void *)writer_;
66 fz_output *out = writer->super.out;
67 int w = writer->super.w;
68 int h = writer->super.h;
69 int s = writer->super.s;
70 int n = writer->super.n;
71 int c = n - writer->super.alpha - s;
72 fz_separations *seps = writer->super.seps;
73 int i;
74 size_t len;
75 static const char psdsig[12] = { '8', 'B', 'P', 'S', 0, 1, 0, 0, 0, 0, 0, 0 };
76 static const char ressig[4] = { '8', 'B', 'I', 'M' };
77 unsigned char *data;
78 size_t size;
79 fz_colorspace *cs_cmyk = cs;
80
81#if FZ_ENABLE_ICC
82 size = fz_buffer_storage(ctx, cs->u.icc.buffer, &data);
83#else
84 size = 0;
85 data = NULL;
86#endif
87
88 if (cs->n != 4)
89 cs_cmyk = fz_device_cmyk(ctx);
90
91 if (!fz_colorspace_is_subtractive(ctx, cs))
92 writer->num_additive = cs->n;
93
94 /* File Header Section */
95 fz_write_data(ctx, out, psdsig, 12);
96 fz_write_int16_be(ctx, out, n);
97 fz_write_int32_be(ctx, out, h);
98 fz_write_int32_be(ctx, out, w);
99 fz_write_int16_be(ctx, out, 8); /* bits per channel */
100
101 switch (c)
102 {
103 case 0:
104 case 1:
105 fz_write_int16_be(ctx, out, 1); /* Greyscale */
106 break;
107 case 3:
108 fz_write_int16_be(ctx, out, 3); /* RGB */
109 break;
110 case 4:
111 fz_write_int16_be(ctx, out, 4); /* CMYK */
112 break;
113 default:
114 fz_write_int16_be(ctx, out, 7); /* Multichannel */
115 break;
116 }
117
118 /* Color Mode Data Section - empty */
119 fz_write_int32_be(ctx, out, 0);
120
121 /* Image Resources Section - Spot Names, Equivalent colors, resolution, ICC Profile */
122 /* Spot names */
123 len = 0;
124 for (i = 0; i < s; i++)
125 {
126 const char *name = fz_separation_name(ctx, seps, i);
127 char text[32];
128 size_t len2;
129 if (name == NULL)
130 {
131 fz_snprintf(text, sizeof text, "Spot%d", i-4);
132 name = text;
133 }
134 len2 = strlen(name);
135 if (len2 > 255)
136 len2 = 255;
137 len += len2 + 1;
138 }
139
140 /* Write the size of all the following resources */
141 fz_write_int32_be(ctx, out,
142 (s ? 12 + ((len + 1)&~1) : 0) + /* Spot Names */
143 (s ? 12 + (14 * s) : 0) + /* DisplayInfo */
144 28 + /* Resolutions */
145 (size ? (size+19)&~1 : 0)); /* ICC Profile */
146
147 /* Spot names */
148 if (s != 0)
149 {
150 fz_write_data(ctx, out, ressig, 4);
151 fz_write_int16_be(ctx, out, 0x03EE);
152 fz_write_int16_be(ctx, out, 0); /* PString */
153 fz_write_int32_be(ctx, out, (len + 1)&~1);
154 for (i = 0; i < s; i++) {
155 size_t len2;
156 const char *name = fz_separation_name(ctx, seps, i);
157 char text[32];
158 if (name == NULL)
159 {
160 fz_snprintf(text, sizeof text, "Spot%d", i-4);
161 name = text;
162 }
163 len2 = strlen(name);
164 if (len2 > 255)
165 len2 = 255;
166 fz_write_byte(ctx, out, len2);
167 fz_write_data(ctx, out, name, len2);
168 }
169 if (len & 1)
170 {
171 fz_write_byte(ctx, out, 0);
172 }
173
174 /* DisplayInfo - Colors for each spot channel */
175 fz_write_data(ctx, out, ressig, 4);
176 fz_write_int16_be(ctx, out, 0x03EF);
177 fz_write_int16_be(ctx, out, 0); /* PString */
178 fz_write_int32_be(ctx, out, 14 * s); /* Length */
179 for (i = 0; i < s; i++) {
180 float cmyk[4];
181 fz_separation_equivalent(ctx, seps, i, cs_cmyk, cmyk, NULL, fz_default_color_params);
182 fz_write_int16_be(ctx, out, 02); /* CMYK */
183 /* PhotoShop stores all component values as if they were additive. */
184 fz_write_int16_be(ctx, out, 65535 * (1-cmyk[0]));/* Cyan */
185 fz_write_int16_be(ctx, out, 65535 * (1-cmyk[1]));/* Magenta */
186 fz_write_int16_be(ctx, out, 65535 * (1-cmyk[2]));/* Yellow */
187 fz_write_int16_be(ctx, out, 65535 * (1-cmyk[3]));/* Black */
188 fz_write_int16_be(ctx, out, 0); /* Opacity 0 to 100 */
189 fz_write_byte(ctx, out, 2); /* Don't know */
190 fz_write_byte(ctx, out, 0); /* Padding - Always Zero */
191 }
192 }
193
194 /* ICC Profile - (size + 19)&~1 bytes */
195 if (size != 0)
196 {
197 /* Image Resource block */
198 fz_write_data(ctx, out, ressig, 4);
199 fz_write_int16_be(ctx, out, 0x40f); /* ICC Profile */
200 fz_write_data(ctx, out, "\x07Profile", 8); /* Profile name (must be even!) */
201 fz_write_int32_be(ctx, out, size);
202 fz_write_data(ctx, out, data, size); /* Actual data */
203 if (size & 1)
204 fz_write_byte(ctx, out, 0); /* Pad to even */
205 }
206
207 /* Image resolution - 28 bytes */
208 fz_write_data(ctx, out, ressig, 4);
209 fz_write_int16_be(ctx, out, 0x03ED);
210 fz_write_int16_be(ctx, out, 0); /* PString */
211 fz_write_int32_be(ctx, out, 16); /* Length */
212 /* Resolution is specified as a fixed 16.16 bits */
213 fz_write_int32_be(ctx, out, writer->super.xres);
214 fz_write_int16_be(ctx, out, 1); /* width: 1 --> resolution is pixels per inch */
215 fz_write_int16_be(ctx, out, 1); /* width: 1 --> resolution is pixels per inch */
216 fz_write_int32_be(ctx, out, writer->super.yres);
217 fz_write_int16_be(ctx, out, 1); /* height: 1 --> resolution is pixels per inch */
218 fz_write_int16_be(ctx, out, 1); /* height: 1 --> resolution is pixels per inch */
219
220 /* Layer and Mask Information Section */
221 fz_write_int32_be(ctx, out, 0);
222
223 /* Image Data Section */
224 fz_write_int16_be(ctx, out, 0); /* Raw image data */
225}
226
227static void
228psd_invert_buffer(unsigned char *buffer, int size)
229{
230 int k;
231
232 for (k = 0; k < size; k++)
233 buffer[k] = 255 - buffer[k];
234}
235
236static void
237psd_write_band(fz_context *ctx, fz_band_writer *writer_, int stride, int band_start, int band_height, const unsigned char *sp)
238{
239 psd_band_writer *writer = (psd_band_writer *)(void *)writer_;
240 fz_output *out = writer->super.out;
241 int y, x, k, finalband;
242 int w, h, n;
243 unsigned char buffer[256];
244 unsigned char *buffer_end = &buffer[sizeof(buffer)];
245 unsigned char *b;
246 int plane_inc;
247 int line_skip;
248 int num_additive = writer->num_additive;
249
250 if (!out)
251 return;
252
253 w = writer->super.w;
254 h = writer->super.h;
255 n = writer->super.n;
256
257 finalband = (band_start+band_height >= h);
258 if (finalband)
259 band_height = h - band_start;
260
261 plane_inc = w * (h - band_height);
262 line_skip = stride - w*n;
263 b = buffer;
264 if (writer->super.alpha)
265 {
266 const unsigned char *ap = &sp[n-1];
267 for (k = 0; k < n-1; k++)
268 {
269 for (y = 0; y < band_height; y++)
270 {
271 for (x = 0; x < w; x++)
272 {
273 int a = *ap;
274 ap += n;
275 *b++ = a != 0 ? (*sp * 255 + 128)/a : 0;
276 sp += n;
277 if (b == buffer_end)
278 {
279 if (k >= num_additive)
280 psd_invert_buffer(buffer, sizeof(buffer));
281 fz_write_data(ctx, out, buffer, sizeof(buffer));
282 b = buffer;
283 }
284 }
285 sp += line_skip;
286 ap += line_skip;
287 }
288 sp -= stride * band_height - 1;
289 ap -= stride * band_height;
290 if (b != buffer)
291 {
292 if (k >= num_additive)
293 psd_invert_buffer(buffer, sizeof(buffer));
294 fz_write_data(ctx, out, buffer, b - buffer);
295 b = buffer;
296 }
297 fz_seek_output(ctx, out, plane_inc, SEEK_CUR);
298 }
299 for (y = 0; y < band_height; y++)
300 {
301 for (x = 0; x < w; x++)
302 {
303 *b++ = *sp;
304 sp += n;
305 if (b == buffer_end)
306 {
307 fz_write_data(ctx, out, buffer, sizeof(buffer));
308 b = buffer;
309 }
310 }
311 sp += line_skip;
312 }
313 if (b != buffer)
314 {
315 fz_write_data(ctx, out, buffer, b - buffer);
316 b = buffer;
317 }
318 fz_seek_output(ctx, out, plane_inc, SEEK_CUR);
319 }
320 else
321 {
322 for (k = 0; k < n; k++)
323 {
324 for (y = 0; y < band_height; y++)
325 {
326 for (x = 0; x < w; x++)
327 {
328 *b++ = *sp;
329 sp += n;
330 if (b == buffer_end)
331 {
332 if (k >= num_additive)
333 psd_invert_buffer(buffer, sizeof(buffer));
334 fz_write_data(ctx, out, buffer, sizeof(buffer));
335 b = buffer;
336 }
337 }
338 sp += line_skip;
339 }
340 sp -= stride * band_height - 1;
341 if (b != buffer)
342 {
343 if (k >= num_additive)
344 psd_invert_buffer(buffer, sizeof(buffer));
345 fz_write_data(ctx, out, buffer, b - buffer);
346 b = buffer;
347 }
348 fz_seek_output(ctx, out, plane_inc, SEEK_CUR);
349 }
350 }
351 fz_seek_output(ctx, out, w * (band_height - h * n), SEEK_CUR);
352}
353
354static void
355psd_write_trailer(fz_context *ctx, fz_band_writer *writer_)
356{
357 psd_band_writer *writer = (psd_band_writer *)(void *)writer_;
358 fz_output *out = writer->super.out;
359
360 (void)out;
361 (void)writer;
362}
363
364static void
365psd_drop_band_writer(fz_context *ctx, fz_band_writer *writer_)
366{
367 psd_band_writer *writer = (psd_band_writer *)(void *)writer_;
368
369 (void)writer;
370}
371
372fz_band_writer *fz_new_psd_band_writer(fz_context *ctx, fz_output *out)
373{
374 psd_band_writer *writer = fz_new_band_writer(ctx, psd_band_writer, out);
375
376 writer->super.header = psd_write_header;
377 writer->super.band = psd_write_band;
378 writer->super.trailer = psd_write_trailer;
379 writer->super.drop = psd_drop_band_writer;
380
381 writer->num_additive = 0;
382
383 return &writer->super;
384}
385