1 | #include "mupdf/fitz.h" |
2 | |
3 | #include <string.h> |
4 | |
5 | void |
6 | fz_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 | |
31 | void |
32 | fz_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 | |
56 | typedef struct psd_band_writer_s |
57 | { |
58 | fz_band_writer super; |
59 | int num_additive; |
60 | } psd_band_writer; |
61 | |
62 | static void |
63 | (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 | |
227 | static void |
228 | psd_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 | |
236 | static void |
237 | psd_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 | |
354 | static void |
355 | psd_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 | |
364 | static void |
365 | psd_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 | |
372 | fz_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 | |