1 #include "mupdf/fitz.h"
2 
3 #include <string.h>
4 
5 fz_device *
fz_new_device_of_size(fz_context * ctx,int size)6 fz_new_device_of_size(fz_context *ctx, int size)
7 {
8 	fz_device *dev = Memento_label(fz_calloc(ctx, 1, size), "fz_device");
9 	dev->refs = 1;
10 	return dev;
11 }
12 
13 static void
fz_disable_device(fz_context * ctx,fz_device * dev)14 fz_disable_device(fz_context *ctx, fz_device *dev)
15 {
16 	dev->close_device = NULL;
17 	dev->fill_path = NULL;
18 	dev->stroke_path = NULL;
19 	dev->clip_path = NULL;
20 	dev->clip_stroke_path = NULL;
21 	dev->fill_text = NULL;
22 	dev->stroke_text = NULL;
23 	dev->clip_text = NULL;
24 	dev->clip_stroke_text = NULL;
25 	dev->ignore_text = NULL;
26 	dev->fill_shade = NULL;
27 	dev->fill_image = NULL;
28 	dev->fill_image_mask = NULL;
29 	dev->clip_image_mask = NULL;
30 	dev->pop_clip = NULL;
31 	dev->begin_mask = NULL;
32 	dev->end_mask = NULL;
33 	dev->begin_group = NULL;
34 	dev->end_group = NULL;
35 	dev->begin_tile = NULL;
36 	dev->end_tile = NULL;
37 	dev->render_flags = NULL;
38 	dev->set_default_colorspaces = NULL;
39 	dev->begin_layer = NULL;
40 	dev->end_layer = NULL;
41 }
42 
43 void
fz_close_device(fz_context * ctx,fz_device * dev)44 fz_close_device(fz_context *ctx, fz_device *dev)
45 {
46 	if (dev == NULL)
47 		return;
48 
49 	fz_try(ctx)
50 	{
51 		if (dev->close_device)
52 			dev->close_device(ctx, dev);
53 	}
54 	fz_always(ctx)
55 		fz_disable_device(ctx, dev);
56 	fz_catch(ctx)
57 		fz_rethrow(ctx);
58 }
59 
60 fz_device *
fz_keep_device(fz_context * ctx,fz_device * dev)61 fz_keep_device(fz_context *ctx, fz_device *dev)
62 {
63 	return fz_keep_imp(ctx, dev, &dev->refs);
64 }
65 
66 void
fz_drop_device(fz_context * ctx,fz_device * dev)67 fz_drop_device(fz_context *ctx, fz_device *dev)
68 {
69 	if (fz_drop_imp(ctx, dev, &dev->refs))
70 	{
71 		if (dev->close_device)
72 			fz_warn(ctx, "dropping unclosed device");
73 		if (dev->drop_device)
74 			dev->drop_device(ctx, dev);
75 		fz_free(ctx, dev->container);
76 		fz_free(ctx, dev);
77 	}
78 }
79 
80 void
fz_enable_device_hints(fz_context * ctx,fz_device * dev,int hints)81 fz_enable_device_hints(fz_context *ctx, fz_device *dev, int hints)
82 {
83 	dev->hints |= hints;
84 }
85 
86 void
fz_disable_device_hints(fz_context * ctx,fz_device * dev,int hints)87 fz_disable_device_hints(fz_context *ctx, fz_device *dev, int hints)
88 {
89 	dev->hints &= ~hints;
90 }
91 
92 static void
push_clip_stack(fz_context * ctx,fz_device * dev,fz_rect rect,int type)93 push_clip_stack(fz_context *ctx, fz_device *dev, fz_rect rect, int type)
94 {
95 	if (dev->container_len == dev->container_cap)
96 	{
97 		int newmax = dev->container_cap * 2;
98 		if (newmax == 0)
99 			newmax = 4;
100 		dev->container = fz_realloc_array(ctx, dev->container, newmax, fz_device_container_stack);
101 		dev->container_cap = newmax;
102 	}
103 	if (dev->container_len == 0)
104 		dev->container[0].scissor = rect;
105 	else
106 	{
107 		dev->container[dev->container_len].scissor = fz_intersect_rect(dev->container[dev->container_len-1].scissor, rect);
108 	}
109 	dev->container[dev->container_len].type = type;
110 	dev->container[dev->container_len].user = 0;
111 	dev->container_len++;
112 }
113 
114 static void
pop_clip_stack(fz_context * ctx,fz_device * dev,int type)115 pop_clip_stack(fz_context *ctx, fz_device *dev, int type)
116 {
117 	if (dev->container_len == 0 || dev->container[dev->container_len-1].type != type)
118 	{
119 		fz_disable_device(ctx, dev);
120 		fz_throw(ctx, FZ_ERROR_GENERIC, "device calls unbalanced");
121 	}
122 	dev->container_len--;
123 }
124 
125 static void
pop_push_clip_stack(fz_context * ctx,fz_device * dev,int pop_type,int push_type)126 pop_push_clip_stack(fz_context *ctx, fz_device *dev, int pop_type, int push_type)
127 {
128 	if (dev->container_len == 0 || dev->container[dev->container_len-1].type != pop_type)
129 	{
130 		fz_disable_device(ctx, dev);
131 		fz_throw(ctx, FZ_ERROR_GENERIC, "device calls unbalanced");
132 	}
133 	dev->container[dev->container_len-1].type = push_type;
134 }
135 
136 void
fz_fill_path(fz_context * ctx,fz_device * dev,const fz_path * path,int even_odd,fz_matrix ctm,fz_colorspace * colorspace,const float * color,float alpha,fz_color_params color_params)137 fz_fill_path(fz_context *ctx, fz_device *dev, const fz_path *path, int even_odd, fz_matrix ctm,
138 	fz_colorspace *colorspace, const float *color, float alpha, fz_color_params color_params)
139 {
140 	if (dev->fill_path)
141 	{
142 		fz_try(ctx)
143 			dev->fill_path(ctx, dev, path, even_odd, ctm, colorspace, color, alpha, color_params);
144 		fz_catch(ctx)
145 		{
146 			fz_disable_device(ctx, dev);
147 			fz_rethrow(ctx);
148 		}
149 	}
150 }
151 
152 void
fz_stroke_path(fz_context * ctx,fz_device * dev,const fz_path * path,const fz_stroke_state * stroke,fz_matrix ctm,fz_colorspace * colorspace,const float * color,float alpha,fz_color_params color_params)153 fz_stroke_path(fz_context *ctx, fz_device *dev, const fz_path *path, const fz_stroke_state *stroke, fz_matrix ctm,
154 	fz_colorspace *colorspace, const float *color, float alpha, fz_color_params color_params)
155 {
156 	if (dev->stroke_path)
157 	{
158 		fz_try(ctx)
159 			dev->stroke_path(ctx, dev, path, stroke, ctm, colorspace, color, alpha, color_params);
160 		fz_catch(ctx)
161 		{
162 			fz_disable_device(ctx, dev);
163 			fz_rethrow(ctx);
164 		}
165 	}
166 }
167 
168 void
fz_clip_path(fz_context * ctx,fz_device * dev,const fz_path * path,int even_odd,fz_matrix ctm,fz_rect scissor)169 fz_clip_path(fz_context *ctx, fz_device *dev, const fz_path *path, int even_odd, fz_matrix ctm, fz_rect scissor)
170 {
171 	fz_rect bbox = fz_bound_path(ctx, path, NULL, ctm);
172 	bbox = fz_intersect_rect(bbox, scissor);
173 	push_clip_stack(ctx, dev, bbox, fz_device_container_stack_is_clip);
174 
175 	if (dev->clip_path)
176 	{
177 		fz_try(ctx)
178 			dev->clip_path(ctx, dev, path, even_odd, ctm, scissor);
179 		fz_catch(ctx)
180 		{
181 			fz_disable_device(ctx, dev);
182 			fz_rethrow(ctx);
183 		}
184 	}
185 }
186 
187 void
fz_clip_stroke_path(fz_context * ctx,fz_device * dev,const fz_path * path,const fz_stroke_state * stroke,fz_matrix ctm,fz_rect scissor)188 fz_clip_stroke_path(fz_context *ctx, fz_device *dev, const fz_path *path, const fz_stroke_state *stroke, fz_matrix ctm, fz_rect scissor)
189 {
190 	fz_rect bbox = fz_bound_path(ctx, path, stroke, ctm);
191 	bbox = fz_intersect_rect(bbox, scissor);
192 	push_clip_stack(ctx, dev, bbox, fz_device_container_stack_is_clip);
193 
194 	if (dev->clip_stroke_path)
195 	{
196 		fz_try(ctx)
197 			dev->clip_stroke_path(ctx, dev, path, stroke, ctm, scissor);
198 		fz_catch(ctx)
199 		{
200 			fz_disable_device(ctx, dev);
201 			fz_rethrow(ctx);
202 		}
203 	}
204 }
205 
206 void
fz_fill_text(fz_context * ctx,fz_device * dev,const fz_text * text,fz_matrix ctm,fz_colorspace * colorspace,const float * color,float alpha,fz_color_params color_params)207 fz_fill_text(fz_context *ctx, fz_device *dev, const fz_text *text, fz_matrix ctm,
208 	fz_colorspace *colorspace, const float *color, float alpha, fz_color_params color_params)
209 {
210 	if (dev->fill_text)
211 	{
212 		fz_try(ctx)
213 			dev->fill_text(ctx, dev, text, ctm, colorspace, color, alpha, color_params);
214 		fz_catch(ctx)
215 		{
216 			fz_disable_device(ctx, dev);
217 			fz_rethrow(ctx);
218 		}
219 	}
220 }
221 
222 void
fz_stroke_text(fz_context * ctx,fz_device * dev,const fz_text * text,const fz_stroke_state * stroke,fz_matrix ctm,fz_colorspace * colorspace,const float * color,float alpha,fz_color_params color_params)223 fz_stroke_text(fz_context *ctx, fz_device *dev, const fz_text *text, const fz_stroke_state *stroke, fz_matrix ctm,
224 	fz_colorspace *colorspace, const float *color, float alpha, fz_color_params color_params)
225 {
226 	if (dev->stroke_text)
227 	{
228 		fz_try(ctx)
229 			dev->stroke_text(ctx, dev, text, stroke, ctm, colorspace, color, alpha, color_params);
230 		fz_catch(ctx)
231 		{
232 			fz_disable_device(ctx, dev);
233 			fz_rethrow(ctx);
234 		}
235 	}
236 }
237 
238 void
fz_clip_text(fz_context * ctx,fz_device * dev,const fz_text * text,fz_matrix ctm,fz_rect scissor)239 fz_clip_text(fz_context *ctx, fz_device *dev, const fz_text *text, fz_matrix ctm, fz_rect scissor)
240 {
241 	fz_rect bbox = fz_bound_text(ctx, text, NULL, ctm);
242 	bbox = fz_intersect_rect(bbox, scissor);
243 	push_clip_stack(ctx, dev, bbox, fz_device_container_stack_is_clip);
244 
245 	if (dev->clip_text)
246 	{
247 		fz_try(ctx)
248 			dev->clip_text(ctx, dev, text, ctm, scissor);
249 		fz_catch(ctx)
250 		{
251 			fz_disable_device(ctx, dev);
252 			fz_rethrow(ctx);
253 		}
254 	}
255 }
256 
257 void
fz_clip_stroke_text(fz_context * ctx,fz_device * dev,const fz_text * text,const fz_stroke_state * stroke,fz_matrix ctm,fz_rect scissor)258 fz_clip_stroke_text(fz_context *ctx, fz_device *dev, const fz_text *text, const fz_stroke_state *stroke, fz_matrix ctm, fz_rect scissor)
259 {
260 	fz_rect bbox = fz_bound_text(ctx, text, stroke, ctm);
261 	bbox = fz_intersect_rect(bbox, scissor);
262 	push_clip_stack(ctx, dev, bbox, fz_device_container_stack_is_clip);
263 
264 	if (dev->clip_stroke_text)
265 	{
266 		fz_try(ctx)
267 			dev->clip_stroke_text(ctx, dev, text, stroke, ctm, scissor);
268 		fz_catch(ctx)
269 		{
270 			fz_disable_device(ctx, dev);
271 			fz_rethrow(ctx);
272 		}
273 	}
274 }
275 
276 void
fz_ignore_text(fz_context * ctx,fz_device * dev,const fz_text * text,fz_matrix ctm)277 fz_ignore_text(fz_context *ctx, fz_device *dev, const fz_text *text, fz_matrix ctm)
278 {
279 	if (dev->ignore_text)
280 	{
281 		fz_try(ctx)
282 			dev->ignore_text(ctx, dev, text, ctm);
283 		fz_catch(ctx)
284 		{
285 			fz_disable_device(ctx, dev);
286 			fz_rethrow(ctx);
287 		}
288 	}
289 }
290 
291 void
fz_pop_clip(fz_context * ctx,fz_device * dev)292 fz_pop_clip(fz_context *ctx, fz_device *dev)
293 {
294 	pop_clip_stack(ctx, dev, fz_device_container_stack_is_clip);
295 
296 	if (dev->pop_clip)
297 	{
298 		fz_try(ctx)
299 			dev->pop_clip(ctx, dev);
300 		fz_catch(ctx)
301 		{
302 			fz_disable_device(ctx, dev);
303 			fz_rethrow(ctx);
304 		}
305 	}
306 }
307 
308 void
fz_fill_shade(fz_context * ctx,fz_device * dev,fz_shade * shade,fz_matrix ctm,float alpha,fz_color_params color_params)309 fz_fill_shade(fz_context *ctx, fz_device *dev, fz_shade *shade, fz_matrix ctm, float alpha, fz_color_params color_params)
310 {
311 	if (dev->fill_shade)
312 	{
313 		fz_try(ctx)
314 			dev->fill_shade(ctx, dev, shade, ctm, alpha, color_params);
315 		fz_catch(ctx)
316 		{
317 			fz_disable_device(ctx, dev);
318 			fz_rethrow(ctx);
319 		}
320 	}
321 }
322 
323 void
fz_fill_image(fz_context * ctx,fz_device * dev,fz_image * image,fz_matrix ctm,float alpha,fz_color_params color_params)324 fz_fill_image(fz_context *ctx, fz_device *dev, fz_image *image, fz_matrix ctm, float alpha, fz_color_params color_params)
325 {
326 	if (dev->fill_image)
327 	{
328 		fz_try(ctx)
329 			dev->fill_image(ctx, dev, image, ctm, alpha, color_params);
330 		fz_catch(ctx)
331 		{
332 			fz_disable_device(ctx, dev);
333 			fz_rethrow(ctx);
334 		}
335 	}
336 }
337 
338 void
fz_fill_image_mask(fz_context * ctx,fz_device * dev,fz_image * image,fz_matrix ctm,fz_colorspace * colorspace,const float * color,float alpha,fz_color_params color_params)339 fz_fill_image_mask(fz_context *ctx, fz_device *dev, fz_image *image, fz_matrix ctm,
340 	fz_colorspace *colorspace, const float *color, float alpha, fz_color_params color_params)
341 {
342 	if (dev->fill_image_mask)
343 	{
344 		fz_try(ctx)
345 			dev->fill_image_mask(ctx, dev, image, ctm, colorspace, color, alpha, color_params);
346 		fz_catch(ctx)
347 		{
348 			fz_disable_device(ctx, dev);
349 			fz_rethrow(ctx);
350 		}
351 	}
352 }
353 
354 void
fz_clip_image_mask(fz_context * ctx,fz_device * dev,fz_image * image,fz_matrix ctm,fz_rect scissor)355 fz_clip_image_mask(fz_context *ctx, fz_device *dev, fz_image *image, fz_matrix ctm, fz_rect scissor)
356 {
357 	fz_rect bbox = fz_transform_rect(fz_unit_rect, ctm);
358 	bbox = fz_intersect_rect(bbox, scissor);
359 	push_clip_stack(ctx, dev, bbox, fz_device_container_stack_is_clip);
360 
361 	if (dev->clip_image_mask)
362 	{
363 		fz_try(ctx)
364 			dev->clip_image_mask(ctx, dev, image, ctm, scissor);
365 		fz_catch(ctx)
366 		{
367 			fz_disable_device(ctx, dev);
368 			fz_rethrow(ctx);
369 		}
370 	}
371 }
372 
373 void
fz_begin_mask(fz_context * ctx,fz_device * dev,fz_rect area,int luminosity,fz_colorspace * colorspace,const float * bc,fz_color_params color_params)374 fz_begin_mask(fz_context *ctx, fz_device *dev, fz_rect area, int luminosity, fz_colorspace *colorspace, const float *bc, fz_color_params color_params)
375 {
376 	push_clip_stack(ctx, dev, area, fz_device_container_stack_is_mask);
377 
378 	if (dev->begin_mask)
379 	{
380 		fz_try(ctx)
381 			dev->begin_mask(ctx, dev, area, luminosity, colorspace, bc, color_params);
382 		fz_catch(ctx)
383 		{
384 			fz_disable_device(ctx, dev);
385 			fz_rethrow(ctx);
386 		}
387 	}
388 }
389 
390 void
fz_end_mask(fz_context * ctx,fz_device * dev)391 fz_end_mask(fz_context *ctx, fz_device *dev)
392 {
393 	pop_push_clip_stack(ctx, dev, fz_device_container_stack_is_mask, fz_device_container_stack_is_clip);
394 
395 	if (dev->end_mask)
396 	{
397 		fz_try(ctx)
398 			dev->end_mask(ctx, dev);
399 		fz_catch(ctx)
400 		{
401 			fz_disable_device(ctx, dev);
402 			fz_rethrow(ctx);
403 		}
404 	}
405 }
406 
407 void
fz_begin_group(fz_context * ctx,fz_device * dev,fz_rect area,fz_colorspace * cs,int isolated,int knockout,int blendmode,float alpha)408 fz_begin_group(fz_context *ctx, fz_device *dev, fz_rect area, fz_colorspace *cs, int isolated, int knockout, int blendmode, float alpha)
409 {
410 	push_clip_stack(ctx, dev, area, fz_device_container_stack_is_group);
411 
412 	if (dev->begin_group)
413 	{
414 		fz_try(ctx)
415 			dev->begin_group(ctx, dev, area, cs, isolated, knockout, blendmode, alpha);
416 		fz_catch(ctx)
417 		{
418 			fz_disable_device(ctx, dev);
419 			fz_rethrow(ctx);
420 		}
421 	}
422 }
423 
424 void
fz_end_group(fz_context * ctx,fz_device * dev)425 fz_end_group(fz_context *ctx, fz_device *dev)
426 {
427 	pop_clip_stack(ctx, dev, fz_device_container_stack_is_group);
428 
429 	if (dev->end_group)
430 	{
431 		fz_try(ctx)
432 			dev->end_group(ctx, dev);
433 		fz_catch(ctx)
434 		{
435 			fz_disable_device(ctx, dev);
436 			fz_rethrow(ctx);
437 		}
438 	}
439 }
440 
441 void
fz_begin_tile(fz_context * ctx,fz_device * dev,fz_rect area,fz_rect view,float xstep,float ystep,fz_matrix ctm)442 fz_begin_tile(fz_context *ctx, fz_device *dev, fz_rect area, fz_rect view, float xstep, float ystep, fz_matrix ctm)
443 {
444 	(void)fz_begin_tile_id(ctx, dev, area, view, xstep, ystep, ctm, 0);
445 }
446 
447 int
fz_begin_tile_id(fz_context * ctx,fz_device * dev,fz_rect area,fz_rect view,float xstep,float ystep,fz_matrix ctm,int id)448 fz_begin_tile_id(fz_context *ctx, fz_device *dev, fz_rect area, fz_rect view, float xstep, float ystep, fz_matrix ctm, int id)
449 {
450 	int result = 0;
451 
452 	push_clip_stack(ctx, dev, area, fz_device_container_stack_is_tile);
453 
454 	if (xstep < 0)
455 		xstep = -xstep;
456 	if (ystep < 0)
457 		ystep = -ystep;
458 	if (dev->begin_tile)
459 	{
460 		fz_try(ctx)
461 			result = dev->begin_tile(ctx, dev, area, view, xstep, ystep, ctm, id);
462 		fz_catch(ctx)
463 		{
464 			fz_disable_device(ctx, dev);
465 			fz_rethrow(ctx);
466 		}
467 	}
468 
469 	return result;
470 }
471 
472 void
fz_end_tile(fz_context * ctx,fz_device * dev)473 fz_end_tile(fz_context *ctx, fz_device *dev)
474 {
475 	pop_clip_stack(ctx, dev, fz_device_container_stack_is_tile);
476 
477 	if (dev->end_tile)
478 	{
479 		fz_try(ctx)
480 			dev->end_tile(ctx, dev);
481 		fz_catch(ctx)
482 		{
483 			fz_disable_device(ctx, dev);
484 			fz_rethrow(ctx);
485 		}
486 	}
487 }
488 
489 void
fz_render_flags(fz_context * ctx,fz_device * dev,int set,int clear)490 fz_render_flags(fz_context *ctx, fz_device *dev, int set, int clear)
491 {
492 	if (dev->render_flags)
493 	{
494 		fz_try(ctx)
495 			dev->render_flags(ctx, dev, set, clear);
496 		fz_catch(ctx)
497 		{
498 			fz_disable_device(ctx, dev);
499 			fz_rethrow(ctx);
500 		}
501 	}
502 }
503 
504 void
fz_set_default_colorspaces(fz_context * ctx,fz_device * dev,fz_default_colorspaces * default_cs)505 fz_set_default_colorspaces(fz_context *ctx, fz_device *dev, fz_default_colorspaces *default_cs)
506 {
507 	if (dev->set_default_colorspaces)
508 	{
509 		fz_try(ctx)
510 			dev->set_default_colorspaces(ctx, dev, default_cs);
511 		fz_catch(ctx)
512 		{
513 			fz_disable_device(ctx, dev);
514 			fz_rethrow(ctx);
515 		}
516 	}
517 }
518 
fz_begin_layer(fz_context * ctx,fz_device * dev,const char * layer_name)519 void fz_begin_layer(fz_context *ctx, fz_device *dev, const char *layer_name)
520 {
521 	if (dev->begin_layer)
522 	{
523 		fz_try(ctx)
524 			dev->begin_layer(ctx, dev, layer_name);
525 		fz_catch(ctx)
526 		{
527 			fz_disable_device(ctx, dev);
528 			fz_rethrow(ctx);
529 		}
530 	}
531 }
532 
fz_end_layer(fz_context * ctx,fz_device * dev)533 void fz_end_layer(fz_context *ctx, fz_device *dev)
534 {
535 	if (dev->end_layer)
536 	{
537 		fz_try(ctx)
538 			dev->end_layer(ctx, dev);
539 		fz_catch(ctx)
540 		{
541 			fz_disable_device(ctx, dev);
542 			fz_rethrow(ctx);
543 		}
544 	}
545 }
546 
547 fz_rect
fz_device_current_scissor(fz_context * ctx,fz_device * dev)548 fz_device_current_scissor(fz_context *ctx, fz_device *dev)
549 {
550 	if (dev->container_len > 0)
551 		return dev->container[dev->container_len-1].scissor;
552 	return fz_infinite_rect;
553 }
554