1 #include "mupdf/fitz.h"
2 #include "mupdf/pdf.h"
3
4 #include <string.h>
5
6 /* ICCBased */
7 static fz_colorspace *
load_icc_based(fz_context * ctx,pdf_obj * dict,int allow_alt)8 load_icc_based(fz_context *ctx, pdf_obj *dict, int allow_alt)
9 {
10 int n = pdf_dict_get_int(ctx, dict, PDF_NAME(N));
11 fz_colorspace *alt = NULL;
12 fz_colorspace *cs = NULL;
13 pdf_obj *obj;
14
15 fz_var(alt);
16 fz_var(cs);
17
18 /* Look at Alternate to detect type (especially Lab). */
19 if (allow_alt)
20 {
21 obj = pdf_dict_get(ctx, dict, PDF_NAME(Alternate));
22 if (obj)
23 {
24 fz_try(ctx)
25 alt = pdf_load_colorspace(ctx, obj);
26 fz_catch(ctx)
27 {
28 fz_rethrow_if(ctx, FZ_ERROR_TRYLATER);
29 fz_warn(ctx, "ignoring broken ICC Alternate colorspace");
30 }
31 }
32 }
33
34 #if FZ_ENABLE_ICC
35 {
36 fz_buffer *buf = NULL;
37 fz_var(buf);
38 fz_try(ctx)
39 {
40 buf = pdf_load_stream(ctx, dict);
41 cs = fz_new_icc_colorspace(ctx, alt ? alt->type : FZ_COLORSPACE_NONE, 0, NULL, buf);
42 if (cs->n > n)
43 {
44 fz_warn(ctx, "ICC colorspace N=%d does not match profile N=%d (ignoring profile)", n, cs->n);
45 fz_drop_colorspace(ctx, cs);
46 cs = NULL;
47 }
48 else if (cs->n < n)
49 {
50 fz_warn(ctx, "ICC colorspace N=%d does not match profile N=%d (using profile)", n, cs->n);
51 }
52 }
53 fz_always(ctx)
54 fz_drop_buffer(ctx, buf);
55 fz_catch(ctx)
56 {
57 fz_rethrow_if(ctx, FZ_ERROR_TRYLATER);
58 fz_warn(ctx, "ignoring broken ICC profile");
59 }
60 }
61 #endif
62
63 if (!cs)
64 cs = alt;
65 else
66 fz_drop_colorspace(ctx, alt);
67
68 if (!cs)
69 {
70 if (n == 1) cs = fz_keep_colorspace(ctx, fz_device_gray(ctx));
71 else if (n == 3) cs = fz_keep_colorspace(ctx, fz_device_rgb(ctx));
72 else if (n == 4) cs = fz_keep_colorspace(ctx, fz_device_cmyk(ctx));
73 else fz_throw(ctx, FZ_ERROR_SYNTAX, "invalid ICC colorspace");
74 }
75
76 return cs;
77 }
78
79 static void
devicen_eval(fz_context * ctx,void * tint,const float * sv,int sn,float * dv,int dn)80 devicen_eval(fz_context *ctx, void *tint, const float *sv, int sn, float *dv, int dn)
81 {
82 pdf_eval_function(ctx, tint, sv, sn, dv, dn);
83 }
84
85 static void
devicen_drop(fz_context * ctx,void * tint)86 devicen_drop(fz_context *ctx, void *tint)
87 {
88 pdf_drop_function(ctx, tint);
89 }
90
91 static fz_colorspace *
load_devicen(fz_context * ctx,pdf_obj * array,int is_devn)92 load_devicen(fz_context *ctx, pdf_obj *array, int is_devn)
93 {
94 fz_colorspace *base = NULL;
95 fz_colorspace *cs = NULL;
96 pdf_obj *nameobj = pdf_array_get(ctx, array, 1);
97 pdf_obj *baseobj = pdf_array_get(ctx, array, 2);
98 pdf_obj *tintobj = pdf_array_get(ctx, array, 3);
99 char name[100];
100 int i, n;
101
102 if (pdf_is_array(ctx, nameobj))
103 {
104 n = pdf_array_len(ctx, nameobj);
105 if (n < 1)
106 fz_throw(ctx, FZ_ERROR_SYNTAX, "too few components in DeviceN colorspace");
107 if (n > FZ_MAX_COLORS)
108 fz_throw(ctx, FZ_ERROR_SYNTAX, "too many components in DeviceN colorspace");
109 }
110 else
111 {
112 n = 1;
113 }
114
115 base = pdf_load_colorspace(ctx, baseobj);
116 fz_try(ctx)
117 {
118 if (is_devn)
119 {
120 fz_snprintf(name, sizeof name, "DeviceN(%d,%s", n, base->name);
121 for (i = 0; i < n; i++) {
122 fz_strlcat(name, ",", sizeof name);
123 fz_strlcat(name, pdf_array_get_name(ctx, nameobj, i), sizeof name);
124 }
125 fz_strlcat(name, ")", sizeof name);
126 }
127 else
128 {
129 fz_snprintf(name, sizeof name, "Separation(%s,%s)", base->name, pdf_to_name(ctx, nameobj));
130 }
131
132 cs = fz_new_colorspace(ctx, FZ_COLORSPACE_SEPARATION, 0, n, name);
133 cs->u.separation.eval = devicen_eval;
134 cs->u.separation.drop = devicen_drop;
135 cs->u.separation.base = fz_keep_colorspace(ctx, base);
136 cs->u.separation.tint = pdf_load_function(ctx, tintobj, n, cs->u.separation.base->n);
137 if (pdf_is_array(ctx, nameobj))
138 for (i = 0; i < n; i++)
139 fz_colorspace_name_colorant(ctx, cs, i, pdf_to_name(ctx, pdf_array_get(ctx, nameobj, i)));
140 else
141 fz_colorspace_name_colorant(ctx, cs, 0, pdf_to_name(ctx, nameobj));
142 }
143 fz_always(ctx)
144 {
145 fz_drop_colorspace(ctx, base);
146 }
147 fz_catch(ctx)
148 {
149 fz_drop_colorspace(ctx, cs);
150 fz_rethrow(ctx);
151 }
152
153 return cs;
154 }
155
156 int
pdf_is_tint_colorspace(fz_context * ctx,fz_colorspace * cs)157 pdf_is_tint_colorspace(fz_context *ctx, fz_colorspace *cs)
158 {
159 return cs && cs->type == FZ_COLORSPACE_SEPARATION;
160 }
161
162 /* Indexed */
163
164 static fz_colorspace *
load_indexed(fz_context * ctx,pdf_obj * array)165 load_indexed(fz_context *ctx, pdf_obj *array)
166 {
167 pdf_obj *baseobj = pdf_array_get(ctx, array, 1);
168 pdf_obj *highobj = pdf_array_get(ctx, array, 2);
169 pdf_obj *lookupobj = pdf_array_get(ctx, array, 3);
170 fz_colorspace *base = NULL;
171 fz_colorspace *cs;
172 size_t i, n;
173 int high;
174 unsigned char *lookup = NULL;
175
176 fz_var(base);
177 fz_var(lookup);
178
179 fz_try(ctx)
180 {
181 base = pdf_load_colorspace(ctx, baseobj);
182
183 high = pdf_to_int(ctx, highobj);
184 high = fz_clampi(high, 0, 255);
185 n = (size_t)base->n * (high + 1);
186 lookup = Memento_label(fz_malloc(ctx, n), "cs_lookup");
187
188 if (pdf_is_string(ctx, lookupobj))
189 {
190 size_t sn = fz_minz(n, pdf_to_str_len(ctx, lookupobj));
191 unsigned char *buf = (unsigned char *) pdf_to_str_buf(ctx, lookupobj);
192 for (i = 0; i < sn; ++i)
193 lookup[i] = buf[i];
194 for (; i < n; ++i)
195 lookup[i] = 0;
196 }
197 else if (pdf_is_indirect(ctx, lookupobj))
198 {
199 fz_stream *file = NULL;
200
201 fz_var(file);
202
203 fz_try(ctx)
204 {
205 file = pdf_open_stream(ctx, lookupobj);
206 i = fz_read(ctx, file, lookup, n);
207 if (i < n)
208 memset(lookup+i, 0, n-i);
209 }
210 fz_always(ctx)
211 {
212 fz_drop_stream(ctx, file);
213 }
214 fz_catch(ctx)
215 {
216 fz_rethrow(ctx);
217 }
218 }
219 else
220 {
221 fz_throw(ctx, FZ_ERROR_SYNTAX, "cannot parse colorspace lookup table");
222 }
223
224 cs = fz_new_indexed_colorspace(ctx, base, high, lookup);
225 }
226 fz_always(ctx)
227 fz_drop_colorspace(ctx, base);
228 fz_catch(ctx)
229 {
230 fz_free(ctx, lookup);
231 fz_rethrow(ctx);
232 }
233
234 return cs;
235 }
236
237 static void
pdf_load_cal_common(fz_context * ctx,pdf_obj * dict,float * wp,float * bp,float * gamma)238 pdf_load_cal_common(fz_context *ctx, pdf_obj *dict, float *wp, float *bp, float *gamma)
239 {
240 pdf_obj *obj;
241 int i;
242
243 obj = pdf_dict_get(ctx, dict, PDF_NAME(WhitePoint));
244 if (pdf_array_len(ctx, obj) != 3)
245 fz_throw(ctx, FZ_ERROR_SYNTAX, "WhitePoint must be a 3-element array");
246
247 for (i = 0; i < 3; i++)
248 {
249 wp[i] = pdf_array_get_real(ctx, obj, i);
250 if (wp[i] < 0)
251 fz_throw(ctx, FZ_ERROR_SYNTAX, "WhitePoint numbers must be positive");
252 }
253 if (wp[1] != 1)
254 fz_throw(ctx, FZ_ERROR_SYNTAX, "WhitePoint Yw must be 1.0");
255
256 obj = pdf_dict_get(ctx, dict, PDF_NAME(BlackPoint));
257 if (pdf_array_len(ctx, obj) == 3)
258 {
259 for (i = 0; i < 3; i++)
260 {
261 bp[i] = pdf_array_get_real(ctx, obj, i);
262 if (bp[i] < 0)
263 fz_throw(ctx, FZ_ERROR_SYNTAX, "BlackPoint numbers must be positive");
264 }
265 }
266
267 obj = pdf_dict_get(ctx, dict, PDF_NAME(Gamma));
268 if (pdf_is_number(ctx, obj))
269 {
270 gamma[0] = pdf_to_real(ctx, obj);
271 gamma[1] = gamma[2];
272 if (gamma[0] <= 0)
273 fz_throw(ctx, FZ_ERROR_SYNTAX, "Gamma must be greater than zero");
274 }
275 else if (pdf_array_len(ctx, obj) == 3)
276 {
277 for (i = 0; i < 3; i++)
278 {
279 gamma[i] = pdf_array_get_real(ctx, obj, i);
280 if (gamma[i] <= 0)
281 fz_throw(ctx, FZ_ERROR_SYNTAX, "Gamma must be greater than zero");
282 }
283 }
284 }
285
286 static fz_colorspace *
pdf_load_cal_gray(fz_context * ctx,pdf_obj * dict)287 pdf_load_cal_gray(fz_context *ctx, pdf_obj *dict)
288 {
289 float wp[3];
290 float bp[3] = { 0, 0, 0 };
291 float gamma[3] = { 1, 1, 1 };
292
293 if (dict == NULL)
294 return fz_keep_colorspace(ctx, fz_device_gray(ctx));
295
296 fz_try(ctx)
297 pdf_load_cal_common(ctx, dict, wp, bp, gamma);
298 fz_catch(ctx)
299 return fz_keep_colorspace(ctx, fz_device_gray(ctx));
300 return fz_new_cal_gray_colorspace(ctx, wp, bp, gamma[0]);
301 }
302
303 static fz_colorspace *
pdf_load_cal_rgb(fz_context * ctx,pdf_obj * dict)304 pdf_load_cal_rgb(fz_context *ctx, pdf_obj *dict)
305 {
306 pdf_obj *obj;
307 float matrix[9] = { 1, 0, 0, 0, 1, 0, 0, 0, 1 };
308 float wp[3];
309 float bp[3] = { 0, 0, 0 };
310 float gamma[3] = { 1, 1, 1 };
311 int i;
312
313 if (dict == NULL)
314 return fz_keep_colorspace(ctx, fz_device_rgb(ctx));
315
316 fz_try(ctx)
317 {
318 pdf_load_cal_common(ctx, dict, wp, bp, gamma);
319 obj = pdf_dict_get(ctx, dict, PDF_NAME(Matrix));
320 if (pdf_array_len(ctx, obj) == 9)
321 {
322 for (i = 0; i < 9; i++)
323 matrix[i] = pdf_array_get_real(ctx, obj, i);
324 }
325 }
326 fz_catch(ctx)
327 return fz_keep_colorspace(ctx, fz_device_rgb(ctx));
328 return fz_new_cal_rgb_colorspace(ctx, wp, bp, gamma, matrix);
329 }
330
331 /* Parse and create colorspace from PDF object */
332
333 static fz_colorspace *
pdf_load_colorspace_imp(fz_context * ctx,pdf_obj * obj)334 pdf_load_colorspace_imp(fz_context *ctx, pdf_obj *obj)
335 {
336 if (pdf_obj_marked(ctx, obj))
337 fz_throw(ctx, FZ_ERROR_SYNTAX, "recursion in colorspace definition");
338
339 if (pdf_is_name(ctx, obj))
340 {
341 if (pdf_name_eq(ctx, obj, PDF_NAME(Pattern)))
342 return fz_keep_colorspace(ctx, fz_device_gray(ctx));
343 else if (pdf_name_eq(ctx, obj, PDF_NAME(G)))
344 return fz_keep_colorspace(ctx, fz_device_gray(ctx));
345 else if (pdf_name_eq(ctx, obj, PDF_NAME(RGB)))
346 return fz_keep_colorspace(ctx, fz_device_rgb(ctx));
347 else if (pdf_name_eq(ctx, obj, PDF_NAME(CMYK)))
348 return fz_keep_colorspace(ctx, fz_device_cmyk(ctx));
349 else if (pdf_name_eq(ctx, obj, PDF_NAME(DeviceGray)))
350 return fz_keep_colorspace(ctx, fz_device_gray(ctx));
351 else if (pdf_name_eq(ctx, obj, PDF_NAME(DeviceRGB)))
352 return fz_keep_colorspace(ctx, fz_device_rgb(ctx));
353 else if (pdf_name_eq(ctx, obj, PDF_NAME(DeviceCMYK)))
354 return fz_keep_colorspace(ctx, fz_device_cmyk(ctx));
355 else
356 fz_throw(ctx, FZ_ERROR_SYNTAX, "unknown colorspace: %s", pdf_to_name(ctx, obj));
357 }
358
359 else if (pdf_is_array(ctx, obj))
360 {
361 pdf_obj *name = pdf_array_get(ctx, obj, 0);
362
363 if (pdf_is_name(ctx, name))
364 {
365 /* load base colorspace instead */
366 if (pdf_name_eq(ctx, name, PDF_NAME(G)))
367 return fz_keep_colorspace(ctx, fz_device_gray(ctx));
368 else if (pdf_name_eq(ctx, name, PDF_NAME(RGB)))
369 return fz_keep_colorspace(ctx, fz_device_rgb(ctx));
370 else if (pdf_name_eq(ctx, name, PDF_NAME(CMYK)))
371 return fz_keep_colorspace(ctx, fz_device_cmyk(ctx));
372 else if (pdf_name_eq(ctx, name, PDF_NAME(DeviceGray)))
373 return fz_keep_colorspace(ctx, fz_device_gray(ctx));
374 else if (pdf_name_eq(ctx, name, PDF_NAME(DeviceRGB)))
375 return fz_keep_colorspace(ctx, fz_device_rgb(ctx));
376 else if (pdf_name_eq(ctx, name, PDF_NAME(DeviceCMYK)))
377 return fz_keep_colorspace(ctx, fz_device_cmyk(ctx));
378 else if (pdf_name_eq(ctx, name, PDF_NAME(CalGray)))
379 return pdf_load_cal_gray(ctx, pdf_array_get(ctx, obj, 1));
380 else if (pdf_name_eq(ctx, name, PDF_NAME(CalRGB)))
381 return pdf_load_cal_rgb(ctx, pdf_array_get(ctx, obj, 1));
382 else if (pdf_name_eq(ctx, name, PDF_NAME(CalCMYK)))
383 return fz_keep_colorspace(ctx, fz_device_cmyk(ctx));
384 else if (pdf_name_eq(ctx, name, PDF_NAME(Lab)))
385 return fz_keep_colorspace(ctx, fz_device_lab(ctx));
386 else
387 {
388 fz_colorspace *cs;
389 fz_try(ctx)
390 {
391 if (pdf_mark_obj(ctx, obj))
392 fz_throw(ctx, FZ_ERROR_SYNTAX, "recursive colorspace");
393 if (pdf_name_eq(ctx, name, PDF_NAME(ICCBased)))
394 cs = load_icc_based(ctx, pdf_array_get(ctx, obj, 1), 1);
395
396 else if (pdf_name_eq(ctx, name, PDF_NAME(Indexed)))
397 cs = load_indexed(ctx, obj);
398 else if (pdf_name_eq(ctx, name, PDF_NAME(I)))
399 cs = load_indexed(ctx, obj);
400
401 else if (pdf_name_eq(ctx, name, PDF_NAME(Separation)))
402 cs = load_devicen(ctx, obj, 0);
403
404 else if (pdf_name_eq(ctx, name, PDF_NAME(DeviceN)))
405 cs = load_devicen(ctx, obj, 1);
406 else if (pdf_name_eq(ctx, name, PDF_NAME(Pattern)))
407 {
408 pdf_obj *pobj;
409
410 pobj = pdf_array_get(ctx, obj, 1);
411 if (!pobj)
412 {
413 cs = fz_keep_colorspace(ctx, fz_device_gray(ctx));
414 break;
415 }
416
417 cs = pdf_load_colorspace(ctx, pobj);
418 }
419 else
420 fz_throw(ctx, FZ_ERROR_SYNTAX, "unknown colorspace %s", pdf_to_name(ctx, name));
421 }
422 fz_always(ctx)
423 {
424 pdf_unmark_obj(ctx, obj);
425 }
426 fz_catch(ctx)
427 {
428 fz_rethrow(ctx);
429 }
430 return cs;
431 }
432 }
433 }
434
435 /* We have seen files where /DefaultRGB is specified as 1 0 R,
436 * and 1 0 obj << /Length 3144 /Alternate /DeviceRGB /N 3 >>
437 * stream ...iccprofile... endstream endobj.
438 * This *should* be [ /ICCBased 1 0 R ], but Acrobat seems to
439 * handle it, so do our best. */
440 else if (pdf_is_dict(ctx, obj))
441 {
442 return load_icc_based(ctx, obj, 1);
443 }
444
445 fz_throw(ctx, FZ_ERROR_SYNTAX, "could not parse color space (%d 0 R)", pdf_to_num(ctx, obj));
446 }
447
448 fz_colorspace *
pdf_load_colorspace(fz_context * ctx,pdf_obj * obj)449 pdf_load_colorspace(fz_context *ctx, pdf_obj *obj)
450 {
451 fz_colorspace *cs;
452
453 if ((cs = pdf_find_item(ctx, fz_drop_colorspace_imp, obj)) != NULL)
454 {
455 return cs;
456 }
457
458 cs = pdf_load_colorspace_imp(ctx, obj);
459
460 pdf_store_item(ctx, obj, cs, 1000);
461
462 return cs;
463 }
464
465 #if FZ_ENABLE_ICC
466
467 static fz_colorspace *
pdf_load_output_intent(fz_context * ctx,pdf_document * doc)468 pdf_load_output_intent(fz_context *ctx, pdf_document *doc)
469 {
470 pdf_obj *root = pdf_dict_get(ctx, pdf_trailer(ctx, doc), PDF_NAME(Root));
471 pdf_obj *intents = pdf_dict_get(ctx, root, PDF_NAME(OutputIntents));
472 pdf_obj *intent_dict;
473 pdf_obj *dest_profile;
474 fz_colorspace *cs = NULL;
475
476 /* An array of intents */
477 if (!intents)
478 return NULL;
479
480 /* For now, always just use the first intent. I have never even seen a file
481 * with multiple intents but it could happen */
482 intent_dict = pdf_array_get(ctx, intents, 0);
483 if (!intent_dict)
484 return NULL;
485 dest_profile = pdf_dict_get(ctx, intent_dict, PDF_NAME(DestOutputProfile));
486 if (!dest_profile)
487 return NULL;
488
489 fz_var(cs);
490
491 fz_try(ctx)
492 cs = load_icc_based(ctx, dest_profile, 0);
493 fz_catch(ctx)
494 {
495 fz_rethrow_if(ctx, FZ_ERROR_TRYLATER);
496 fz_warn(ctx, "Attempt to read Output Intent failed");
497 }
498 return cs;
499 }
500
501 fz_colorspace *
pdf_document_output_intent(fz_context * ctx,pdf_document * doc)502 pdf_document_output_intent(fz_context *ctx, pdf_document *doc)
503 {
504 if (!doc->oi)
505 doc->oi = pdf_load_output_intent(ctx, doc);
506 return doc->oi;
507 }
508
509 #else
510
511 fz_colorspace *
pdf_document_output_intent(fz_context * ctx,pdf_document * doc)512 pdf_document_output_intent(fz_context *ctx, pdf_document *doc)
513 {
514 return NULL;
515 }
516
517 #endif
518