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