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