1 | #include "mupdf/fitz.h" |
2 | #include "fitz-imp.h" |
3 | |
4 | #include <string.h> |
5 | |
6 | fz_device * |
7 | fz_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 | |
14 | static void |
15 | fz_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 | */ |
48 | void |
49 | fz_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 | |
62 | fz_device * |
63 | fz_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 | */ |
73 | void |
74 | fz_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 | |
87 | void |
88 | fz_enable_device_hints(fz_context *ctx, fz_device *dev, int hints) |
89 | { |
90 | dev->hints |= hints; |
91 | } |
92 | |
93 | void |
94 | fz_disable_device_hints(fz_context *ctx, fz_device *dev, int hints) |
95 | { |
96 | dev->hints &= ~hints; |
97 | } |
98 | |
99 | static void |
100 | push_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 | |
121 | static void |
122 | pop_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 | |
132 | static void |
133 | pop_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 | |
143 | void |
144 | fz_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 | |
159 | void |
160 | fz_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 | |
175 | void |
176 | fz_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 | |
194 | void |
195 | 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) |
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 | |
213 | void |
214 | fz_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 | |
229 | void |
230 | fz_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 | |
245 | void |
246 | fz_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 | |
264 | void |
265 | 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) |
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 | |
283 | void |
284 | fz_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 | |
298 | void |
299 | fz_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 | |
315 | void |
316 | fz_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 | |
330 | void |
331 | fz_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 | |
345 | void |
346 | fz_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 | |
361 | void |
362 | fz_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 | |
380 | void |
381 | 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) |
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 | |
397 | void |
398 | fz_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 | |
414 | void |
415 | fz_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 | |
431 | void |
432 | fz_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 | |
448 | void |
449 | fz_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 | |
454 | int |
455 | 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) |
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 | |
479 | void |
480 | fz_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 | |
496 | void |
497 | fz_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 | |
511 | void |
512 | fz_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 | |
526 | void 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 | |
540 | void 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 | */ |
557 | fz_rect |
558 | fz_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 | |