1 | #include "mupdf/fitz.h" |
2 | #include "mupdf/pdf.h" |
3 | |
4 | #include <ft2build.h> |
5 | #include FT_FREETYPE_H |
6 | #include FT_ADVANCES_H |
7 | |
8 | #define ALLOWED_TEXT_POS_ERROR (0.001f) |
9 | |
10 | typedef struct pdf_device_s pdf_device; |
11 | |
12 | typedef struct gstate_s gstate; |
13 | |
14 | struct gstate_s |
15 | { |
16 | /* The first few entries aren't really graphics state things, but |
17 | * they are recorded here as they are fundamentally intertwined with |
18 | * the push/pulling of the gstates. */ |
19 | fz_buffer *buf; |
20 | void (*on_pop)(fz_context*,pdf_device*,void *); |
21 | void *on_pop_arg; |
22 | |
23 | /* The graphics state proper */ |
24 | fz_matrix ctm; |
25 | fz_colorspace *colorspace[2]; |
26 | float color[2][4]; |
27 | float alpha[2]; |
28 | fz_stroke_state *stroke_state; |
29 | int font; |
30 | float font_size; |
31 | int text_rendering_mode; |
32 | int knockout; |
33 | }; |
34 | |
35 | /* The image digest information, object reference, as well as indirect reference |
36 | * ID are all stored in doc->resources->image, and so they are maintained |
37 | * through the life of the document not just this page level device. As we |
38 | * encounter images on a page, we will add to the hash table if they are not |
39 | * already present. When we have an image on a particular page, the resource |
40 | * dict will be updated with the proper indirect reference across the document. |
41 | * We do need to maintain some information as to what image resources we have |
42 | * already specified for this page which is the purpose of image_indices |
43 | */ |
44 | |
45 | typedef struct alpha_entry_s alpha_entry; |
46 | |
47 | struct alpha_entry_s |
48 | { |
49 | float alpha; |
50 | int stroke; |
51 | }; |
52 | |
53 | typedef struct group_entry_s group_entry; |
54 | |
55 | struct group_entry_s |
56 | { |
57 | int alpha; |
58 | int isolated; |
59 | int knockout; |
60 | fz_colorspace *colorspace; |
61 | pdf_obj *ref; |
62 | }; |
63 | |
64 | struct pdf_device_s |
65 | { |
66 | fz_device super; |
67 | |
68 | pdf_document *doc; |
69 | pdf_obj *resources; |
70 | |
71 | int in_text; |
72 | |
73 | int num_forms; |
74 | int num_smasks; |
75 | |
76 | int num_gstates; |
77 | int max_gstates; |
78 | gstate *gstates; |
79 | |
80 | int num_imgs; |
81 | int max_imgs; |
82 | int *image_indices; |
83 | |
84 | int num_cid_fonts; |
85 | int max_cid_fonts; |
86 | fz_font **cid_fonts; |
87 | |
88 | int num_alphas; |
89 | int max_alphas; |
90 | alpha_entry *alphas; |
91 | |
92 | int num_groups; |
93 | int max_groups; |
94 | group_entry *groups; |
95 | }; |
96 | |
97 | #define CURRENT_GSTATE(pdev) (&(pdev)->gstates[(pdev)->num_gstates-1]) |
98 | |
99 | /* Helper functions */ |
100 | static void |
101 | pdf_dev_stroke_state(fz_context *ctx, pdf_device *pdev, const fz_stroke_state *stroke_state) |
102 | { |
103 | gstate *gs = CURRENT_GSTATE(pdev); |
104 | |
105 | if (stroke_state == gs->stroke_state) |
106 | return; |
107 | if (gs->stroke_state && !memcmp(stroke_state, gs->stroke_state, sizeof(*stroke_state))) |
108 | return; |
109 | if (!gs->stroke_state || gs->stroke_state->linewidth != stroke_state->linewidth) |
110 | { |
111 | fz_append_printf(ctx, gs->buf, "%g w\n" , stroke_state->linewidth); |
112 | } |
113 | if (!gs->stroke_state || gs->stroke_state->start_cap != stroke_state->start_cap) |
114 | { |
115 | int cap = stroke_state->start_cap; |
116 | /* FIXME: Triangle caps aren't supported in pdf */ |
117 | if (cap == FZ_LINECAP_TRIANGLE) |
118 | cap = FZ_LINECAP_BUTT; |
119 | fz_append_printf(ctx, gs->buf, "%d J\n" , cap); |
120 | } |
121 | if (!gs->stroke_state || gs->stroke_state->linejoin != stroke_state->linejoin) |
122 | { |
123 | int join = stroke_state->linejoin; |
124 | if (join == FZ_LINEJOIN_MITER_XPS) |
125 | join = FZ_LINEJOIN_MITER; |
126 | fz_append_printf(ctx, gs->buf, "%d j\n" , join); |
127 | } |
128 | if (!gs->stroke_state || gs->stroke_state->miterlimit != stroke_state->miterlimit) |
129 | { |
130 | fz_append_printf(ctx, gs->buf, "%g M\n" , stroke_state->miterlimit); |
131 | } |
132 | if (gs->stroke_state == NULL && stroke_state->dash_len == 0) |
133 | {} |
134 | else if (!gs->stroke_state || gs->stroke_state->dash_phase != stroke_state->dash_phase || gs->stroke_state->dash_len != stroke_state->dash_len || |
135 | memcmp(gs->stroke_state->dash_list, stroke_state->dash_list, sizeof(float)*stroke_state->dash_len)) |
136 | { |
137 | int i; |
138 | fz_append_byte(ctx, gs->buf, '['); |
139 | for (i = 0; i < stroke_state->dash_len; i++) |
140 | { |
141 | if (i > 0) |
142 | fz_append_byte(ctx, gs->buf, ' '); |
143 | fz_append_printf(ctx, gs->buf, "%g" , stroke_state->dash_list[i]); |
144 | } |
145 | fz_append_printf(ctx, gs->buf, "]%g d\n" , stroke_state->dash_phase); |
146 | } |
147 | fz_drop_stroke_state(ctx, gs->stroke_state); |
148 | gs->stroke_state = fz_keep_stroke_state(ctx, stroke_state); |
149 | } |
150 | |
151 | typedef struct |
152 | { |
153 | fz_context *ctx; |
154 | fz_buffer *buf; |
155 | } pdf_dev_path_arg; |
156 | |
157 | static void |
158 | pdf_dev_path_moveto(fz_context *ctx, void *arg, float x, float y) |
159 | { |
160 | fz_buffer *buf = (fz_buffer *)arg; |
161 | fz_append_printf(ctx, buf, "%g %g m\n" , x, y); |
162 | } |
163 | |
164 | static void |
165 | pdf_dev_path_lineto(fz_context *ctx, void *arg, float x, float y) |
166 | { |
167 | fz_buffer *buf = (fz_buffer *)arg; |
168 | fz_append_printf(ctx, buf, "%g %g l\n" , x, y); |
169 | } |
170 | |
171 | static void |
172 | pdf_dev_path_curveto(fz_context *ctx, void *arg, float x1, float y1, float x2, float y2, float x3, float y3) |
173 | { |
174 | fz_buffer *buf = (fz_buffer *)arg; |
175 | fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n" , x1, y1, x2, y2, x3, y3); |
176 | } |
177 | |
178 | static void |
179 | pdf_dev_path_close(fz_context *ctx, void *arg) |
180 | { |
181 | fz_buffer *buf = (fz_buffer *)arg; |
182 | fz_append_string(ctx, buf, "h\n" ); |
183 | } |
184 | |
185 | static const fz_path_walker pdf_dev_path_proc = |
186 | { |
187 | pdf_dev_path_moveto, |
188 | pdf_dev_path_lineto, |
189 | pdf_dev_path_curveto, |
190 | pdf_dev_path_close |
191 | }; |
192 | |
193 | static void |
194 | pdf_dev_path(fz_context *ctx, pdf_device *pdev, const fz_path *path) |
195 | { |
196 | gstate *gs = CURRENT_GSTATE(pdev); |
197 | |
198 | fz_walk_path(ctx, path, &pdf_dev_path_proc, (void *)gs->buf); |
199 | } |
200 | |
201 | static void |
202 | pdf_dev_ctm(fz_context *ctx, pdf_device *pdev, fz_matrix ctm) |
203 | { |
204 | fz_matrix inverse; |
205 | gstate *gs = CURRENT_GSTATE(pdev); |
206 | |
207 | if (memcmp(&gs->ctm, &ctm, sizeof(ctm)) == 0) |
208 | return; |
209 | inverse = fz_invert_matrix(gs->ctm); |
210 | inverse = fz_concat(ctm, inverse); |
211 | gs->ctm = ctm; |
212 | fz_append_printf(ctx, gs->buf, "%M cm\n" , &inverse); |
213 | } |
214 | |
215 | static void |
216 | pdf_dev_color(fz_context *ctx, pdf_device *pdev, fz_colorspace *colorspace, const float *color, int stroke, fz_color_params color_params) |
217 | { |
218 | int diff = 0; |
219 | int i; |
220 | int cspace = 0; |
221 | float rgb[FZ_MAX_COLORS]; |
222 | gstate *gs = CURRENT_GSTATE(pdev); |
223 | |
224 | if (colorspace == fz_device_gray(ctx)) |
225 | cspace = 1; |
226 | else if (colorspace == fz_device_rgb(ctx)) |
227 | cspace = 3; |
228 | else if (colorspace == fz_device_cmyk(ctx)) |
229 | cspace = 4; |
230 | |
231 | if (cspace == 0) |
232 | { |
233 | /* If it's an unknown colorspace, fallback to rgb */ |
234 | fz_convert_color(ctx, colorspace, color, fz_device_rgb(ctx), rgb, NULL, color_params); |
235 | color = rgb; |
236 | colorspace = fz_device_rgb(ctx); |
237 | cspace = 3; |
238 | } |
239 | |
240 | if (gs->colorspace[stroke] != colorspace) |
241 | { |
242 | gs->colorspace[stroke] = colorspace; |
243 | diff = 1; |
244 | } |
245 | |
246 | for (i=0; i < cspace; i++) |
247 | if (gs->color[stroke][i] != color[i]) |
248 | { |
249 | gs->color[stroke][i] = color[i]; |
250 | diff = 1; |
251 | } |
252 | |
253 | if (diff == 0) |
254 | return; |
255 | |
256 | switch (cspace + stroke*8) |
257 | { |
258 | case 1: |
259 | fz_append_printf(ctx, gs->buf, "%g g\n" , color[0]); |
260 | break; |
261 | case 3: |
262 | fz_append_printf(ctx, gs->buf, "%g %g %g rg\n" , color[0], color[1], color[2]); |
263 | break; |
264 | case 4: |
265 | fz_append_printf(ctx, gs->buf, "%g %g %g %g k\n" , color[0], color[1], color[2], color[3]); |
266 | break; |
267 | case 1+8: |
268 | fz_append_printf(ctx, gs->buf, "%g G\n" , color[0]); |
269 | break; |
270 | case 3+8: |
271 | fz_append_printf(ctx, gs->buf, "%g %g %g RG\n" , color[0], color[1], color[2]); |
272 | break; |
273 | case 4+8: |
274 | fz_append_printf(ctx, gs->buf, "%g %g %g %g K\n" , color[0], color[1], color[2], color[3]); |
275 | break; |
276 | } |
277 | } |
278 | |
279 | static void |
280 | pdf_dev_alpha(fz_context *ctx, pdf_device *pdev, float alpha, int stroke) |
281 | { |
282 | int i; |
283 | pdf_document *doc = pdev->doc; |
284 | gstate *gs = CURRENT_GSTATE(pdev); |
285 | |
286 | /* If the alpha is unchanged, nothing to do */ |
287 | if (gs->alpha[stroke] == alpha) |
288 | return; |
289 | |
290 | /* Have we sent such an alpha before? */ |
291 | for (i = 0; i < pdev->num_alphas; i++) |
292 | if (pdev->alphas[i].alpha == alpha && pdev->alphas[i].stroke == stroke) |
293 | break; |
294 | |
295 | if (i == pdev->num_alphas) |
296 | { |
297 | pdf_obj *o, *ref; |
298 | |
299 | /* No. Need to make a new one */ |
300 | if (pdev->num_alphas == pdev->max_alphas) |
301 | { |
302 | int newmax = pdev->max_alphas * 2; |
303 | if (newmax == 0) |
304 | newmax = 4; |
305 | pdev->alphas = fz_realloc_array(ctx, pdev->alphas, newmax, alpha_entry); |
306 | pdev->max_alphas = newmax; |
307 | } |
308 | pdev->alphas[i].alpha = alpha; |
309 | pdev->alphas[i].stroke = stroke; |
310 | |
311 | o = pdf_new_dict(ctx, doc, 1); |
312 | fz_try(ctx) |
313 | { |
314 | char text[32]; |
315 | pdf_dict_put_real(ctx, o, (stroke ? PDF_NAME(CA) : PDF_NAME(ca)), alpha); |
316 | fz_snprintf(text, sizeof(text), "ExtGState/Alp%d" , i); |
317 | ref = pdf_add_object(ctx, doc, o); |
318 | pdf_dict_putp_drop(ctx, pdev->resources, text, ref); |
319 | } |
320 | fz_always(ctx) |
321 | { |
322 | pdf_drop_obj(ctx, o); |
323 | } |
324 | fz_catch(ctx) |
325 | { |
326 | fz_rethrow(ctx); |
327 | } |
328 | pdev->num_alphas++; |
329 | } |
330 | fz_append_printf(ctx, gs->buf, "/Alp%d gs\n" , i); |
331 | } |
332 | |
333 | static int |
334 | pdf_dev_add_font_res(fz_context *ctx, pdf_device *pdev, fz_font *font) |
335 | { |
336 | pdf_obj *fres; |
337 | char text[32]; |
338 | int k; |
339 | int num; |
340 | |
341 | /* Check if we already had this one */ |
342 | for (k = 0; k < pdev->num_cid_fonts; k++) |
343 | if (pdev->cid_fonts[k] == font) |
344 | return k; |
345 | |
346 | /* This will add it to the xref if needed */ |
347 | fres = pdf_add_cid_font(ctx, pdev->doc, font); |
348 | |
349 | /* Not there so add to resources */ |
350 | fz_snprintf(text, sizeof(text), "Font/F%d" , pdev->num_cid_fonts); |
351 | pdf_dict_putp_drop(ctx, pdev->resources, text, fres); |
352 | |
353 | /* And add index to our list for this page */ |
354 | if (pdev->num_cid_fonts == pdev->max_cid_fonts) |
355 | { |
356 | int newmax = pdev->max_cid_fonts * 2; |
357 | if (newmax == 0) |
358 | newmax = 4; |
359 | pdev->cid_fonts = fz_realloc_array(ctx, pdev->cid_fonts, newmax, fz_font*); |
360 | pdev->max_cid_fonts = newmax; |
361 | } |
362 | num = pdev->num_cid_fonts++; |
363 | pdev->cid_fonts[num] = fz_keep_font(ctx, font); |
364 | return num; |
365 | } |
366 | |
367 | static void |
368 | pdf_dev_font(fz_context *ctx, pdf_device *pdev, fz_font *font, fz_matrix trm) |
369 | { |
370 | gstate *gs = CURRENT_GSTATE(pdev); |
371 | float font_size = fz_matrix_expansion(trm); |
372 | |
373 | /* If the font is unchanged, nothing to do */ |
374 | if (gs->font >= 0 && pdev->cid_fonts[gs->font] == font && gs->font_size == font_size) |
375 | return; |
376 | |
377 | if (fz_font_t3_procs(ctx, font)) |
378 | fz_throw(ctx, FZ_ERROR_GENERIC, "pdf device does not support type 3 fonts" ); |
379 | if (fz_font_flags(font)->ft_substitute) |
380 | fz_throw(ctx, FZ_ERROR_GENERIC, "pdf device does not support substitute fonts" ); |
381 | if (!pdf_font_writing_supported(font)) |
382 | fz_throw(ctx, FZ_ERROR_GENERIC, "pdf device does not support font types found in this file" ); |
383 | |
384 | gs->font = pdf_dev_add_font_res(ctx, pdev, font); |
385 | gs->font_size = font_size; |
386 | |
387 | fz_append_printf(ctx, gs->buf, "/F%d %g Tf\n" , gs->font, gs->font_size); |
388 | } |
389 | |
390 | static void |
391 | pdf_dev_push_new_buf(fz_context *ctx, pdf_device *pdev, fz_buffer *buf, void (*on_pop)(fz_context*,pdf_device*,void*), void *on_pop_arg) |
392 | { |
393 | if (pdev->num_gstates == pdev->max_gstates) |
394 | { |
395 | int newmax = pdev->max_gstates*2; |
396 | pdev->gstates = fz_realloc_array(ctx, pdev->gstates, newmax, gstate); |
397 | pdev->max_gstates = newmax; |
398 | } |
399 | memcpy(&pdev->gstates[pdev->num_gstates], &pdev->gstates[pdev->num_gstates-1], sizeof(*pdev->gstates)); |
400 | fz_keep_stroke_state(ctx, pdev->gstates[pdev->num_gstates].stroke_state); |
401 | if (buf) |
402 | pdev->gstates[pdev->num_gstates].buf = buf; |
403 | else |
404 | fz_keep_buffer(ctx, pdev->gstates[pdev->num_gstates].buf); |
405 | pdev->gstates[pdev->num_gstates].on_pop = on_pop; |
406 | pdev->gstates[pdev->num_gstates].on_pop_arg = on_pop_arg; |
407 | fz_append_string(ctx, pdev->gstates[pdev->num_gstates].buf, "q\n" ); |
408 | pdev->num_gstates++; |
409 | } |
410 | |
411 | static void |
412 | pdf_dev_push(fz_context *ctx, pdf_device *pdev) |
413 | { |
414 | pdf_dev_push_new_buf(ctx, pdev, NULL, NULL, NULL); |
415 | } |
416 | |
417 | static void * |
418 | pdf_dev_pop(fz_context *ctx, pdf_device *pdev) |
419 | { |
420 | gstate *gs = CURRENT_GSTATE(pdev); |
421 | void *arg = gs->on_pop_arg; |
422 | |
423 | fz_append_string(ctx, gs->buf, "Q\n" ); |
424 | if (gs->on_pop) |
425 | gs->on_pop(ctx, pdev, arg); |
426 | pdev->num_gstates--; |
427 | fz_drop_stroke_state(ctx, pdev->gstates[pdev->num_gstates].stroke_state); |
428 | fz_drop_buffer(ctx, pdev->gstates[pdev->num_gstates].buf); |
429 | return arg; |
430 | } |
431 | |
432 | static void |
433 | pdf_dev_text_span(fz_context *ctx, pdf_device *pdev, fz_text_span *span) |
434 | { |
435 | gstate *gs = CURRENT_GSTATE(pdev); |
436 | fz_matrix trm, tm, tlm, inv_trm, inv_tm; |
437 | fz_matrix inv_tfs; |
438 | fz_point d; |
439 | float adv; |
440 | int dx, dy; |
441 | int i; |
442 | |
443 | if (span->len == 0) |
444 | return; |
445 | |
446 | inv_tfs = fz_scale(1 / gs->font_size, 1 / gs->font_size); |
447 | |
448 | trm = span->trm; |
449 | trm.e = span->items[0].x; |
450 | trm.f = span->items[0].y; |
451 | |
452 | tm = fz_concat(inv_tfs, trm); |
453 | tlm = tm; |
454 | |
455 | inv_tm = fz_invert_matrix(tm); |
456 | inv_trm = fz_invert_matrix(trm); |
457 | |
458 | fz_append_printf(ctx, gs->buf, "%M Tm\n[<" , &tm); |
459 | |
460 | for (i = 0; i < span->len; ++i) |
461 | { |
462 | fz_text_item *it = &span->items[i]; |
463 | if (it->gid < 0) |
464 | continue; |
465 | |
466 | /* transform difference from expected pen position into font units. */ |
467 | d.x = it->x - trm.e; |
468 | d.y = it->y - trm.f; |
469 | d = fz_transform_vector(d, inv_trm); |
470 | dx = (int)(d.x * 1000 + (d.x < 0 ? -0.5f : 0.5f)); |
471 | dy = (int)(d.y * 1000 + (d.y < 0 ? -0.5f : 0.5f)); |
472 | |
473 | trm.e = it->x; |
474 | trm.f = it->y; |
475 | |
476 | if (dx != 0 || dy != 0) |
477 | { |
478 | if (span->wmode == 0 && dy == 0) |
479 | fz_append_printf(ctx, gs->buf, ">%d<" , -dx); |
480 | else if (span->wmode == 1 && dx == 0) |
481 | fz_append_printf(ctx, gs->buf, ">%d<" , -dy); |
482 | else |
483 | { |
484 | /* Calculate offset from start of the previous line */ |
485 | tm = fz_concat(inv_tfs, trm); |
486 | d.x = tm.e - tlm.e; |
487 | d.y = tm.f - tlm.f; |
488 | d = fz_transform_vector(d, inv_tm); |
489 | fz_append_printf(ctx, gs->buf, ">]TJ\n%g %g Td\n[<" , d.x, d.y); |
490 | tlm = tm; |
491 | } |
492 | } |
493 | |
494 | if (fz_font_t3_procs(ctx, span->font)) |
495 | fz_append_printf(ctx, gs->buf, "%02x" , it->gid); |
496 | else |
497 | fz_append_printf(ctx, gs->buf, "%04x" , it->gid); |
498 | |
499 | adv = fz_advance_glyph(ctx, span->font, it->gid, span->wmode); |
500 | if (span->wmode == 0) |
501 | trm = fz_pre_translate(trm, adv, 0); |
502 | else |
503 | trm = fz_pre_translate(trm, 0, adv); |
504 | } |
505 | |
506 | fz_append_string(ctx, gs->buf, ">]TJ\n" ); |
507 | } |
508 | |
509 | static void |
510 | pdf_dev_trm(fz_context *ctx, pdf_device *pdev, int trm) |
511 | { |
512 | gstate *gs = CURRENT_GSTATE(pdev); |
513 | |
514 | if (gs->text_rendering_mode == trm) |
515 | return; |
516 | gs->text_rendering_mode = trm; |
517 | fz_append_printf(ctx, gs->buf, "%d Tr\n" , trm); |
518 | } |
519 | |
520 | static void |
521 | pdf_dev_begin_text(fz_context *ctx, pdf_device *pdev, int trm) |
522 | { |
523 | pdf_dev_trm(ctx, pdev, trm); |
524 | if (!pdev->in_text) |
525 | { |
526 | gstate *gs = CURRENT_GSTATE(pdev); |
527 | fz_append_string(ctx, gs->buf, "BT\n" ); |
528 | pdev->in_text = 1; |
529 | } |
530 | } |
531 | |
532 | static void |
533 | pdf_dev_end_text(fz_context *ctx, pdf_device *pdev) |
534 | { |
535 | gstate *gs = CURRENT_GSTATE(pdev); |
536 | |
537 | if (!pdev->in_text) |
538 | return; |
539 | pdev->in_text = 0; |
540 | fz_append_string(ctx, gs->buf, "ET\n" ); |
541 | } |
542 | |
543 | static int |
544 | pdf_dev_new_form(fz_context *ctx, pdf_obj **form_ref, pdf_device *pdev, fz_rect bbox, int isolated, int knockout, float alpha, fz_colorspace *colorspace) |
545 | { |
546 | pdf_document *doc = pdev->doc; |
547 | int num; |
548 | pdf_obj *group_ref = NULL; |
549 | pdf_obj *group; |
550 | pdf_obj *form; |
551 | |
552 | *form_ref = NULL; |
553 | |
554 | /* Find (or make) a new group with the required options. */ |
555 | for(num = 0; num < pdev->num_groups; num++) |
556 | { |
557 | group_entry *g = &pdev->groups[num]; |
558 | if (g->isolated == isolated && g->knockout == knockout && g->alpha == alpha && g->colorspace == colorspace) |
559 | { |
560 | group_ref = pdev->groups[num].ref; |
561 | break; |
562 | } |
563 | } |
564 | |
565 | /* If we didn't find one, make one */ |
566 | if (num == pdev->num_groups) |
567 | { |
568 | if (pdev->num_groups == pdev->max_groups) |
569 | { |
570 | int newmax = pdev->max_groups * 2; |
571 | if (newmax == 0) |
572 | newmax = 4; |
573 | pdev->groups = fz_realloc_array(ctx, pdev->groups, newmax, group_entry); |
574 | pdev->max_groups = newmax; |
575 | } |
576 | pdev->num_groups++; |
577 | pdev->groups[num].isolated = isolated; |
578 | pdev->groups[num].knockout = knockout; |
579 | pdev->groups[num].alpha = alpha; |
580 | pdev->groups[num].colorspace = fz_keep_colorspace(ctx, colorspace); |
581 | pdev->groups[num].ref = NULL; |
582 | group = pdf_new_dict(ctx, doc, 5); |
583 | fz_try(ctx) |
584 | { |
585 | pdf_dict_put(ctx, group, PDF_NAME(Type), PDF_NAME(Group)); |
586 | pdf_dict_put(ctx, group, PDF_NAME(S), PDF_NAME(Transparency)); |
587 | pdf_dict_put_bool(ctx, group, PDF_NAME(K), knockout); |
588 | pdf_dict_put_bool(ctx, group, PDF_NAME(I), isolated); |
589 | switch (fz_colorspace_type(ctx, colorspace)) |
590 | { |
591 | case FZ_COLORSPACE_GRAY: |
592 | pdf_dict_put(ctx, group, PDF_NAME(CS), PDF_NAME(DeviceGray)); |
593 | break; |
594 | case FZ_COLORSPACE_RGB: |
595 | pdf_dict_put(ctx, group, PDF_NAME(CS), PDF_NAME(DeviceRGB)); |
596 | break; |
597 | case FZ_COLORSPACE_CMYK: |
598 | pdf_dict_put(ctx, group, PDF_NAME(CS), PDF_NAME(DeviceCMYK)); |
599 | break; |
600 | default: |
601 | break; |
602 | } |
603 | group_ref = pdev->groups[num].ref = pdf_add_object(ctx, doc, group); |
604 | } |
605 | fz_always(ctx) |
606 | { |
607 | pdf_drop_obj(ctx, group); |
608 | } |
609 | fz_catch(ctx) |
610 | { |
611 | fz_rethrow(ctx); |
612 | } |
613 | } |
614 | |
615 | /* Make us a new Forms object that points to that group, and change |
616 | * to writing into the buffer for that Forms object. */ |
617 | form = pdf_new_dict(ctx, doc, 4); |
618 | fz_try(ctx) |
619 | { |
620 | pdf_dict_put(ctx, form, PDF_NAME(Subtype), PDF_NAME(Form)); |
621 | pdf_dict_put(ctx, form, PDF_NAME(Group), group_ref); |
622 | pdf_dict_put_int(ctx, form, PDF_NAME(FormType), 1); |
623 | pdf_dict_put_rect(ctx, form, PDF_NAME(BBox), bbox); |
624 | *form_ref = pdf_add_object(ctx, doc, form); |
625 | } |
626 | fz_always(ctx) |
627 | { |
628 | pdf_drop_obj(ctx, form); |
629 | } |
630 | fz_catch(ctx) |
631 | { |
632 | fz_rethrow(ctx); |
633 | } |
634 | |
635 | /* Insert the new form object into the resources */ |
636 | { |
637 | char text[32]; |
638 | num = pdev->num_forms++; |
639 | fz_snprintf(text, sizeof(text), "XObject/Fm%d" , num); |
640 | pdf_dict_putp(ctx, pdev->resources, text, *form_ref); |
641 | } |
642 | |
643 | return num; |
644 | } |
645 | |
646 | /* Entry points */ |
647 | |
648 | static void |
649 | pdf_dev_fill_path(fz_context *ctx, fz_device *dev, const fz_path *path, int even_odd, fz_matrix ctm, |
650 | fz_colorspace *colorspace, const float *color, float alpha, fz_color_params color_params) |
651 | { |
652 | pdf_device *pdev = (pdf_device*)dev; |
653 | gstate *gs = CURRENT_GSTATE(pdev); |
654 | |
655 | pdf_dev_end_text(ctx, pdev); |
656 | pdf_dev_alpha(ctx, pdev, alpha, 0); |
657 | pdf_dev_color(ctx, pdev, colorspace, color, 0, color_params); |
658 | pdf_dev_ctm(ctx, pdev, ctm); |
659 | pdf_dev_path(ctx, pdev, path); |
660 | fz_append_string(ctx, gs->buf, (even_odd ? "f*\n" : "f\n" )); |
661 | } |
662 | |
663 | static void |
664 | pdf_dev_stroke_path(fz_context *ctx, fz_device *dev, const fz_path *path, const fz_stroke_state *stroke, fz_matrix ctm, |
665 | fz_colorspace *colorspace, const float *color, float alpha, fz_color_params color_params) |
666 | { |
667 | pdf_device *pdev = (pdf_device*)dev; |
668 | gstate *gs = CURRENT_GSTATE(pdev); |
669 | |
670 | pdf_dev_end_text(ctx, pdev); |
671 | pdf_dev_alpha(ctx, pdev, alpha, 1); |
672 | pdf_dev_color(ctx, pdev, colorspace, color, 1, color_params); |
673 | pdf_dev_ctm(ctx, pdev, ctm); |
674 | pdf_dev_stroke_state(ctx, pdev, stroke); |
675 | pdf_dev_path(ctx, pdev, path); |
676 | fz_append_string(ctx, gs->buf, "S\n" ); |
677 | } |
678 | |
679 | static void |
680 | pdf_dev_clip_path(fz_context *ctx, fz_device *dev, const fz_path *path, int even_odd, fz_matrix ctm, fz_rect scissor) |
681 | { |
682 | pdf_device *pdev = (pdf_device*)dev; |
683 | gstate *gs; |
684 | |
685 | pdf_dev_end_text(ctx, pdev); |
686 | pdf_dev_push(ctx, pdev); |
687 | pdf_dev_ctm(ctx, pdev, ctm); |
688 | pdf_dev_path(ctx, pdev, path); |
689 | gs = CURRENT_GSTATE(pdev); |
690 | fz_append_string(ctx, gs->buf, (even_odd ? "W* n\n" : "W n\n" )); |
691 | } |
692 | |
693 | static void |
694 | pdf_dev_clip_stroke_path(fz_context *ctx, fz_device *dev, const fz_path *path, const fz_stroke_state *stroke, fz_matrix ctm, fz_rect scissor) |
695 | { |
696 | pdf_device *pdev = (pdf_device*)dev; |
697 | gstate *gs; |
698 | |
699 | pdf_dev_end_text(ctx, pdev); |
700 | pdf_dev_push(ctx, pdev); |
701 | /* FIXME: Need to push a group, select a pattern (or shading) here, |
702 | * stroke with the pattern/shading. Then move to defining that pattern |
703 | * with the next calls to the device interface until the next pop |
704 | * when we pop the group. */ |
705 | pdf_dev_ctm(ctx, pdev, ctm); |
706 | pdf_dev_path(ctx, pdev, path); |
707 | gs = CURRENT_GSTATE(pdev); |
708 | fz_append_string(ctx, gs->buf, "W n\n" ); |
709 | } |
710 | |
711 | static void |
712 | pdf_dev_fill_text(fz_context *ctx, fz_device *dev, const fz_text *text, fz_matrix ctm, |
713 | fz_colorspace *colorspace, const float *color, float alpha, fz_color_params color_params) |
714 | { |
715 | pdf_device *pdev = (pdf_device*)dev; |
716 | fz_text_span *span; |
717 | |
718 | pdf_dev_ctm(ctx, pdev, ctm); |
719 | pdf_dev_alpha(ctx, pdev, alpha, 0); |
720 | pdf_dev_color(ctx, pdev, colorspace, color, 0, color_params); |
721 | |
722 | for (span = text->head; span; span = span->next) |
723 | { |
724 | pdf_dev_begin_text(ctx, pdev, 0); |
725 | pdf_dev_font(ctx, pdev, span->font, span->trm); |
726 | pdf_dev_text_span(ctx, pdev, span); |
727 | } |
728 | } |
729 | |
730 | static void |
731 | pdf_dev_stroke_text(fz_context *ctx, fz_device *dev, const fz_text *text, const fz_stroke_state *stroke, fz_matrix ctm, |
732 | fz_colorspace *colorspace, const float *color, float alpha, fz_color_params color_params) |
733 | { |
734 | pdf_device *pdev = (pdf_device*)dev; |
735 | fz_text_span *span; |
736 | |
737 | pdf_dev_ctm(ctx, pdev, ctm); |
738 | pdf_dev_alpha(ctx, pdev, alpha, 1); |
739 | pdf_dev_color(ctx, pdev, colorspace, color, 1, color_params); |
740 | |
741 | for (span = text->head; span; span = span->next) |
742 | { |
743 | pdf_dev_begin_text(ctx, pdev, 1); |
744 | pdf_dev_font(ctx, pdev, span->font, span->trm); |
745 | pdf_dev_text_span(ctx, pdev, span); |
746 | } |
747 | } |
748 | |
749 | static void |
750 | pdf_dev_clip_text(fz_context *ctx, fz_device *dev, const fz_text *text, fz_matrix ctm, fz_rect scissor) |
751 | { |
752 | pdf_device *pdev = (pdf_device*)dev; |
753 | fz_text_span *span; |
754 | |
755 | pdf_dev_end_text(ctx, pdev); |
756 | pdf_dev_push(ctx, pdev); |
757 | |
758 | pdf_dev_ctm(ctx, pdev, ctm); |
759 | |
760 | for (span = text->head; span; span = span->next) |
761 | { |
762 | pdf_dev_begin_text(ctx, pdev, 7); |
763 | pdf_dev_font(ctx, pdev, span->font, span->trm); |
764 | pdf_dev_text_span(ctx, pdev, span); |
765 | } |
766 | } |
767 | |
768 | static void |
769 | pdf_dev_clip_stroke_text(fz_context *ctx, fz_device *dev, const fz_text *text, const fz_stroke_state *stroke, fz_matrix ctm, fz_rect scissor) |
770 | { |
771 | pdf_device *pdev = (pdf_device*)dev; |
772 | fz_text_span *span; |
773 | |
774 | pdf_dev_end_text(ctx, pdev); |
775 | pdf_dev_push(ctx, pdev); |
776 | |
777 | pdf_dev_ctm(ctx, pdev, ctm); |
778 | |
779 | for (span = text->head; span; span = span->next) |
780 | { |
781 | pdf_dev_begin_text(ctx, pdev, 7); |
782 | pdf_dev_font(ctx, pdev, span->font, span->trm); |
783 | pdf_dev_text_span(ctx, pdev, span); |
784 | } |
785 | } |
786 | |
787 | static void |
788 | pdf_dev_ignore_text(fz_context *ctx, fz_device *dev, const fz_text *text, fz_matrix ctm) |
789 | { |
790 | pdf_device *pdev = (pdf_device*)dev; |
791 | fz_text_span *span; |
792 | |
793 | pdf_dev_ctm(ctx, pdev, ctm); |
794 | |
795 | for (span = text->head; span; span = span->next) |
796 | { |
797 | pdf_dev_begin_text(ctx, pdev, 0); |
798 | pdf_dev_font(ctx, pdev, span->font, span->trm); |
799 | pdf_dev_text_span(ctx, pdev, span); |
800 | } |
801 | } |
802 | |
803 | static void |
804 | pdf_dev_add_image_res(fz_context *ctx, fz_device *dev, pdf_obj *im_res) |
805 | { |
806 | char text[32]; |
807 | pdf_device *pdev = (pdf_device*)dev; |
808 | int k; |
809 | int num; |
810 | |
811 | /* Check if we already had this one */ |
812 | for (k = 0; k < pdev->num_imgs; k++) |
813 | { |
814 | if (pdev->image_indices[k] == pdf_to_num(ctx, im_res)) |
815 | return; |
816 | } |
817 | |
818 | /* Not there so add to resources */ |
819 | fz_snprintf(text, sizeof(text), "XObject/Img%d" , pdf_to_num(ctx, im_res)); |
820 | pdf_dict_putp(ctx, pdev->resources, text, im_res); |
821 | |
822 | /* And add index to our list for this page */ |
823 | if (pdev->num_imgs == pdev->max_imgs) |
824 | { |
825 | int newmax = pdev->max_imgs * 2; |
826 | if (newmax == 0) |
827 | newmax = 4; |
828 | pdev->image_indices = fz_realloc_array(ctx, pdev->image_indices, newmax, int); |
829 | pdev->max_imgs = newmax; |
830 | } |
831 | num = pdev->num_imgs++; |
832 | pdev->image_indices[num] = pdf_to_num(ctx, im_res); |
833 | } |
834 | |
835 | static void |
836 | pdf_dev_fill_image(fz_context *ctx, fz_device *dev, fz_image *image, fz_matrix ctm, float alpha, fz_color_params color_params) |
837 | { |
838 | pdf_device *pdev = (pdf_device*)dev; |
839 | pdf_obj *im_res; |
840 | gstate *gs = CURRENT_GSTATE(pdev); |
841 | |
842 | pdf_dev_end_text(ctx, pdev); |
843 | im_res = pdf_add_image(ctx, pdev->doc, image); |
844 | if (im_res == NULL) |
845 | { |
846 | fz_warn(ctx, "pdf_add_image: problem adding image resource" ); |
847 | return; |
848 | } |
849 | |
850 | fz_try(ctx) |
851 | { |
852 | pdf_dev_alpha(ctx, pdev, alpha, 0); |
853 | |
854 | /* PDF images are upside down, so fiddle the ctm */ |
855 | ctm = fz_pre_scale(ctm, 1, -1); |
856 | ctm = fz_pre_translate(ctm, 0, -1); |
857 | pdf_dev_ctm(ctx, pdev, ctm); |
858 | fz_append_printf(ctx, gs->buf, "/Img%d Do\n" , pdf_to_num(ctx, im_res)); |
859 | |
860 | /* Possibly add to page resources */ |
861 | pdf_dev_add_image_res(ctx, dev, im_res); |
862 | } |
863 | fz_always(ctx) |
864 | pdf_drop_obj(ctx, im_res); |
865 | fz_catch(ctx) |
866 | fz_rethrow(ctx); |
867 | } |
868 | |
869 | static void |
870 | pdf_dev_fill_shade(fz_context *ctx, fz_device *dev, fz_shade *shade, fz_matrix ctm, float alpha, fz_color_params color_params) |
871 | { |
872 | pdf_device *pdev = (pdf_device*)dev; |
873 | |
874 | /* FIXME */ |
875 | pdf_dev_end_text(ctx, pdev); |
876 | } |
877 | |
878 | static void |
879 | pdf_dev_fill_image_mask(fz_context *ctx, fz_device *dev, fz_image *image, fz_matrix ctm, |
880 | fz_colorspace *colorspace, const float *color, float alpha, fz_color_params color_params) |
881 | { |
882 | pdf_device *pdev = (pdf_device*)dev; |
883 | pdf_obj *im_res = NULL; |
884 | gstate *gs = CURRENT_GSTATE(pdev); |
885 | |
886 | pdf_dev_end_text(ctx, pdev); |
887 | im_res = pdf_add_image(ctx, pdev->doc, image); |
888 | if (im_res == NULL) |
889 | { |
890 | fz_warn(ctx, "pdf_add_image: problem adding image resource" ); |
891 | return; |
892 | } |
893 | |
894 | fz_try(ctx) |
895 | { |
896 | fz_append_string(ctx, gs->buf, "q\n" ); |
897 | pdf_dev_alpha(ctx, pdev, alpha, 0); |
898 | pdf_dev_color(ctx, pdev, colorspace, color, 0, color_params); |
899 | |
900 | /* PDF images are upside down, so fiddle the ctm */ |
901 | ctm = fz_pre_scale(ctm, 1, -1); |
902 | ctm = fz_pre_translate(ctm, 0, -1); |
903 | pdf_dev_ctm(ctx, pdev, ctm); |
904 | fz_append_printf(ctx, gs->buf, "/Img%d Do Q\n" , pdf_to_num(ctx, im_res)); |
905 | |
906 | /* Possibly add to page resources */ |
907 | pdf_dev_add_image_res(ctx, dev, im_res); |
908 | } |
909 | fz_always(ctx) |
910 | pdf_drop_obj(ctx, im_res); |
911 | fz_catch(ctx) |
912 | fz_rethrow(ctx); |
913 | } |
914 | |
915 | static void |
916 | pdf_dev_clip_image_mask(fz_context *ctx, fz_device *dev, fz_image *image, fz_matrix ctm, fz_rect scissor) |
917 | { |
918 | pdf_device *pdev = (pdf_device*)dev; |
919 | |
920 | /* FIXME */ |
921 | pdf_dev_end_text(ctx, pdev); |
922 | pdf_dev_push(ctx, pdev); |
923 | } |
924 | |
925 | static void |
926 | pdf_dev_pop_clip(fz_context *ctx, fz_device *dev) |
927 | { |
928 | pdf_device *pdev = (pdf_device*)dev; |
929 | |
930 | /* FIXME */ |
931 | pdf_dev_end_text(ctx, pdev); |
932 | pdf_dev_pop(ctx, pdev); |
933 | } |
934 | |
935 | static void |
936 | pdf_dev_begin_mask(fz_context *ctx, fz_device *dev, fz_rect bbox, int luminosity, fz_colorspace *colorspace, const float *color, fz_color_params color_params) |
937 | { |
938 | pdf_device *pdev = (pdf_device*)dev; |
939 | gstate *gs; |
940 | pdf_obj *smask = NULL; |
941 | char egsname[32]; |
942 | pdf_obj *egs = NULL; |
943 | pdf_obj *egss; |
944 | pdf_obj *form_ref; |
945 | pdf_obj *color_obj = NULL; |
946 | int i, n; |
947 | |
948 | fz_var(smask); |
949 | fz_var(egs); |
950 | fz_var(color_obj); |
951 | |
952 | pdf_dev_end_text(ctx, pdev); |
953 | |
954 | /* Make a new form to contain the contents of the softmask */ |
955 | pdf_dev_new_form(ctx, &form_ref, pdev, bbox, 0, 0, 1, colorspace); |
956 | |
957 | fz_try(ctx) |
958 | { |
959 | fz_snprintf(egsname, sizeof(egsname), "SM%d" , pdev->num_smasks++); |
960 | egss = pdf_dict_get(ctx, pdev->resources, PDF_NAME(ExtGState)); |
961 | if (!egss) |
962 | egss = pdf_dict_put_dict(ctx, pdev->resources, PDF_NAME(ExtGState), 10); |
963 | egs = pdf_dict_puts_dict(ctx, egss, egsname, 1); |
964 | |
965 | pdf_dict_put(ctx, egs, PDF_NAME(Type), PDF_NAME(ExtGState)); |
966 | smask = pdf_dict_put_dict(ctx, egs, PDF_NAME(SMask), 4); |
967 | |
968 | pdf_dict_put(ctx, smask, PDF_NAME(Type), PDF_NAME(Mask)); |
969 | pdf_dict_put(ctx, smask, PDF_NAME(S), (luminosity ? PDF_NAME(Luminosity) : PDF_NAME(Alpha))); |
970 | pdf_dict_put(ctx, smask, PDF_NAME(G), form_ref); |
971 | |
972 | n = fz_colorspace_n(ctx, colorspace); |
973 | color_obj = pdf_dict_put_array(ctx, smask, PDF_NAME(BC), n); |
974 | for (i = 0; i < n; i++) |
975 | pdf_array_push_real(ctx, color_obj, color[i]); |
976 | |
977 | gs = CURRENT_GSTATE(pdev); |
978 | fz_append_printf(ctx, gs->buf, "/SM%d gs\n" , pdev->num_smasks-1); |
979 | } |
980 | fz_catch(ctx) |
981 | { |
982 | pdf_drop_obj(ctx, form_ref); |
983 | fz_rethrow(ctx); |
984 | } |
985 | |
986 | /* Now, everything we get until the end_mask needs to go into a |
987 | * new buffer, which will be the stream contents for the form. */ |
988 | pdf_dev_push_new_buf(ctx, pdev, fz_new_buffer(ctx, 1024), NULL, form_ref); |
989 | } |
990 | |
991 | static void |
992 | pdf_dev_end_mask(fz_context *ctx, fz_device *dev) |
993 | { |
994 | pdf_device *pdev = (pdf_device*)dev; |
995 | pdf_document *doc = pdev->doc; |
996 | gstate *gs = CURRENT_GSTATE(pdev); |
997 | pdf_obj *form_ref = (pdf_obj *)gs->on_pop_arg; |
998 | |
999 | /* Here we do part of the pop, but not all of it. */ |
1000 | pdf_dev_end_text(ctx, pdev); |
1001 | fz_append_string(ctx, gs->buf, "Q\n" ); |
1002 | pdf_update_stream(ctx, doc, form_ref, gs->buf, 0); |
1003 | fz_drop_buffer(ctx, gs->buf); |
1004 | gs->buf = fz_keep_buffer(ctx, gs[-1].buf); |
1005 | gs->on_pop_arg = NULL; |
1006 | pdf_drop_obj(ctx, form_ref); |
1007 | fz_append_string(ctx, gs->buf, "q\n" ); |
1008 | } |
1009 | |
1010 | static void |
1011 | pdf_dev_begin_group(fz_context *ctx, fz_device *dev, fz_rect bbox, fz_colorspace *cs, int isolated, int knockout, int blendmode, float alpha) |
1012 | { |
1013 | pdf_device *pdev = (pdf_device*)dev; |
1014 | pdf_document *doc = pdev->doc; |
1015 | int num; |
1016 | pdf_obj *form_ref; |
1017 | gstate *gs; |
1018 | |
1019 | pdf_dev_end_text(ctx, pdev); |
1020 | |
1021 | num = pdf_dev_new_form(ctx, &form_ref, pdev, bbox, isolated, knockout, alpha, cs); |
1022 | |
1023 | /* Do we have an appropriate blending extgstate already? */ |
1024 | { |
1025 | char text[32]; |
1026 | pdf_obj *obj; |
1027 | fz_snprintf(text, sizeof(text), "ExtGState/BlendMode%d" , blendmode); |
1028 | obj = pdf_dict_getp(ctx, pdev->resources, text); |
1029 | if (obj == NULL) |
1030 | { |
1031 | /* No, better make one */ |
1032 | obj = pdf_new_dict(ctx, doc, 2); |
1033 | pdf_dict_put(ctx, obj, PDF_NAME(Type), PDF_NAME(ExtGState)); |
1034 | pdf_dict_put_name(ctx, obj, PDF_NAME(BM), fz_blendmode_name(blendmode)); |
1035 | pdf_dict_putp_drop(ctx, pdev->resources, text, obj); |
1036 | } |
1037 | } |
1038 | |
1039 | /* Add the call to this group */ |
1040 | gs = CURRENT_GSTATE(pdev); |
1041 | fz_append_printf(ctx, gs->buf, "/BlendMode%d gs /Fm%d Do\n" , blendmode, num); |
1042 | |
1043 | /* Now, everything we get until the end of group needs to go into a |
1044 | * new buffer, which will be the stream contents for the form. */ |
1045 | pdf_dev_push_new_buf(ctx, pdev, fz_new_buffer(ctx, 1024), NULL, form_ref); |
1046 | } |
1047 | |
1048 | static void |
1049 | pdf_dev_end_group(fz_context *ctx, fz_device *dev) |
1050 | { |
1051 | pdf_device *pdev = (pdf_device*)dev; |
1052 | pdf_document *doc = pdev->doc; |
1053 | gstate *gs = CURRENT_GSTATE(pdev); |
1054 | fz_buffer *buf = fz_keep_buffer(ctx, gs->buf); |
1055 | pdf_obj *form_ref; |
1056 | |
1057 | pdf_dev_end_text(ctx, pdev); |
1058 | form_ref = (pdf_obj *)pdf_dev_pop(ctx, pdev); |
1059 | pdf_update_stream(ctx, doc, form_ref, buf, 0); |
1060 | fz_drop_buffer(ctx, buf); |
1061 | pdf_drop_obj(ctx, form_ref); |
1062 | } |
1063 | |
1064 | static int |
1065 | pdf_dev_begin_tile(fz_context *ctx, fz_device *dev, fz_rect area, fz_rect view, float xstep, float ystep, fz_matrix ctm, int id) |
1066 | { |
1067 | pdf_device *pdev = (pdf_device*)dev; |
1068 | |
1069 | /* FIXME */ |
1070 | pdf_dev_end_text(ctx, pdev); |
1071 | return 0; |
1072 | } |
1073 | |
1074 | static void |
1075 | pdf_dev_end_tile(fz_context *ctx, fz_device *dev) |
1076 | { |
1077 | pdf_device *pdev = (pdf_device*)dev; |
1078 | |
1079 | /* FIXME */ |
1080 | pdf_dev_end_text(ctx, pdev); |
1081 | } |
1082 | |
1083 | static void |
1084 | pdf_dev_close_device(fz_context *ctx, fz_device *dev) |
1085 | { |
1086 | pdf_device *pdev = (pdf_device*)dev; |
1087 | pdf_dev_end_text(ctx, pdev); |
1088 | } |
1089 | |
1090 | static void |
1091 | pdf_dev_drop_device(fz_context *ctx, fz_device *dev) |
1092 | { |
1093 | pdf_device *pdev = (pdf_device*)dev; |
1094 | int i; |
1095 | |
1096 | for (i = pdev->num_gstates-1; i >= 0; i--) |
1097 | { |
1098 | fz_drop_buffer(ctx, pdev->gstates[i].buf); |
1099 | fz_drop_stroke_state(ctx, pdev->gstates[i].stroke_state); |
1100 | } |
1101 | |
1102 | for (i = pdev->num_cid_fonts-1; i >= 0; i--) |
1103 | fz_drop_font(ctx, pdev->cid_fonts[i]); |
1104 | |
1105 | for (i = pdev->num_groups - 1; i >= 0; i--) |
1106 | { |
1107 | pdf_drop_obj(ctx, pdev->groups[i].ref); |
1108 | fz_drop_colorspace(ctx, pdev->groups[i].colorspace); |
1109 | } |
1110 | |
1111 | pdf_drop_obj(ctx, pdev->resources); |
1112 | fz_free(ctx, pdev->cid_fonts); |
1113 | fz_free(ctx, pdev->image_indices); |
1114 | fz_free(ctx, pdev->groups); |
1115 | fz_free(ctx, pdev->alphas); |
1116 | fz_free(ctx, pdev->gstates); |
1117 | } |
1118 | |
1119 | /* |
1120 | Create a pdf device. Rendering to the device creates |
1121 | new pdf content. WARNING: this device is work in progress. It doesn't |
1122 | currently support all rendering cases. |
1123 | |
1124 | Note that contents must be a stream (dictionary) to be updated (or |
1125 | a reference to a stream). Callers should take care to ensure that it |
1126 | is not an array, and that is it not shared with other objects/pages. |
1127 | */ |
1128 | fz_device *pdf_new_pdf_device(fz_context *ctx, pdf_document *doc, fz_matrix topctm, fz_rect mediabox, pdf_obj *resources, fz_buffer *buf) |
1129 | { |
1130 | pdf_device *dev = fz_new_derived_device(ctx, pdf_device); |
1131 | |
1132 | dev->super.close_device = pdf_dev_close_device; |
1133 | dev->super.drop_device = pdf_dev_drop_device; |
1134 | |
1135 | dev->super.fill_path = pdf_dev_fill_path; |
1136 | dev->super.stroke_path = pdf_dev_stroke_path; |
1137 | dev->super.clip_path = pdf_dev_clip_path; |
1138 | dev->super.clip_stroke_path = pdf_dev_clip_stroke_path; |
1139 | |
1140 | dev->super.fill_text = pdf_dev_fill_text; |
1141 | dev->super.stroke_text = pdf_dev_stroke_text; |
1142 | dev->super.clip_text = pdf_dev_clip_text; |
1143 | dev->super.clip_stroke_text = pdf_dev_clip_stroke_text; |
1144 | dev->super.ignore_text = pdf_dev_ignore_text; |
1145 | |
1146 | dev->super.fill_shade = pdf_dev_fill_shade; |
1147 | dev->super.fill_image = pdf_dev_fill_image; |
1148 | dev->super.fill_image_mask = pdf_dev_fill_image_mask; |
1149 | dev->super.clip_image_mask = pdf_dev_clip_image_mask; |
1150 | |
1151 | dev->super.pop_clip = pdf_dev_pop_clip; |
1152 | |
1153 | dev->super.begin_mask = pdf_dev_begin_mask; |
1154 | dev->super.end_mask = pdf_dev_end_mask; |
1155 | dev->super.begin_group = pdf_dev_begin_group; |
1156 | dev->super.end_group = pdf_dev_end_group; |
1157 | |
1158 | dev->super.begin_tile = pdf_dev_begin_tile; |
1159 | dev->super.end_tile = pdf_dev_end_tile; |
1160 | |
1161 | fz_var(buf); |
1162 | |
1163 | fz_try(ctx) |
1164 | { |
1165 | if (buf) |
1166 | buf = fz_keep_buffer(ctx, buf); |
1167 | else |
1168 | buf = fz_new_buffer(ctx, 256); |
1169 | dev->doc = doc; |
1170 | dev->resources = pdf_keep_obj(ctx, resources); |
1171 | dev->gstates = fz_malloc_struct(ctx, gstate); |
1172 | dev->gstates[0].buf = buf; |
1173 | dev->gstates[0].ctm = fz_identity; // XXX |
1174 | dev->gstates[0].colorspace[0] = fz_device_gray(ctx); |
1175 | dev->gstates[0].colorspace[1] = fz_device_gray(ctx); |
1176 | dev->gstates[0].color[0][0] = 1; |
1177 | dev->gstates[0].color[1][0] = 1; |
1178 | dev->gstates[0].alpha[0] = 1.0f; |
1179 | dev->gstates[0].alpha[1] = 1.0f; |
1180 | dev->gstates[0].font = -1; |
1181 | dev->num_gstates = 1; |
1182 | dev->max_gstates = 1; |
1183 | |
1184 | if (!fz_is_identity(topctm)) |
1185 | fz_append_printf(ctx, buf, "%M cm\n" , &topctm); |
1186 | } |
1187 | fz_catch(ctx) |
1188 | { |
1189 | fz_drop_buffer(ctx, buf); |
1190 | fz_free(ctx, dev); |
1191 | fz_rethrow(ctx); |
1192 | } |
1193 | |
1194 | return (fz_device*)dev; |
1195 | } |
1196 | |
1197 | /* |
1198 | Create a device that will record the |
1199 | graphical operations given to it into a sequence of |
1200 | pdf operations, together with a set of resources. This |
1201 | sequence/set pair can then be used as the basis for |
1202 | adding a page to the document (see pdf_add_page). |
1203 | |
1204 | doc: The document for which these are intended. |
1205 | |
1206 | mediabox: The bbox for the created page. |
1207 | |
1208 | presources: Pointer to a place to put the created |
1209 | resources dictionary. |
1210 | |
1211 | pcontents: Pointer to a place to put the created |
1212 | contents buffer. |
1213 | */ |
1214 | fz_device *pdf_page_write(fz_context *ctx, pdf_document *doc, fz_rect mediabox, pdf_obj **presources, fz_buffer **pcontents) |
1215 | { |
1216 | fz_matrix pagectm = { 1, 0, 0, -1, -mediabox.x0, mediabox.y1 }; |
1217 | *presources = pdf_new_dict(ctx, doc, 0); |
1218 | *pcontents = fz_new_buffer(ctx, 0); |
1219 | return pdf_new_pdf_device(ctx, doc, pagectm, mediabox, *presources, *pcontents); |
1220 | } |
1221 | |