1 | #include "mupdf/fitz.h" |
2 | #include "mupdf/pdf.h" |
3 | |
4 | #include <float.h> |
5 | #include <limits.h> |
6 | #include <math.h> |
7 | #include <string.h> |
8 | |
9 | #include <stdio.h> |
10 | |
11 | #include "annotation-icons.h" |
12 | |
13 | #define REPLACEMENT 0xB7 |
14 | #define CIRCLE_MAGIC 0.551915f |
15 | |
16 | static float pdf_write_border_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf) |
17 | { |
18 | float w = pdf_annot_border(ctx, annot); |
19 | fz_append_printf(ctx, buf, "%g w\n" , w); |
20 | return w; |
21 | } |
22 | |
23 | static int pdf_write_stroke_color_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf) |
24 | { |
25 | float color[4]; |
26 | int n; |
27 | pdf_annot_color(ctx, annot, &n, color); |
28 | switch (n) |
29 | { |
30 | default: return 0; |
31 | case 1: fz_append_printf(ctx, buf, "%g G\n" , color[0]); break; |
32 | case 3: fz_append_printf(ctx, buf, "%g %g %g RG\n" , color[0], color[1], color[2]); break; |
33 | case 4: fz_append_printf(ctx, buf, "%g %g %g %g K\n" , color[0], color[1], color[2], color[3]); break; |
34 | } |
35 | return 1; |
36 | } |
37 | |
38 | static int pdf_is_dark_fill_color(fz_context *ctx, pdf_annot *annot) |
39 | { |
40 | float color[4], gray; |
41 | int n; |
42 | pdf_annot_color(ctx, annot, &n, color); |
43 | switch (n) |
44 | { |
45 | default: |
46 | gray = 1; |
47 | break; |
48 | case 1: |
49 | gray = color[0]; |
50 | break; |
51 | case 3: |
52 | gray = color[0] * 0.3f + color[1] * 0.59f + color[2] * 0.11f; |
53 | break; |
54 | case 4: |
55 | gray = color[0] * 0.3f + color[1] * 0.59f + color[2] * 0.11f + color[3]; |
56 | gray = 1 - fz_min(gray, 1); |
57 | break; |
58 | } |
59 | return gray < 0.25f; |
60 | } |
61 | |
62 | static int pdf_write_fill_color_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf) |
63 | { |
64 | float color[4]; |
65 | int n; |
66 | pdf_annot_color(ctx, annot, &n, color); |
67 | switch (n) |
68 | { |
69 | default: return 0; |
70 | case 1: fz_append_printf(ctx, buf, "%g g\n" , color[0]); break; |
71 | case 3: fz_append_printf(ctx, buf, "%g %g %g rg\n" , color[0], color[1], color[2]); break; |
72 | case 4: fz_append_printf(ctx, buf, "%g %g %g %g k\n" , color[0], color[1], color[2], color[3]); break; |
73 | } |
74 | return 1; |
75 | } |
76 | |
77 | static int pdf_write_interior_fill_color_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf) |
78 | { |
79 | float color[4]; |
80 | int n; |
81 | pdf_annot_interior_color(ctx, annot, &n, color); |
82 | switch (n) |
83 | { |
84 | default: return 0; |
85 | case 1: fz_append_printf(ctx, buf, "%g g\n" , color[0]); break; |
86 | case 3: fz_append_printf(ctx, buf, "%g %g %g rg\n" , color[0], color[1], color[2]); break; |
87 | case 4: fz_append_printf(ctx, buf, "%g %g %g %g k\n" , color[0], color[1], color[2], color[3]); break; |
88 | } |
89 | return 1; |
90 | } |
91 | |
92 | static int pdf_write_MK_BG_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf) |
93 | { |
94 | float color[4]; |
95 | int n; |
96 | pdf_annot_MK_BG(ctx, annot, &n, color); |
97 | switch (n) |
98 | { |
99 | default: return 0; |
100 | case 1: fz_append_printf(ctx, buf, "%g g\n" , color[0]); break; |
101 | case 3: fz_append_printf(ctx, buf, "%g %g %g rg\n" , color[0], color[1], color[2]); break; |
102 | case 4: fz_append_printf(ctx, buf, "%g %g %g %g k\n" , color[0], color[1], color[2], color[3]); break; |
103 | } |
104 | return 1; |
105 | } |
106 | |
107 | static int pdf_write_MK_BC_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf) |
108 | { |
109 | float color[4]; |
110 | int n; |
111 | pdf_annot_MK_BC(ctx, annot, &n, color); |
112 | switch (n) |
113 | { |
114 | default: return 0; |
115 | case 1: fz_append_printf(ctx, buf, "%g G\n" , color[0]); break; |
116 | case 3: fz_append_printf(ctx, buf, "%g %g %g RG\n" , color[0], color[1], color[2]); break; |
117 | case 4: fz_append_printf(ctx, buf, "%g %g %g %g K\n" , color[0], color[1], color[2], color[3]); break; |
118 | } |
119 | return 1; |
120 | } |
121 | |
122 | static fz_point rotate_vector(float angle, float x, float y) |
123 | { |
124 | float ca = cosf(angle); |
125 | float sa = sinf(angle); |
126 | return fz_make_point(x*ca - y*sa, x*sa + y*ca); |
127 | } |
128 | |
129 | static void pdf_write_arrow_appearance(fz_context *ctx, fz_buffer *buf, fz_rect *rect, float x, float y, float dx, float dy, float w) |
130 | { |
131 | float r = fz_max(1, w); |
132 | float angle = atan2f(dy, dx); |
133 | fz_point v, a, b; |
134 | |
135 | v = rotate_vector(angle, 8.8f*r, 4.5f*r); |
136 | a = fz_make_point(x + v.x, y + v.y); |
137 | v = rotate_vector(angle, 8.8f*r, -4.5f*r); |
138 | b = fz_make_point(x + v.x, y + v.y); |
139 | |
140 | *rect = fz_include_point_in_rect(*rect, a); |
141 | *rect = fz_include_point_in_rect(*rect, b); |
142 | *rect = fz_expand_rect(*rect, w); |
143 | |
144 | fz_append_printf(ctx, buf, "%g %g m\n" , a.x, a.y); |
145 | fz_append_printf(ctx, buf, "%g %g l\n" , x, y); |
146 | fz_append_printf(ctx, buf, "%g %g l\n" , b.x, b.y); |
147 | } |
148 | |
149 | static void include_cap(fz_rect *rect, float x, float y, float r) |
150 | { |
151 | rect->x0 = fz_min(rect->x0, x-r); |
152 | rect->y0 = fz_min(rect->y0, y-r); |
153 | rect->x1 = fz_max(rect->x1, x+r); |
154 | rect->y1 = fz_max(rect->y1, y+r); |
155 | } |
156 | |
157 | static void |
158 | pdf_write_line_cap_appearance(fz_context *ctx, fz_buffer *buf, fz_rect *rect, |
159 | float x, float y, float dx, float dy, float w, int ic, pdf_obj *cap) |
160 | { |
161 | if (cap == PDF_NAME(Square)) |
162 | { |
163 | float r = fz_max(2.5f, w * 2.5f); |
164 | fz_append_printf(ctx, buf, "%g %g %g %g re\n" , x-r, y-r, r*2, r*2); |
165 | fz_append_string(ctx, buf, ic ? "b\n" : "s\n" ); |
166 | include_cap(rect, x, y, r); |
167 | } |
168 | else if (cap == PDF_NAME(Circle)) |
169 | { |
170 | float r = fz_max(2.5f, w * 2.5f); |
171 | float m = r * CIRCLE_MAGIC; |
172 | fz_append_printf(ctx, buf, "%g %g m\n" , x, y+r); |
173 | fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n" , x+m, y+r, x+r, y+m, x+r, y); |
174 | fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n" , x+r, y-m, x+m, y-r, x, y-r); |
175 | fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n" , x-m, y-r, x-r, y-m, x-r, y); |
176 | fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n" , x-r, y+m, x-m, y+r, x, y+r); |
177 | fz_append_string(ctx, buf, ic ? "b\n" : "s\n" ); |
178 | include_cap(rect, x, y, r); |
179 | } |
180 | else if (cap == PDF_NAME(Diamond)) |
181 | { |
182 | float r = fz_max(2.5f, w * 2.5f); |
183 | fz_append_printf(ctx, buf, "%g %g m\n" , x, y+r); |
184 | fz_append_printf(ctx, buf, "%g %g l\n" , x+r, y); |
185 | fz_append_printf(ctx, buf, "%g %g l\n" , x, y-r); |
186 | fz_append_printf(ctx, buf, "%g %g l\n" , x-r, y); |
187 | fz_append_string(ctx, buf, ic ? "b\n" : "s\n" ); |
188 | include_cap(rect, x, y, r); |
189 | } |
190 | else if (cap == PDF_NAME(OpenArrow)) |
191 | { |
192 | pdf_write_arrow_appearance(ctx, buf, rect, x, y, dx, dy, w); |
193 | fz_append_string(ctx, buf, "S\n" ); |
194 | } |
195 | else if (cap == PDF_NAME(ClosedArrow)) |
196 | { |
197 | pdf_write_arrow_appearance(ctx, buf, rect, x, y, dx, dy, w); |
198 | fz_append_string(ctx, buf, ic ? "b\n" : "s\n" ); |
199 | } |
200 | /* PDF 1.5 */ |
201 | else if (cap == PDF_NAME(Butt)) |
202 | { |
203 | float r = fz_max(3, w * 3); |
204 | fz_point a = { x-dy*r, y+dx*r }; |
205 | fz_point b = { x+dy*r, y-dx*r }; |
206 | fz_append_printf(ctx, buf, "%g %g m\n" , a.x, a.y); |
207 | fz_append_printf(ctx, buf, "%g %g l\n" , b.x, b.y); |
208 | fz_append_string(ctx, buf, "S\n" ); |
209 | *rect = fz_include_point_in_rect(*rect, a); |
210 | *rect = fz_include_point_in_rect(*rect, b); |
211 | } |
212 | /* PDF 1.6 */ |
213 | else if (cap == PDF_NAME(ROpenArrow)) |
214 | { |
215 | pdf_write_arrow_appearance(ctx, buf, rect, x, y, -dx, -dy, w); |
216 | fz_append_string(ctx, buf, "S\n" ); |
217 | } |
218 | else if (cap == PDF_NAME(RClosedArrow)) |
219 | { |
220 | pdf_write_arrow_appearance(ctx, buf, rect, x, y, -dx, -dy, w); |
221 | fz_append_string(ctx, buf, ic ? "b\n" : "s\n" ); |
222 | } |
223 | else if (cap == PDF_NAME(Slash)) |
224 | { |
225 | float r = fz_max(5, w * 5); |
226 | float angle = atan2f(dy, dx) - (30 * FZ_PI / 180); |
227 | fz_point a, b, v; |
228 | v = rotate_vector(angle, 0, r); |
229 | a = fz_make_point(x + v.x, y + v.y); |
230 | v = rotate_vector(angle, 0, -r); |
231 | b = fz_make_point(x + v.x, y + v.y); |
232 | fz_append_printf(ctx, buf, "%g %g m\n" , a.x, a.y); |
233 | fz_append_printf(ctx, buf, "%g %g l\n" , b.x, b.y); |
234 | fz_append_string(ctx, buf, "S\n" ); |
235 | *rect = fz_include_point_in_rect(*rect, a); |
236 | *rect = fz_include_point_in_rect(*rect, b); |
237 | } |
238 | } |
239 | |
240 | static void |
241 | pdf_write_line_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect) |
242 | { |
243 | pdf_obj *line, *le; |
244 | fz_point a, b; |
245 | float w; |
246 | int ic; |
247 | |
248 | w = pdf_write_border_appearance(ctx, annot, buf); |
249 | pdf_write_stroke_color_appearance(ctx, annot, buf); |
250 | ic = pdf_write_interior_fill_color_appearance(ctx, annot, buf); |
251 | |
252 | line = pdf_dict_get(ctx, annot->obj, PDF_NAME(L)); |
253 | a.x = pdf_array_get_real(ctx, line, 0); |
254 | a.y = pdf_array_get_real(ctx, line, 1); |
255 | b.x = pdf_array_get_real(ctx, line, 2); |
256 | b.y = pdf_array_get_real(ctx, line, 3); |
257 | |
258 | fz_append_printf(ctx, buf, "%g %g m\n%g %g l\nS\n" , a.x, a.y, b.x, b.y); |
259 | |
260 | rect->x0 = fz_min(a.x, b.x); |
261 | rect->y0 = fz_min(a.y, b.y); |
262 | rect->x1 = fz_max(a.x, b.x); |
263 | rect->y1 = fz_max(a.y, b.y); |
264 | |
265 | le = pdf_dict_get(ctx, annot->obj, PDF_NAME(LE)); |
266 | if (pdf_array_len(ctx, le) == 2) |
267 | { |
268 | float dx = b.x - a.x; |
269 | float dy = b.y - a.y; |
270 | float l = sqrtf(dx*dx + dy*dy); |
271 | pdf_write_line_cap_appearance(ctx, buf, rect, a.x, a.y, dx/l, dy/l, w, ic, pdf_array_get(ctx, le, 0)); |
272 | pdf_write_line_cap_appearance(ctx, buf, rect, b.x, b.y, -dx/l, -dy/l, w, ic, pdf_array_get(ctx, le, 1)); |
273 | } |
274 | *rect = fz_expand_rect(*rect, fz_max(1, w)); |
275 | } |
276 | |
277 | static void |
278 | pdf_write_square_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect) |
279 | { |
280 | float x, y, w, h; |
281 | float lw; |
282 | int ic; |
283 | |
284 | lw = pdf_write_border_appearance(ctx, annot, buf); |
285 | pdf_write_stroke_color_appearance(ctx, annot, buf); |
286 | ic = pdf_write_interior_fill_color_appearance(ctx, annot, buf); |
287 | |
288 | x = rect->x0 + lw; |
289 | y = rect->y0 + lw; |
290 | w = rect->x1 - x - lw; |
291 | h = rect->y1 - y - lw; |
292 | |
293 | fz_append_printf(ctx, buf, "%g %g %g %g re\n" , x, y, w, h); |
294 | fz_append_string(ctx, buf, ic ? "b" : "s" ); |
295 | } |
296 | |
297 | static void |
298 | draw_circle(fz_context *ctx, fz_buffer *buf, const char *op, float rx, float ry, float cx, float cy) |
299 | { |
300 | float mx = rx * CIRCLE_MAGIC; |
301 | float my = ry * CIRCLE_MAGIC; |
302 | fz_append_printf(ctx, buf, "%g %g m\n" , cx, cy+ry); |
303 | fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n" , cx+mx, cy+ry, cx+rx, cy+my, cx+rx, cy); |
304 | fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n" , cx+rx, cy-my, cx+mx, cy-ry, cx, cy-ry); |
305 | fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n" , cx-mx, cy-ry, cx-rx, cy-my, cx-rx, cy); |
306 | fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n" , cx-rx, cy+my, cx-mx, cy+ry, cx, cy+ry); |
307 | fz_append_string(ctx, buf, op); |
308 | } |
309 | |
310 | static void |
311 | draw_circle_in_box(fz_context *ctx, fz_buffer *buf, const char *op, float lw, float x0, float y0, float x1, float y1) |
312 | { |
313 | float rx = (x1 - x0) / 2 - lw; |
314 | float ry = (y1 - y0) / 2 - lw; |
315 | float cx = x0 + lw + rx; |
316 | float cy = y0 + lw + ry; |
317 | draw_circle(ctx, buf, op, rx, ry, cx, cy); |
318 | } |
319 | |
320 | static void |
321 | pdf_write_circle_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect) |
322 | { |
323 | float lw; |
324 | int ic; |
325 | |
326 | lw = pdf_write_border_appearance(ctx, annot, buf); |
327 | pdf_write_stroke_color_appearance(ctx, annot, buf); |
328 | ic = pdf_write_interior_fill_color_appearance(ctx, annot, buf); |
329 | |
330 | draw_circle_in_box(ctx, buf, ic ? "b\n" : "s\n" , lw, rect->x0, rect->y0, rect->x1, rect->y1); |
331 | } |
332 | |
333 | static void |
334 | pdf_write_polygon_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, int close) |
335 | { |
336 | pdf_obj *verts; |
337 | fz_point p; |
338 | int i, n; |
339 | float lw; |
340 | |
341 | lw = pdf_write_border_appearance(ctx, annot, buf); |
342 | pdf_write_stroke_color_appearance(ctx, annot, buf); |
343 | |
344 | *rect = fz_empty_rect; |
345 | |
346 | verts = pdf_dict_get(ctx, annot->obj, PDF_NAME(Vertices)); |
347 | n = pdf_array_len(ctx, verts) / 2; |
348 | if (n > 0) |
349 | { |
350 | for (i = 0; i < n; ++i) |
351 | { |
352 | p.x = pdf_array_get_real(ctx, verts, i*2+0); |
353 | p.y = pdf_array_get_real(ctx, verts, i*2+1); |
354 | if (i == 0) |
355 | { |
356 | rect->x0 = rect->x1 = p.x; |
357 | rect->y0 = rect->y1 = p.y; |
358 | } |
359 | else |
360 | *rect = fz_include_point_in_rect(*rect, p); |
361 | if (i == 0) |
362 | fz_append_printf(ctx, buf, "%g %g m\n" , p.x, p.y); |
363 | else |
364 | fz_append_printf(ctx, buf, "%g %g l\n" , p.x, p.y); |
365 | } |
366 | fz_append_string(ctx, buf, close ? "s" : "S" ); |
367 | *rect = fz_expand_rect(*rect, lw); |
368 | } |
369 | } |
370 | |
371 | static void |
372 | pdf_write_ink_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect) |
373 | { |
374 | pdf_obj *ink_list, *stroke; |
375 | int i, n, k, m; |
376 | float lw; |
377 | fz_point p; |
378 | |
379 | lw = pdf_write_border_appearance(ctx, annot, buf); |
380 | pdf_write_stroke_color_appearance(ctx, annot, buf); |
381 | |
382 | *rect = fz_empty_rect; |
383 | |
384 | ink_list = pdf_dict_get(ctx, annot->obj, PDF_NAME(InkList)); |
385 | n = pdf_array_len(ctx, ink_list); |
386 | for (i = 0; i < n; ++i) |
387 | { |
388 | stroke = pdf_array_get(ctx, ink_list, i); |
389 | m = pdf_array_len(ctx, stroke) / 2; |
390 | for (k = 0; k < m; ++k) |
391 | { |
392 | p.x = pdf_array_get_real(ctx, stroke, k*2+0); |
393 | p.y = pdf_array_get_real(ctx, stroke, k*2+1); |
394 | if (i == 0 && k == 0) |
395 | { |
396 | rect->x0 = rect->x1 = p.x; |
397 | rect->y0 = rect->y1 = p.y; |
398 | } |
399 | else |
400 | *rect = fz_include_point_in_rect(*rect, p); |
401 | fz_append_printf(ctx, buf, "%g %g %c\n" , p.x, p.y, k == 0 ? 'm' : 'l'); |
402 | } |
403 | } |
404 | fz_append_printf(ctx, buf, "S" ); |
405 | *rect = fz_expand_rect(*rect, lw); |
406 | } |
407 | |
408 | /* Contrary to the specification, the points within a QuadPoint are NOT |
409 | * ordered in a counter-clockwise fashion starting with the lower left. |
410 | * Experiments with Adobe's implementation indicates a cross-wise |
411 | * ordering is intended: ul, ur, ll, lr. |
412 | */ |
413 | enum { UL, UR, LL, LR }; |
414 | |
415 | static float |
416 | (fz_context *ctx, fz_point *quad, pdf_obj *obj, int i) |
417 | { |
418 | float dx, dy; |
419 | quad[0].x = pdf_array_get_real(ctx, obj, i+0); |
420 | quad[0].y = pdf_array_get_real(ctx, obj, i+1); |
421 | quad[1].x = pdf_array_get_real(ctx, obj, i+2); |
422 | quad[1].y = pdf_array_get_real(ctx, obj, i+3); |
423 | quad[2].x = pdf_array_get_real(ctx, obj, i+4); |
424 | quad[2].y = pdf_array_get_real(ctx, obj, i+5); |
425 | quad[3].x = pdf_array_get_real(ctx, obj, i+6); |
426 | quad[3].y = pdf_array_get_real(ctx, obj, i+7); |
427 | dx = quad[UL].x - quad[LL].x; |
428 | dy = quad[UL].y - quad[LL].y; |
429 | return sqrtf(dx * dx + dy * dy); |
430 | } |
431 | |
432 | static void |
433 | union_quad(fz_rect *rect, const fz_point quad[4], float lw) |
434 | { |
435 | fz_rect qbox; |
436 | qbox.x0 = fz_min(fz_min(quad[0].x, quad[1].x), fz_min(quad[2].x, quad[3].x)); |
437 | qbox.y0 = fz_min(fz_min(quad[0].y, quad[1].y), fz_min(quad[2].y, quad[3].y)); |
438 | qbox.x1 = fz_max(fz_max(quad[0].x, quad[1].x), fz_max(quad[2].x, quad[3].x)); |
439 | qbox.y1 = fz_max(fz_max(quad[0].y, quad[1].y), fz_max(quad[2].y, quad[3].y)); |
440 | *rect = fz_union_rect(*rect, fz_expand_rect(qbox, lw)); |
441 | } |
442 | |
443 | static fz_point |
444 | lerp_point(fz_point a, fz_point b, float t) |
445 | { |
446 | return fz_make_point(a.x + t * (b.x - a.x), a.y + t * (b.y - a.y)); |
447 | } |
448 | |
449 | static void |
450 | pdf_write_highlight_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, pdf_obj **res) |
451 | { |
452 | pdf_obj *res_egs, *res_egs_h; |
453 | pdf_obj *qp; |
454 | fz_point quad[4], mquad[4], v; |
455 | float opacity, h, m, dx, dy, vn; |
456 | int i, n; |
457 | |
458 | *rect = fz_empty_rect; |
459 | |
460 | /* /Resources << /ExtGState << /H << /Type/ExtGState /BM/Multiply /CA %g >> >> >> */ |
461 | *res = pdf_new_dict(ctx, annot->page->doc, 1); |
462 | res_egs = pdf_dict_put_dict(ctx, *res, PDF_NAME(ExtGState), 1); |
463 | res_egs_h = pdf_dict_put_dict(ctx, res_egs, PDF_NAME(H), 2); |
464 | pdf_dict_put(ctx, res_egs_h, PDF_NAME(Type), PDF_NAME(ExtGState)); |
465 | pdf_dict_put(ctx, res_egs_h, PDF_NAME(BM), PDF_NAME(Multiply)); |
466 | opacity = pdf_annot_opacity(ctx, annot); |
467 | if (opacity < 1) |
468 | pdf_dict_put_real(ctx, res_egs_h, PDF_NAME(ca), opacity); |
469 | |
470 | pdf_write_fill_color_appearance(ctx, annot, buf); |
471 | |
472 | fz_append_printf(ctx, buf, "/H gs\n" ); |
473 | |
474 | qp = pdf_dict_get(ctx, annot->obj, PDF_NAME(QuadPoints)); |
475 | n = pdf_array_len(ctx, qp); |
476 | if (n > 0) |
477 | { |
478 | for (i = 0; i < n; i += 8) |
479 | { |
480 | h = extract_quad(ctx, quad, qp, i); |
481 | m = h / 4.2425f; /* magic number that matches adobe's appearance */ |
482 | dx = quad[LR].x - quad[LL].x; |
483 | dy = quad[LR].y - quad[LL].y; |
484 | vn = sqrtf(dx * dx + dy * dy); |
485 | v = fz_make_point(dx * m / vn, dy * m / vn); |
486 | |
487 | mquad[LL].x = quad[LL].x - v.x - v.y; |
488 | mquad[LL].y = quad[LL].y - v.y + v.x; |
489 | mquad[UL].x = quad[UL].x - v.x + v.y; |
490 | mquad[UL].y = quad[UL].y - v.y - v.x; |
491 | mquad[LR].x = quad[LR].x + v.x - v.y; |
492 | mquad[LR].y = quad[LR].y + v.y + v.x; |
493 | mquad[UR].x = quad[UR].x + v.x + v.y; |
494 | mquad[UR].y = quad[UR].y + v.y - v.x; |
495 | |
496 | fz_append_printf(ctx, buf, "%g %g m\n" , quad[LL].x, quad[LL].y); |
497 | fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n" , |
498 | mquad[LL].x, mquad[LL].y, |
499 | mquad[UL].x, mquad[UL].y, |
500 | quad[UL].x, quad[UL].y); |
501 | fz_append_printf(ctx, buf, "%g %g l\n" , quad[UR].x, quad[UR].y); |
502 | fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n" , |
503 | mquad[UR].x, mquad[UR].y, |
504 | mquad[LR].x, mquad[LR].y, |
505 | quad[LR].x, quad[LR].y); |
506 | fz_append_printf(ctx, buf, "f\n" ); |
507 | |
508 | union_quad(rect, quad, h/16); |
509 | union_quad(rect, mquad, 0); |
510 | } |
511 | } |
512 | } |
513 | |
514 | static void |
515 | pdf_write_underline_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect) |
516 | { |
517 | fz_point quad[4], a, b; |
518 | float h; |
519 | pdf_obj *qp; |
520 | int i, n; |
521 | |
522 | *rect = fz_empty_rect; |
523 | |
524 | pdf_write_stroke_color_appearance(ctx, annot, buf); |
525 | |
526 | qp = pdf_dict_get(ctx, annot->obj, PDF_NAME(QuadPoints)); |
527 | n = pdf_array_len(ctx, qp); |
528 | if (n > 0) |
529 | { |
530 | for (i = 0; i < n; i += 8) |
531 | { |
532 | /* Acrobat draws the line at 1/7 of the box width from the bottom |
533 | * of the box and 1/16 thick of the box width. */ |
534 | |
535 | h = extract_quad(ctx, quad, qp, i); |
536 | a = lerp_point(quad[LL], quad[UL], 1/7.0f); |
537 | b = lerp_point(quad[LR], quad[UR], 1/7.0f); |
538 | |
539 | fz_append_printf(ctx, buf, "%g w\n" , h/16); |
540 | fz_append_printf(ctx, buf, "%g %g m\n" , a.x, a.y); |
541 | fz_append_printf(ctx, buf, "%g %g l\n" , b.x, b.y); |
542 | fz_append_printf(ctx, buf, "S\n" ); |
543 | |
544 | union_quad(rect, quad, h/16); |
545 | } |
546 | } |
547 | } |
548 | |
549 | static void |
550 | pdf_write_strike_out_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect) |
551 | { |
552 | fz_point quad[4], a, b; |
553 | float h; |
554 | pdf_obj *qp; |
555 | int i, n; |
556 | |
557 | pdf_write_stroke_color_appearance(ctx, annot, buf); |
558 | |
559 | qp = pdf_dict_get(ctx, annot->obj, PDF_NAME(QuadPoints)); |
560 | n = pdf_array_len(ctx, qp); |
561 | if (n > 0) |
562 | { |
563 | *rect = fz_empty_rect; |
564 | for (i = 0; i < n; i += 8) |
565 | { |
566 | /* Acrobat draws the line at 3/7 of the box width from the bottom |
567 | * of the box and 1/16 thick of the box width. */ |
568 | |
569 | h = extract_quad(ctx, quad, qp, i); |
570 | a = lerp_point(quad[LL], quad[UL], 3/7.0f); |
571 | b = lerp_point(quad[LR], quad[UR], 3/7.0f); |
572 | |
573 | fz_append_printf(ctx, buf, "%g w\n" , h/16); |
574 | fz_append_printf(ctx, buf, "%g %g m\n" , a.x, a.y); |
575 | fz_append_printf(ctx, buf, "%g %g l\n" , b.x, b.y); |
576 | fz_append_printf(ctx, buf, "S\n" ); |
577 | |
578 | union_quad(rect, quad, h/16); |
579 | } |
580 | } |
581 | } |
582 | |
583 | static void |
584 | pdf_write_squiggly_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect) |
585 | { |
586 | fz_point quad[4], a, b, c, v; |
587 | float h, x, w; |
588 | pdf_obj *qp; |
589 | int i, n; |
590 | |
591 | *rect = fz_empty_rect; |
592 | |
593 | pdf_write_stroke_color_appearance(ctx, annot, buf); |
594 | |
595 | qp = pdf_dict_get(ctx, annot->obj, PDF_NAME(QuadPoints)); |
596 | n = pdf_array_len(ctx, qp); |
597 | if (n > 0) |
598 | { |
599 | for (i = 0; i < n; i += 8) |
600 | { |
601 | int up = 1; |
602 | h = extract_quad(ctx, quad, qp, i); |
603 | v = fz_make_point(quad[LR].x - quad[LL].x, quad[LR].y - quad[LL].y); |
604 | w = sqrtf(v.x * v.x + v.y * v.y); |
605 | x = 0; |
606 | |
607 | fz_append_printf(ctx, buf, "%g w\n" , h/16); |
608 | fz_append_printf(ctx, buf, "%g %g m\n" , quad[LL].x, quad[LL].y); |
609 | while (x < w) |
610 | { |
611 | x += h/7; |
612 | a = lerp_point(quad[LL], quad[LR], x/w); |
613 | if (up) |
614 | { |
615 | b = lerp_point(quad[UL], quad[UR], x/w); |
616 | c = lerp_point(a, b, 1/7.0f); |
617 | fz_append_printf(ctx, buf, "%g %g l\n" , c.x, c.y); |
618 | } |
619 | else |
620 | fz_append_printf(ctx, buf, "%g %g l\n" , a.x, a.y); |
621 | up = !up; |
622 | } |
623 | fz_append_printf(ctx, buf, "S\n" ); |
624 | |
625 | union_quad(rect, quad, h/16); |
626 | } |
627 | } |
628 | } |
629 | |
630 | static void |
631 | pdf_write_redact_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect) |
632 | { |
633 | fz_point quad[4]; |
634 | pdf_obj *qp; |
635 | int i, n; |
636 | |
637 | fz_append_printf(ctx, buf, "1 0 0 RG\n" ); |
638 | |
639 | qp = pdf_dict_get(ctx, annot->obj, PDF_NAME(QuadPoints)); |
640 | n = pdf_array_len(ctx, qp); |
641 | if (n > 0) |
642 | { |
643 | *rect = fz_empty_rect; |
644 | for (i = 0; i < n; i += 8) |
645 | { |
646 | extract_quad(ctx, quad, qp, i); |
647 | fz_append_printf(ctx, buf, "%g %g m\n" , quad[LL].x, quad[LL].y); |
648 | fz_append_printf(ctx, buf, "%g %g l\n" , quad[LR].x, quad[LR].y); |
649 | fz_append_printf(ctx, buf, "%g %g l\n" , quad[UR].x, quad[UR].y); |
650 | fz_append_printf(ctx, buf, "%g %g l\n" , quad[UL].x, quad[UL].y); |
651 | fz_append_printf(ctx, buf, "s\n" ); |
652 | union_quad(rect, quad, 1); |
653 | } |
654 | } |
655 | else |
656 | { |
657 | fz_append_printf(ctx, buf, "%g %g m\n" , rect->x0+1, rect->y0+1); |
658 | fz_append_printf(ctx, buf, "%g %g l\n" , rect->x1-1, rect->y0+1); |
659 | fz_append_printf(ctx, buf, "%g %g l\n" , rect->x1-1, rect->y1-1); |
660 | fz_append_printf(ctx, buf, "%g %g l\n" , rect->x0+1, rect->y1-1); |
661 | fz_append_printf(ctx, buf, "s\n" ); |
662 | } |
663 | } |
664 | |
665 | static void |
666 | pdf_write_caret_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, fz_rect *bbox) |
667 | { |
668 | float xc = (rect->x0 + rect->x1) / 2; |
669 | float yc = (rect->y0 + rect->y1) / 2; |
670 | |
671 | pdf_write_fill_color_appearance(ctx, annot, buf); |
672 | |
673 | fz_append_string(ctx, buf, "0 0 m\n" ); |
674 | fz_append_string(ctx, buf, "10 0 10 7 10 14 c\n" ); |
675 | fz_append_string(ctx, buf, "10 7 10 0 20 0 c\n" ); |
676 | fz_append_string(ctx, buf, "f" ); |
677 | |
678 | *rect = fz_make_rect(xc - 10, yc - 7, xc + 10, yc + 7); |
679 | *bbox = fz_make_rect(0, 0, 20, 14); |
680 | } |
681 | |
682 | static void |
683 | pdf_write_icon_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, fz_rect *bbox) |
684 | { |
685 | const char *name; |
686 | float xc = (rect->x0 + rect->x1) / 2; |
687 | float yc = (rect->y0 + rect->y1) / 2; |
688 | |
689 | if (!pdf_write_fill_color_appearance(ctx, annot, buf)) |
690 | fz_append_string(ctx, buf, "1 g\n" ); |
691 | |
692 | fz_append_string(ctx, buf, "1 w\n0.5 0.5 15 15 re\nb\n" ); |
693 | fz_append_string(ctx, buf, "1 0 0 -1 4 12 cm\n" ); |
694 | |
695 | if (pdf_is_dark_fill_color(ctx, annot)) |
696 | fz_append_string(ctx, buf, "1 g\n" ); |
697 | else |
698 | fz_append_string(ctx, buf, "0 g\n" ); |
699 | |
700 | name = pdf_annot_icon_name(ctx, annot); |
701 | |
702 | /* Text names */ |
703 | if (!strcmp(name, "Comment" )) |
704 | fz_append_string(ctx, buf, icon_comment); |
705 | else if (!strcmp(name, "Key" )) |
706 | fz_append_string(ctx, buf, icon_key); |
707 | else if (!strcmp(name, "Note" )) |
708 | fz_append_string(ctx, buf, icon_note); |
709 | else if (!strcmp(name, "Help" )) |
710 | fz_append_string(ctx, buf, icon_help); |
711 | else if (!strcmp(name, "NewParagraph" )) |
712 | fz_append_string(ctx, buf, icon_new_paragraph); |
713 | else if (!strcmp(name, "Paragraph" )) |
714 | fz_append_string(ctx, buf, icon_paragraph); |
715 | else if (!strcmp(name, "Insert" )) |
716 | fz_append_string(ctx, buf, icon_insert); |
717 | |
718 | /* FileAttachment names */ |
719 | else if (!strcmp(name, "Graph" )) |
720 | fz_append_string(ctx, buf, icon_graph); |
721 | else if (!strcmp(name, "PushPin" )) |
722 | fz_append_string(ctx, buf, icon_push_pin); |
723 | else if (!strcmp(name, "Paperclip" )) |
724 | fz_append_string(ctx, buf, icon_paperclip); |
725 | else if (!strcmp(name, "Tag" )) |
726 | fz_append_string(ctx, buf, icon_tag); |
727 | |
728 | /* Sound names */ |
729 | else if (!strcmp(name, "Speaker" )) |
730 | fz_append_string(ctx, buf, icon_speaker); |
731 | else if (!strcmp(name, "Mic" )) |
732 | fz_append_string(ctx, buf, icon_mic); |
733 | |
734 | /* Unknown */ |
735 | else |
736 | fz_append_string(ctx, buf, icon_star); |
737 | |
738 | *rect = fz_make_rect(xc - 9, yc - 9, xc + 9, yc + 9); |
739 | *bbox = fz_make_rect(0, 0, 16, 16); |
740 | } |
741 | |
742 | static float |
743 | measure_simple_string(fz_context *ctx, fz_font *font, const char *text) |
744 | { |
745 | float w = 0; |
746 | while (*text) |
747 | { |
748 | int c, g; |
749 | text += fz_chartorune(&c, text); |
750 | c = fz_windows_1252_from_unicode(c); |
751 | if (c < 0) c = REPLACEMENT; |
752 | g = fz_encode_character(ctx, font, c); |
753 | w += fz_advance_glyph(ctx, font, g, 0); |
754 | } |
755 | return w; |
756 | } |
757 | |
758 | static void |
759 | write_simple_string(fz_context *ctx, fz_buffer *buf, const char *a, const char *b) |
760 | { |
761 | fz_append_byte(ctx, buf, '('); |
762 | while (a < b) |
763 | { |
764 | int c; |
765 | a += fz_chartorune(&c, a); |
766 | c = fz_windows_1252_from_unicode(c); |
767 | if (c < 0) c = REPLACEMENT; |
768 | if (c == '(' || c == ')' || c == '\\') |
769 | fz_append_byte(ctx, buf, '\\'); |
770 | fz_append_byte(ctx, buf, c); |
771 | } |
772 | fz_append_byte(ctx, buf, ')'); |
773 | } |
774 | |
775 | static void |
776 | write_stamp_string(fz_context *ctx, fz_buffer *buf, fz_font *font, const char *text) |
777 | { |
778 | write_simple_string(ctx, buf, text, text+strlen(text)); |
779 | } |
780 | |
781 | static void |
782 | write_stamp(fz_context *ctx, fz_buffer *buf, fz_font *font, const char *text, float y, float h) |
783 | { |
784 | float tw = measure_simple_string(ctx, font, text) * h; |
785 | fz_append_string(ctx, buf, "BT\n" ); |
786 | fz_append_printf(ctx, buf, "/Times %g Tf\n" , h); |
787 | fz_append_printf(ctx, buf, "%g %g Td\n" , (190-tw)/2, y); |
788 | write_stamp_string(ctx, buf, font, text); |
789 | fz_append_string(ctx, buf, " Tj\n" ); |
790 | fz_append_string(ctx, buf, "ET\n" ); |
791 | } |
792 | |
793 | static void |
794 | pdf_write_stamp_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, fz_rect *bbox, pdf_obj **res) |
795 | { |
796 | fz_font *font; |
797 | pdf_obj *res_font; |
798 | pdf_obj *name; |
799 | float w, h, xs, ys; |
800 | fz_matrix rotate; |
801 | |
802 | name = pdf_dict_get(ctx, annot->obj, PDF_NAME(Name)); |
803 | if (!name) |
804 | name = PDF_NAME(Draft); |
805 | |
806 | h = rect->y1 - rect->y0; |
807 | w = rect->x1 - rect->x0; |
808 | xs = w / 190; |
809 | ys = h / 50; |
810 | |
811 | font = fz_new_base14_font(ctx, "Times-Bold" ); |
812 | fz_try(ctx) |
813 | { |
814 | /* /Resources << /Font << /Times %d 0 R >> >> */ |
815 | *res = pdf_new_dict(ctx, annot->page->doc, 1); |
816 | res_font = pdf_dict_put_dict(ctx, *res, PDF_NAME(Font), 1); |
817 | pdf_dict_put_drop(ctx, res_font, PDF_NAME(Times), pdf_add_simple_font(ctx, annot->page->doc, font, 0)); |
818 | |
819 | pdf_write_fill_color_appearance(ctx, annot, buf); |
820 | pdf_write_stroke_color_appearance(ctx, annot, buf); |
821 | rotate = fz_rotate(0.6f); |
822 | fz_append_printf(ctx, buf, "%M cm\n" , &rotate); |
823 | fz_append_string(ctx, buf, "2 w\n2 2 186 44 re\nS\n" ); |
824 | |
825 | if (name == PDF_NAME(Approved)) |
826 | write_stamp(ctx, buf, font, "APPROVED" , 13, 30); |
827 | else if (name == PDF_NAME(AsIs)) |
828 | write_stamp(ctx, buf, font, "AS IS" , 13, 30); |
829 | else if (name == PDF_NAME(Confidential)) |
830 | write_stamp(ctx, buf, font, "CONFIDENTIAL" , 17, 20); |
831 | else if (name == PDF_NAME(Departmental)) |
832 | write_stamp(ctx, buf, font, "DEPARTMENTAL" , 17, 20); |
833 | else if (name == PDF_NAME(Experimental)) |
834 | write_stamp(ctx, buf, font, "EXPERIMENTAL" , 17, 20); |
835 | else if (name == PDF_NAME(Expired)) |
836 | write_stamp(ctx, buf, font, "EXPIRED" , 13, 30); |
837 | else if (name == PDF_NAME(Final)) |
838 | write_stamp(ctx, buf, font, "FINAL" , 13, 30); |
839 | else if (name == PDF_NAME(ForComment)) |
840 | write_stamp(ctx, buf, font, "FOR COMMENT" , 17, 20); |
841 | else if (name == PDF_NAME(ForPublicRelease)) |
842 | { |
843 | write_stamp(ctx, buf, font, "FOR PUBLIC" , 26, 18); |
844 | write_stamp(ctx, buf, font, "RELEASE" , 8.5f, 18); |
845 | } |
846 | else if (name == PDF_NAME(NotApproved)) |
847 | write_stamp(ctx, buf, font, "NOT APPROVED" , 17, 20); |
848 | else if (name == PDF_NAME(NotForPublicRelease)) |
849 | { |
850 | write_stamp(ctx, buf, font, "NOT FOR" , 26, 18); |
851 | write_stamp(ctx, buf, font, "PUBLIC RELEASE" , 8.5, 18); |
852 | } |
853 | else if (name == PDF_NAME(Sold)) |
854 | write_stamp(ctx, buf, font, "SOLD" , 13, 30); |
855 | else if (name == PDF_NAME(TopSecret)) |
856 | write_stamp(ctx, buf, font, "TOP SECRET" , 14, 26); |
857 | else if (name == PDF_NAME(Draft)) |
858 | write_stamp(ctx, buf, font, "DRAFT" , 13, 30); |
859 | else |
860 | write_stamp(ctx, buf, font, pdf_to_name(ctx, name), 17, 20); |
861 | } |
862 | fz_always(ctx) |
863 | fz_drop_font(ctx, font); |
864 | fz_catch(ctx) |
865 | fz_rethrow(ctx); |
866 | |
867 | *bbox = fz_make_rect(0, 0, 190, 50); |
868 | if (xs > ys) |
869 | { |
870 | float xc = (rect->x1+rect->x0) / 2; |
871 | rect->x0 = xc - 95 * ys; |
872 | rect->x1 = xc + 95 * ys; |
873 | } |
874 | else |
875 | { |
876 | float yc = (rect->y1+rect->y0) / 2; |
877 | rect->y0 = yc - 25 * xs; |
878 | rect->y1 = yc + 25 * xs; |
879 | } |
880 | } |
881 | |
882 | static float |
883 | break_simple_string(fz_context *ctx, fz_font *font, float size, const char *a, const char **endp, float maxw) |
884 | { |
885 | const char *space = NULL; |
886 | float space_x, x = 0; |
887 | int c, g; |
888 | while (*a) |
889 | { |
890 | a += fz_chartorune(&c, a); |
891 | if (c >= 256) |
892 | c = REPLACEMENT; |
893 | if (c == '\n' || c == '\r') |
894 | break; |
895 | if (c == ' ') |
896 | { |
897 | space = a; |
898 | space_x = x; |
899 | } |
900 | g = fz_encode_character(ctx, font, c); |
901 | x += fz_advance_glyph(ctx, font, g, 0) * size; |
902 | if (space && x > maxw) |
903 | return *endp = space, space_x; |
904 | } |
905 | return *endp = a, x; |
906 | } |
907 | |
908 | static void |
909 | write_simple_string_with_quadding(fz_context *ctx, fz_buffer *buf, fz_font *font, float size, |
910 | const char *a, float maxw, int q) |
911 | { |
912 | const char *b; |
913 | float px = 0, x = 0, w; |
914 | while (*a) |
915 | { |
916 | w = break_simple_string(ctx, font, size, a, &b, maxw); |
917 | if (b > a) |
918 | { |
919 | if (q > 0) |
920 | { |
921 | if (q == 1) |
922 | x = (maxw - w) / 2; |
923 | else |
924 | x = (maxw - w); |
925 | fz_append_printf(ctx, buf, "%g %g Td " , x - px, -size); |
926 | } |
927 | if (b[-1] == '\n' || b[-1] == '\r') |
928 | write_simple_string(ctx, buf, a, b-1); |
929 | else |
930 | write_simple_string(ctx, buf, a, b); |
931 | a = b; |
932 | px = x; |
933 | fz_append_string(ctx, buf, (q > 0) ? "Tj\n" : "'\n" ); |
934 | } |
935 | } |
936 | } |
937 | |
938 | static void |
939 | write_comb_string(fz_context *ctx, fz_buffer *buf, const char *a, const char *b, fz_font *font, float cell_w) |
940 | { |
941 | float gw, pad, carry = 0; |
942 | fz_append_byte(ctx, buf, '['); |
943 | while (a < b) |
944 | { |
945 | int c, g; |
946 | |
947 | a += fz_chartorune(&c, a); |
948 | c = fz_windows_1252_from_unicode(c); |
949 | if (c < 0) c = REPLACEMENT; |
950 | |
951 | g = fz_encode_character(ctx, font, c); |
952 | gw = fz_advance_glyph(ctx, font, g, 0) * 1000; |
953 | pad = (cell_w - gw) / 2; |
954 | fz_append_printf(ctx, buf, "%g" , -(carry + pad)); |
955 | carry = pad; |
956 | |
957 | fz_append_byte(ctx, buf, '('); |
958 | if (c == '(' || c == ')' || c == '\\') |
959 | fz_append_byte(ctx, buf, '\\'); |
960 | fz_append_byte(ctx, buf, c); |
961 | fz_append_byte(ctx, buf, ')'); |
962 | } |
963 | fz_append_string(ctx, buf, "] TJ\n" ); |
964 | } |
965 | |
966 | static void |
967 | layout_comb_string(fz_context *ctx, fz_layout_block *out, float x, float y, |
968 | const char *a, const char *b, fz_font *font, float size, float cell_w) |
969 | { |
970 | int n, c, g; |
971 | int first = 1; |
972 | float w; |
973 | if (a == b) |
974 | fz_add_layout_line(ctx, out, x + cell_w / 2, y, size, a); |
975 | while (a < b) |
976 | { |
977 | n = fz_chartorune(&c, a); |
978 | c = fz_windows_1252_from_unicode(c); |
979 | if (c < 0) c = REPLACEMENT; |
980 | g = fz_encode_character(ctx, font, c); |
981 | w = fz_advance_glyph(ctx, font, g, 0) * size; |
982 | if (first) |
983 | { |
984 | fz_add_layout_line(ctx, out, x + (cell_w - w) / 2, y, size, a); |
985 | first = 0; |
986 | } |
987 | fz_add_layout_char(ctx, out, x + (cell_w - w) / 2, w, a); |
988 | a += n; |
989 | x += cell_w; |
990 | } |
991 | } |
992 | |
993 | static void |
994 | layout_simple_string(fz_context *ctx, fz_layout_block *out, fz_font *font, float size, |
995 | float x, float y, const char *a, const char *b) |
996 | { |
997 | float w; |
998 | int n, c, g; |
999 | fz_add_layout_line(ctx, out, x, y, size, a); |
1000 | while (a < b) |
1001 | { |
1002 | n = fz_chartorune(&c, a); |
1003 | c = fz_windows_1252_from_unicode(c); |
1004 | if (c < 0) c = REPLACEMENT; |
1005 | g = fz_encode_character(ctx, font, c); |
1006 | w = fz_advance_glyph(ctx, font, g, 0) * size; |
1007 | fz_add_layout_char(ctx, out, x, w, a); |
1008 | a += n; |
1009 | x += w; |
1010 | } |
1011 | } |
1012 | |
1013 | static void |
1014 | layout_simple_string_with_quadding(fz_context *ctx, fz_layout_block *out, |
1015 | fz_font *font, float size, float lineheight, |
1016 | float xorig, float y, const char *a, float maxw, int q) |
1017 | { |
1018 | const char *b; |
1019 | float x = 0, w; |
1020 | int add_line_at_end = 0; |
1021 | |
1022 | if (!*a) |
1023 | add_line_at_end = 1; |
1024 | |
1025 | while (*a) |
1026 | { |
1027 | w = break_simple_string(ctx, font, size, a, &b, maxw); |
1028 | if (b > a) |
1029 | { |
1030 | if (q > 0) |
1031 | { |
1032 | if (q == 1) |
1033 | x = (maxw - w) / 2; |
1034 | else |
1035 | x = (maxw - w); |
1036 | } |
1037 | if (b[-1] == '\n' || b[-1] == '\r') |
1038 | { |
1039 | layout_simple_string(ctx, out, font, size, xorig+x, y, a, b-1); |
1040 | add_line_at_end = 1; |
1041 | } |
1042 | else |
1043 | { |
1044 | layout_simple_string(ctx, out, font, size, xorig+x, y, a, b); |
1045 | add_line_at_end = 0; |
1046 | } |
1047 | a = b; |
1048 | y -= lineheight; |
1049 | } |
1050 | } |
1051 | if (add_line_at_end) |
1052 | fz_add_layout_line(ctx, out, xorig, y, size, a); |
1053 | } |
1054 | |
1055 | static const char *full_font_name(const char **name) |
1056 | { |
1057 | if (!strcmp(*name, "Cour" )) return "Courier" ; |
1058 | if (!strcmp(*name, "Helv" )) return "Helvetica" ; |
1059 | if (!strcmp(*name, "TiRo" )) return "Times-Roman" ; |
1060 | if (!strcmp(*name, "Symb" )) return "Symbol" ; |
1061 | if (!strcmp(*name, "ZaDb" )) return "ZapfDingbats" ; |
1062 | return *name = "Helv" , "Helvetica" ; |
1063 | } |
1064 | |
1065 | static void |
1066 | write_variable_text(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, pdf_obj **res, |
1067 | const char *text, const char *fontname, float size, float color[3], int q, |
1068 | float w, float h, float padding, float baseline, float lineheight, |
1069 | int multiline, int comb, int adjust_baseline) |
1070 | { |
1071 | pdf_obj *res_font; |
1072 | fz_font *font; |
1073 | |
1074 | w -= padding * 2; |
1075 | h -= padding * 2; |
1076 | |
1077 | font = fz_new_base14_font(ctx, full_font_name(&fontname)); |
1078 | fz_try(ctx) |
1079 | { |
1080 | if (size == 0) |
1081 | { |
1082 | if (multiline) |
1083 | size = 12; |
1084 | else |
1085 | { |
1086 | size = w / measure_simple_string(ctx, font, text); |
1087 | if (size > h) |
1088 | size = h; |
1089 | } |
1090 | } |
1091 | |
1092 | lineheight = size * lineheight; |
1093 | baseline = size * baseline; |
1094 | |
1095 | if (adjust_baseline) |
1096 | { |
1097 | /* Make sure baseline is inside rectangle */ |
1098 | if (baseline + 0.2f * size > h) |
1099 | baseline = h - 0.2f * size; |
1100 | } |
1101 | |
1102 | /* /Resources << /Font << /Helv %d 0 R >> >> */ |
1103 | *res = pdf_new_dict(ctx, annot->page->doc, 1); |
1104 | res_font = pdf_dict_put_dict(ctx, *res, PDF_NAME(Font), 1); |
1105 | pdf_dict_puts_drop(ctx, res_font, fontname, pdf_add_simple_font(ctx, annot->page->doc, font, 0)); |
1106 | |
1107 | fz_append_string(ctx, buf, "BT\n" ); |
1108 | fz_append_printf(ctx, buf, "%g %g %g rg\n" , color[0], color[1], color[2]); |
1109 | fz_append_printf(ctx, buf, "/%s %g Tf\n" , fontname, size); |
1110 | if (multiline) |
1111 | { |
1112 | fz_append_printf(ctx, buf, "%g TL\n" , lineheight); |
1113 | fz_append_printf(ctx, buf, "%g %g Td\n" , padding, padding+h-baseline+lineheight); |
1114 | write_simple_string_with_quadding(ctx, buf, font, size, text, w, q); |
1115 | } |
1116 | else if (comb > 0) |
1117 | { |
1118 | float ty = (h - size) / 2; |
1119 | fz_append_printf(ctx, buf, "%g %g Td\n" , padding, padding+h-baseline-ty); |
1120 | write_comb_string(ctx, buf, text, text + strlen(text), font, (w * 1000 / size) / comb); |
1121 | } |
1122 | else |
1123 | { |
1124 | float tx = 0, ty = (h - size) / 2; |
1125 | if (q > 0) |
1126 | { |
1127 | float tw = measure_simple_string(ctx, font, text) * size; |
1128 | if (q == 1) |
1129 | tx = (w - tw) / 2; |
1130 | else |
1131 | tx = (w - tw); |
1132 | } |
1133 | fz_append_printf(ctx, buf, "%g %g Td\n" , padding+tx, padding+h-baseline-ty); |
1134 | write_simple_string(ctx, buf, text, text + strlen(text)); |
1135 | fz_append_printf(ctx, buf, " Tj\n" ); |
1136 | } |
1137 | fz_append_string(ctx, buf, "ET\n" ); |
1138 | } |
1139 | fz_always(ctx) |
1140 | fz_drop_font(ctx, font); |
1141 | fz_catch(ctx) |
1142 | fz_rethrow(ctx); |
1143 | } |
1144 | |
1145 | static void |
1146 | layout_variable_text(fz_context *ctx, fz_layout_block *out, |
1147 | const char *text, const char *fontname, float size, int q, |
1148 | float x, float y, float w, float h, float padding, float baseline, float lineheight, |
1149 | int multiline, int comb, int adjust_baseline) |
1150 | { |
1151 | fz_font *font; |
1152 | |
1153 | w -= padding * 2; |
1154 | h -= padding * 2; |
1155 | |
1156 | font = fz_new_base14_font(ctx, full_font_name(&fontname)); |
1157 | fz_try(ctx) |
1158 | { |
1159 | if (size == 0) |
1160 | { |
1161 | if (multiline) |
1162 | size = 12; |
1163 | else |
1164 | { |
1165 | size = w / measure_simple_string(ctx, font, text); |
1166 | if (size > h) |
1167 | size = h; |
1168 | } |
1169 | } |
1170 | |
1171 | lineheight = size * lineheight; |
1172 | baseline = size * baseline; |
1173 | |
1174 | if (adjust_baseline) |
1175 | { |
1176 | /* Make sure baseline is inside rectangle */ |
1177 | if (baseline + 0.2f * size > h) |
1178 | baseline = h - 0.2f * size; |
1179 | } |
1180 | |
1181 | if (multiline) |
1182 | { |
1183 | x += padding; |
1184 | y += padding + h - baseline; |
1185 | layout_simple_string_with_quadding(ctx, out, font, size, lineheight, x, y, text, w, q); |
1186 | } |
1187 | else if (comb > 0) |
1188 | { |
1189 | float ty = (h - size) / 2; |
1190 | x += padding; |
1191 | y += padding + h - baseline - ty; |
1192 | layout_comb_string(ctx, out, x, y, text, text + strlen(text), font, size, w / comb); |
1193 | } |
1194 | else |
1195 | { |
1196 | float tx = 0, ty = (h - size) / 2; |
1197 | if (q > 0) |
1198 | { |
1199 | float tw = measure_simple_string(ctx, font, text) * size; |
1200 | if (q == 1) |
1201 | tx = (w - tw) / 2; |
1202 | else |
1203 | tx = (w - tw); |
1204 | } |
1205 | x += padding + tx; |
1206 | y += padding + h - baseline - ty; |
1207 | layout_simple_string(ctx, out, font, size, x, y, text, text + strlen(text)); |
1208 | } |
1209 | } |
1210 | fz_always(ctx) |
1211 | fz_drop_font(ctx, font); |
1212 | fz_catch(ctx) |
1213 | fz_rethrow(ctx); |
1214 | } |
1215 | |
1216 | static void |
1217 | pdf_write_free_text_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, |
1218 | fz_rect *rect, fz_rect *bbox, fz_matrix *matrix, pdf_obj **res) |
1219 | { |
1220 | const char *font; |
1221 | float size, color[3]; |
1222 | const char *text; |
1223 | float w, h, t, b; |
1224 | int q, r; |
1225 | |
1226 | /* /Rotate is an undocumented annotation property supported by Adobe */ |
1227 | text = pdf_annot_contents(ctx, annot); |
1228 | r = pdf_dict_get_int(ctx, annot->obj, PDF_NAME(Rotate)); |
1229 | q = pdf_annot_quadding(ctx, annot); |
1230 | pdf_annot_default_appearance(ctx, annot, &font, &size, color); |
1231 | |
1232 | w = rect->x1 - rect->x0; |
1233 | h = rect->y1 - rect->y0; |
1234 | if (r == 90 || r == 270) |
1235 | t = h, h = w, w = t; |
1236 | |
1237 | *matrix = fz_rotate(r); |
1238 | *bbox = fz_make_rect(0, 0, w, h); |
1239 | |
1240 | if (pdf_write_fill_color_appearance(ctx, annot, buf)) |
1241 | fz_append_printf(ctx, buf, "0 0 %g %g re\nf\n" , w, h); |
1242 | |
1243 | b = pdf_write_border_appearance(ctx, annot, buf); |
1244 | if (b > 0) |
1245 | { |
1246 | fz_append_printf(ctx, buf, "%g %g %g RG\n" , color[0], color[1], color[2]); |
1247 | fz_append_printf(ctx, buf, "%g %g %g %g re\nS\n" , b/2, b/2, w-b, h-b); |
1248 | } |
1249 | |
1250 | fz_append_printf(ctx, buf, "%g %g %g %g re\nW\nn\n" , b, b, w-b*2, h-b*2); |
1251 | |
1252 | write_variable_text(ctx, annot, buf, res, text, font, size, color, q, w, h, b*2, |
1253 | 0.8f, 1.2f, 1, 0, 0); |
1254 | } |
1255 | |
1256 | static void |
1257 | pdf_write_tx_widget_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, |
1258 | fz_rect *rect, fz_rect *bbox, fz_matrix *matrix, pdf_obj **res, |
1259 | const char *text, int ff) |
1260 | { |
1261 | const char *font; |
1262 | float size, color[3]; |
1263 | float w, h, t, b; |
1264 | int has_bc = 0; |
1265 | int q, r; |
1266 | |
1267 | r = pdf_dict_get_int(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME(MK)), PDF_NAME(R)); |
1268 | q = pdf_annot_quadding(ctx, annot); |
1269 | pdf_annot_default_appearance(ctx, annot, &font, &size, color); |
1270 | |
1271 | w = rect->x1 - rect->x0; |
1272 | h = rect->y1 - rect->y0; |
1273 | r = r % 360; |
1274 | if (r == 90 || r == 270) |
1275 | t = h, h = w, w = t; |
1276 | *matrix = fz_rotate(r); |
1277 | *bbox = fz_make_rect(0, 0, w, h); |
1278 | |
1279 | fz_append_string(ctx, buf, "/Tx BMC\nq\n" ); |
1280 | |
1281 | if (pdf_write_MK_BG_appearance(ctx, annot, buf)) |
1282 | fz_append_printf(ctx, buf, "0 0 %g %g re\nf\n" , w, h); |
1283 | |
1284 | b = pdf_write_border_appearance(ctx, annot, buf); |
1285 | if (b > 0 && pdf_write_MK_BC_appearance(ctx, annot, buf)) |
1286 | { |
1287 | fz_append_printf(ctx, buf, "%g %g %g %g re\ns\n" , b/2, b/2, w-b, h-b); |
1288 | has_bc = 1; |
1289 | } |
1290 | |
1291 | fz_append_printf(ctx, buf, "%g %g %g %g re\nW\nn\n" , b, b, w-b*2, h-b*2); |
1292 | |
1293 | if (ff & PDF_TX_FIELD_IS_MULTILINE) |
1294 | { |
1295 | write_variable_text(ctx, annot, buf, res, text, font, size, color, q, w, h, b*2, |
1296 | 1.116f, 1.116f, 1, 0, 1); |
1297 | } |
1298 | else if (ff & PDF_TX_FIELD_IS_COMB) |
1299 | { |
1300 | int maxlen = pdf_to_int(ctx, pdf_dict_get_inheritable(ctx, annot->obj, PDF_NAME(MaxLen))); |
1301 | if (has_bc && maxlen > 1) |
1302 | { |
1303 | float cell_w = (w - 2 * b) / maxlen; |
1304 | int i; |
1305 | for (i = 1; i < maxlen; ++i) |
1306 | { |
1307 | float x = b + cell_w * i; |
1308 | fz_append_printf(ctx, buf, "%g %g m %g %g l s\n" , x, b, x, h-b); |
1309 | } |
1310 | } |
1311 | write_variable_text(ctx, annot, buf, res, text, font, size, color, q, w, h, 0, |
1312 | 0.8f, 1.2f, 0, maxlen, 0); |
1313 | } |
1314 | else |
1315 | { |
1316 | write_variable_text(ctx, annot, buf, res, text, font, size, color, q, w, h, b*2, |
1317 | 0.8f, 1.2f, 0, 0, 0); |
1318 | } |
1319 | |
1320 | fz_append_string(ctx, buf, "Q\nEMC\n" ); |
1321 | } |
1322 | |
1323 | fz_layout_block * |
1324 | pdf_layout_text_widget(fz_context *ctx, pdf_annot *annot) |
1325 | { |
1326 | fz_layout_block *out; |
1327 | const char *font; |
1328 | const char *text; |
1329 | fz_rect rect; |
1330 | float size, color[3]; |
1331 | float w, h, t, b, x, y; |
1332 | int q, r; |
1333 | int ff; |
1334 | |
1335 | rect = pdf_dict_get_rect(ctx, annot->obj, PDF_NAME(Rect)); |
1336 | text = pdf_field_value(ctx, annot->obj); |
1337 | ff = pdf_field_flags(ctx, annot->obj); |
1338 | |
1339 | b = pdf_annot_border(ctx, annot); |
1340 | r = pdf_dict_get_int(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME(MK)), PDF_NAME(R)); |
1341 | q = pdf_annot_quadding(ctx, annot); |
1342 | pdf_annot_default_appearance(ctx, annot, &font, &size, color); |
1343 | |
1344 | w = rect.x1 - rect.x0; |
1345 | h = rect.y1 - rect.y0; |
1346 | r = r % 360; |
1347 | if (r == 90 || r == 270) |
1348 | t = h, h = w, w = t; |
1349 | |
1350 | x = rect.x0; |
1351 | y = rect.y0; |
1352 | |
1353 | out = fz_new_layout(ctx); |
1354 | fz_try(ctx) |
1355 | { |
1356 | pdf_page_transform(ctx, annot->page, NULL, &out->matrix); |
1357 | out->matrix = fz_concat(out->matrix, fz_rotate(r)); |
1358 | out->inv_matrix = fz_invert_matrix(out->matrix); |
1359 | |
1360 | if (ff & PDF_TX_FIELD_IS_MULTILINE) |
1361 | { |
1362 | layout_variable_text(ctx, out, text, font, size, q, x, y, w, h, b*2, 1.116f, 1.116f, 1, 0, 1); |
1363 | } |
1364 | else if (ff & PDF_TX_FIELD_IS_COMB) |
1365 | { |
1366 | int maxlen = pdf_to_int(ctx, pdf_dict_get_inheritable(ctx, annot->obj, PDF_NAME(MaxLen))); |
1367 | layout_variable_text(ctx, out, text, font, size, q, x, y, w, h, 0, 0.8f, 1.2f, 0, maxlen, 0); |
1368 | } |
1369 | else |
1370 | { |
1371 | layout_variable_text(ctx, out, text, font, size, q, x, y, w, h, b*2, 0.8f, 1.2f, 0, 0, 0); |
1372 | } |
1373 | } |
1374 | fz_catch(ctx) |
1375 | { |
1376 | fz_drop_layout(ctx, out); |
1377 | fz_rethrow(ctx); |
1378 | } |
1379 | return out; |
1380 | } |
1381 | |
1382 | static void |
1383 | pdf_write_ch_widget_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, |
1384 | fz_rect *rect, fz_rect *bbox, fz_matrix *matrix, pdf_obj **res) |
1385 | { |
1386 | int ff = pdf_field_flags(ctx, annot->obj); |
1387 | if (ff & PDF_CH_FIELD_IS_COMBO) |
1388 | { |
1389 | /* TODO: Pop-down arrow */ |
1390 | pdf_write_tx_widget_appearance(ctx, annot, buf, rect, bbox, matrix, res, |
1391 | pdf_field_value(ctx, annot->obj), 0); |
1392 | } |
1393 | else |
1394 | { |
1395 | fz_buffer *text = fz_new_buffer(ctx, 1024); |
1396 | fz_try(ctx) |
1397 | { |
1398 | pdf_obj *opt = pdf_dict_get(ctx, annot->obj, PDF_NAME(Opt)); |
1399 | int i = pdf_dict_get_int(ctx, annot->obj, PDF_NAME(TI)); |
1400 | int n = pdf_array_len(ctx, opt); |
1401 | /* TODO: Scrollbar */ |
1402 | /* TODO: Highlight selected items */ |
1403 | if (i < 0) |
1404 | i = 0; |
1405 | for (; i < n; ++i) |
1406 | { |
1407 | pdf_obj *val = pdf_array_get(ctx, opt, i); |
1408 | if (pdf_is_array(ctx, val)) |
1409 | fz_append_string(ctx, text, pdf_array_get_text_string(ctx, val, 1)); |
1410 | else |
1411 | fz_append_string(ctx, text, pdf_to_text_string(ctx, val)); |
1412 | fz_append_byte(ctx, text, '\n'); |
1413 | } |
1414 | pdf_write_tx_widget_appearance(ctx, annot, buf, rect, bbox, matrix, res, |
1415 | fz_string_from_buffer(ctx, text), PDF_TX_FIELD_IS_MULTILINE); |
1416 | } |
1417 | fz_always(ctx) |
1418 | fz_drop_buffer(ctx, text); |
1419 | fz_catch(ctx) |
1420 | fz_rethrow(ctx); |
1421 | } |
1422 | } |
1423 | |
1424 | static void |
1425 | pdf_write_sig_widget_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, |
1426 | fz_rect *rect, fz_rect *bbox, fz_matrix *matrix, pdf_obj **res) |
1427 | { |
1428 | float x0 = rect->x0 + 1; |
1429 | float y0 = rect->y0 + 1; |
1430 | float x1 = rect->x1 - 1; |
1431 | float y1 = rect->y1 - 1; |
1432 | float w = x1 - x0; |
1433 | float h = y1 - y0; |
1434 | fz_append_printf(ctx, buf, "1 w\n0 G\n" ); |
1435 | fz_append_printf(ctx, buf, "%g %g %g %g re\n" , x0, y0, w, h); |
1436 | fz_append_printf(ctx, buf, "%g %g m %g %g l\n" , x0, y0, x1, y1); |
1437 | fz_append_printf(ctx, buf, "%g %g m %g %g l\n" , x1, y0, x0, y1); |
1438 | fz_append_printf(ctx, buf, "s\n" ); |
1439 | *bbox = *rect; |
1440 | *matrix = fz_identity; |
1441 | } |
1442 | |
1443 | static void |
1444 | pdf_write_widget_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, |
1445 | fz_rect *rect, fz_rect *bbox, fz_matrix *matrix, pdf_obj **res) |
1446 | { |
1447 | pdf_obj *ft = pdf_dict_get_inheritable(ctx, annot->obj, PDF_NAME(FT)); |
1448 | if (pdf_name_eq(ctx, ft, PDF_NAME(Tx))) |
1449 | { |
1450 | int ff = pdf_field_flags(ctx, annot->obj); |
1451 | char *format = NULL; |
1452 | const char *text = NULL; |
1453 | if (!annot->ignore_trigger_events) |
1454 | { |
1455 | format = pdf_field_event_format(ctx, annot->page->doc, annot->obj); |
1456 | if (format) |
1457 | text = format; |
1458 | else |
1459 | text = pdf_field_value(ctx, annot->obj); |
1460 | } |
1461 | else |
1462 | { |
1463 | text = pdf_field_value(ctx, annot->obj); |
1464 | } |
1465 | fz_try(ctx) |
1466 | pdf_write_tx_widget_appearance(ctx, annot, buf, rect, bbox, matrix, res, text, ff); |
1467 | fz_always(ctx) |
1468 | fz_free(ctx, format); |
1469 | fz_catch(ctx) |
1470 | fz_rethrow(ctx); |
1471 | } |
1472 | else if (pdf_name_eq(ctx, ft, PDF_NAME(Ch))) |
1473 | { |
1474 | pdf_write_ch_widget_appearance(ctx, annot, buf, rect, bbox, matrix, res); |
1475 | } |
1476 | else if (pdf_name_eq(ctx, ft, PDF_NAME(Sig))) |
1477 | { |
1478 | pdf_write_sig_widget_appearance(ctx, annot, buf, rect, bbox, matrix, res); |
1479 | } |
1480 | else |
1481 | { |
1482 | fz_throw(ctx, FZ_ERROR_GENERIC, "cannot create appearance stream for %s widgets" , pdf_to_name(ctx, ft)); |
1483 | } |
1484 | } |
1485 | |
1486 | static void |
1487 | pdf_write_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, |
1488 | fz_rect *rect, fz_rect *bbox, fz_matrix *matrix, pdf_obj **res) |
1489 | { |
1490 | switch (pdf_annot_type(ctx, annot)) |
1491 | { |
1492 | default: |
1493 | fz_throw(ctx, FZ_ERROR_GENERIC, "cannot create appearance stream for %s annotations" , |
1494 | pdf_dict_get_name(ctx, annot->obj, PDF_NAME(Subtype))); |
1495 | case PDF_ANNOT_WIDGET: |
1496 | pdf_write_widget_appearance(ctx, annot, buf, rect, bbox, matrix, res); |
1497 | break; |
1498 | case PDF_ANNOT_INK: |
1499 | pdf_write_ink_appearance(ctx, annot, buf, rect); |
1500 | *matrix = fz_identity; |
1501 | *bbox = *rect; |
1502 | break; |
1503 | case PDF_ANNOT_POLYGON: |
1504 | pdf_write_polygon_appearance(ctx, annot, buf, rect, 1); |
1505 | *matrix = fz_identity; |
1506 | *bbox = *rect; |
1507 | break; |
1508 | case PDF_ANNOT_POLY_LINE: |
1509 | pdf_write_polygon_appearance(ctx, annot, buf, rect, 0); |
1510 | *matrix = fz_identity; |
1511 | *bbox = *rect; |
1512 | break; |
1513 | case PDF_ANNOT_LINE: |
1514 | pdf_write_line_appearance(ctx, annot, buf, rect); |
1515 | *matrix = fz_identity; |
1516 | *bbox = *rect; |
1517 | break; |
1518 | case PDF_ANNOT_SQUARE: |
1519 | pdf_write_square_appearance(ctx, annot, buf, rect); |
1520 | *matrix = fz_identity; |
1521 | *bbox = *rect; |
1522 | break; |
1523 | case PDF_ANNOT_CIRCLE: |
1524 | pdf_write_circle_appearance(ctx, annot, buf, rect); |
1525 | *matrix = fz_identity; |
1526 | *bbox = *rect; |
1527 | break; |
1528 | case PDF_ANNOT_CARET: |
1529 | pdf_write_caret_appearance(ctx, annot, buf, rect, bbox); |
1530 | *matrix = fz_identity; |
1531 | break; |
1532 | case PDF_ANNOT_TEXT: |
1533 | case PDF_ANNOT_FILE_ATTACHMENT: |
1534 | case PDF_ANNOT_SOUND: |
1535 | pdf_write_icon_appearance(ctx, annot, buf, rect, bbox); |
1536 | *matrix = fz_identity; |
1537 | break; |
1538 | case PDF_ANNOT_HIGHLIGHT: |
1539 | pdf_write_highlight_appearance(ctx, annot, buf, rect, res); |
1540 | *matrix = fz_identity; |
1541 | *bbox = *rect; |
1542 | break; |
1543 | case PDF_ANNOT_UNDERLINE: |
1544 | pdf_write_underline_appearance(ctx, annot, buf, rect); |
1545 | *matrix = fz_identity; |
1546 | *bbox = *rect; |
1547 | break; |
1548 | case PDF_ANNOT_STRIKE_OUT: |
1549 | pdf_write_strike_out_appearance(ctx, annot, buf, rect); |
1550 | *matrix = fz_identity; |
1551 | *bbox = *rect; |
1552 | break; |
1553 | case PDF_ANNOT_SQUIGGLY: |
1554 | pdf_write_squiggly_appearance(ctx, annot, buf, rect); |
1555 | *matrix = fz_identity; |
1556 | *bbox = *rect; |
1557 | break; |
1558 | case PDF_ANNOT_REDACT: |
1559 | pdf_write_redact_appearance(ctx, annot, buf, rect); |
1560 | *matrix = fz_identity; |
1561 | *bbox = *rect; |
1562 | break; |
1563 | case PDF_ANNOT_STAMP: |
1564 | pdf_write_stamp_appearance(ctx, annot, buf, rect, bbox, res); |
1565 | *matrix = fz_identity; |
1566 | break; |
1567 | case PDF_ANNOT_FREE_TEXT: |
1568 | pdf_write_free_text_appearance(ctx, annot, buf, rect, bbox, matrix, res); |
1569 | break; |
1570 | } |
1571 | } |
1572 | |
1573 | static pdf_obj *draw_push_button(fz_context *ctx, pdf_annot *annot, fz_rect bbox, fz_matrix matrix, float w, float h, |
1574 | const char *caption, const char *font, float size, float color[3], |
1575 | int down) |
1576 | { |
1577 | pdf_obj *ap, *res = NULL; |
1578 | fz_buffer *buf; |
1579 | float bc[3] = { 0, 0, 0 }; |
1580 | float bg[3] = { 0.8f, 0.8f, 0.8f }; |
1581 | float hi[3], sh[3]; |
1582 | int has_bg, has_bc; |
1583 | float b; |
1584 | int i; |
1585 | |
1586 | buf = fz_new_buffer(ctx, 1024); |
1587 | fz_var(res); |
1588 | fz_try(ctx) |
1589 | { |
1590 | b = pdf_annot_border(ctx, annot); |
1591 | has_bc = pdf_annot_MK_BC_rgb(ctx, annot, bc); |
1592 | has_bg = pdf_annot_MK_BG_rgb(ctx, annot, bg); |
1593 | |
1594 | for (i = 0; i < 3; ++i) |
1595 | { |
1596 | if (down) |
1597 | { |
1598 | sh[i] = 1 - (1 - bg[i]) / 2; |
1599 | hi[i] = bg[i] / 2; |
1600 | } |
1601 | else |
1602 | { |
1603 | hi[i] = 1 - (1 - bg[i]) / 2; |
1604 | sh[i] = bg[i] / 2; |
1605 | } |
1606 | } |
1607 | |
1608 | fz_append_string(ctx, buf, "q\n" ); |
1609 | fz_append_printf(ctx, buf, "%g w\n" , b); |
1610 | if (has_bg) |
1611 | { |
1612 | fz_append_printf(ctx, buf, "%g %g %g rg\n" , bg[0], bg[1], bg[2]); |
1613 | fz_append_printf(ctx, buf, "0 0 %g %g re\nf\n" , 0, 0, w, h); |
1614 | } |
1615 | if (has_bc && b > 0) |
1616 | { |
1617 | fz_append_printf(ctx, buf, "%g %g %g RG\n" , bc[0], bc[1], bc[2]); |
1618 | fz_append_printf(ctx, buf, "%g %g %g %g re\nS\n" , b/2, b/2, w-b, h-b); |
1619 | } |
1620 | if (has_bg) |
1621 | { |
1622 | fz_append_printf(ctx, buf, "%g %g %g rg\n" , hi[0], hi[1], hi[2]); |
1623 | fz_append_printf(ctx, buf, "%g %g m %g %g l %g %g l %g %g l %g %g l %g %g l f\n" , |
1624 | b, b, b, h-b, w-b, h-b, w-b-2, h-b-2, b+2, h-b-2, b+2, b+2); |
1625 | fz_append_printf(ctx, buf, "%g %g %g rg\n" , sh[0], sh[1], sh[2]); |
1626 | fz_append_printf(ctx, buf, "%g %g m %g %g l %g %g l %g %g l %g %g l %g %g l f\n" , |
1627 | b, b, b+2, b+2, w-b-2, b+2, w-b-2, h-b-2, w-b, h-b, w-b, b); |
1628 | } |
1629 | if (down) |
1630 | fz_append_string(ctx, buf, "1 0 0 1 2 -2 cm\n" ); |
1631 | write_variable_text(ctx, annot, buf, &res, caption, font, size, color, 1, w, h, b+6, 0.8f, 1.2f, 0, 0, 0); |
1632 | fz_append_string(ctx, buf, "Q\n" ); |
1633 | |
1634 | ap = pdf_new_xobject(ctx, annot->page->doc, bbox, matrix, res, buf); |
1635 | } |
1636 | fz_always(ctx) |
1637 | { |
1638 | pdf_drop_obj(ctx, res); |
1639 | fz_drop_buffer(ctx, buf); |
1640 | } |
1641 | fz_catch(ctx) |
1642 | fz_rethrow(ctx); |
1643 | return ap; |
1644 | } |
1645 | |
1646 | static pdf_obj *draw_radio_button(fz_context *ctx, pdf_annot *annot, fz_rect bbox, fz_matrix matrix, float w, float h, int yes) |
1647 | { |
1648 | pdf_obj *ap; |
1649 | fz_buffer *buf; |
1650 | float b; |
1651 | |
1652 | buf = fz_new_buffer(ctx, 1024); |
1653 | fz_try(ctx) |
1654 | { |
1655 | fz_append_string(ctx, buf, "q\n" ); |
1656 | if (pdf_write_MK_BG_appearance(ctx, annot, buf)) |
1657 | draw_circle_in_box(ctx, buf, "f\n" , 0, 0, 0, w, h); |
1658 | b = pdf_write_border_appearance(ctx, annot, buf); |
1659 | if (b > 0 && pdf_write_MK_BC_appearance(ctx, annot, buf)) |
1660 | draw_circle_in_box(ctx, buf, "s\n" , b, 0, 0, w, h); |
1661 | if (yes) |
1662 | { |
1663 | fz_append_string(ctx, buf, "0 g\n" ); |
1664 | draw_circle(ctx, buf, "f\n" , (w-b*2)/4, (h-b*2)/4, w/2, h/2); |
1665 | } |
1666 | fz_append_string(ctx, buf, "Q\n" ); |
1667 | ap = pdf_new_xobject(ctx, annot->page->doc, bbox, matrix, NULL, buf); |
1668 | } |
1669 | fz_always(ctx) |
1670 | fz_drop_buffer(ctx, buf); |
1671 | fz_catch(ctx) |
1672 | fz_rethrow(ctx); |
1673 | return ap; |
1674 | } |
1675 | |
1676 | static pdf_obj *draw_check_button(fz_context *ctx, pdf_annot *annot, fz_rect bbox, fz_matrix matrix, float w, float h, int yes) |
1677 | { |
1678 | float black[3] = { 0, 0, 0 }; |
1679 | pdf_obj *ap, *res = NULL; |
1680 | fz_buffer *buf; |
1681 | float b; |
1682 | |
1683 | fz_var(res); |
1684 | |
1685 | buf = fz_new_buffer(ctx, 1024); |
1686 | fz_try(ctx) |
1687 | { |
1688 | fz_append_string(ctx, buf, "q\n" ); |
1689 | if (pdf_write_MK_BG_appearance(ctx, annot, buf)) |
1690 | fz_append_printf(ctx, buf, "0 0 %g %g re\nf\n" , w, h); |
1691 | b = pdf_write_border_appearance(ctx, annot, buf); |
1692 | if (b > 0 && pdf_write_MK_BC_appearance(ctx, annot, buf)) |
1693 | fz_append_printf(ctx, buf, "%g %g %g %g re\nS\n" , b/2, b/2, w-b, h-b); |
1694 | if (yes) |
1695 | write_variable_text(ctx, annot, buf, &res, "3" , "ZaDb" , h, black, 0, w, h, b+h/10, 0.8f, 1.2f, 0, 0, 0); |
1696 | fz_append_string(ctx, buf, "Q\n" ); |
1697 | ap = pdf_new_xobject(ctx, annot->page->doc, bbox, matrix, res, buf); |
1698 | } |
1699 | fz_always(ctx) |
1700 | { |
1701 | pdf_drop_obj(ctx, res); |
1702 | fz_drop_buffer(ctx, buf); |
1703 | } |
1704 | fz_catch(ctx) |
1705 | fz_rethrow(ctx); |
1706 | return ap; |
1707 | } |
1708 | |
1709 | static void pdf_update_button_appearance(fz_context *ctx, pdf_annot *annot) |
1710 | { |
1711 | int ff = pdf_field_flags(ctx, annot->obj); |
1712 | fz_rect rect = pdf_dict_get_rect(ctx, annot->obj, PDF_NAME(Rect)); |
1713 | fz_matrix matrix; |
1714 | fz_rect bbox; |
1715 | float w, h, t; |
1716 | int r; |
1717 | |
1718 | r = pdf_dict_get_int(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME(MK)), PDF_NAME(R)); |
1719 | w = rect.x1 - rect.x0; |
1720 | h = rect.y1 - rect.y0; |
1721 | r = r % 360; |
1722 | if (r == 90 || r == 270) |
1723 | t = h, h = w, w = t; |
1724 | matrix = fz_rotate(r); |
1725 | bbox = fz_make_rect(0, 0, w, h); |
1726 | |
1727 | |
1728 | if (ff & PDF_BTN_FIELD_IS_PUSHBUTTON) |
1729 | { |
1730 | pdf_obj *ap_n = NULL; |
1731 | pdf_obj *ap_d = NULL; |
1732 | fz_var(ap_n); |
1733 | fz_var(ap_d); |
1734 | fz_try(ctx) |
1735 | { |
1736 | pdf_obj *ap, *MK, *CA, *AC; |
1737 | const char *font; |
1738 | const char *label; |
1739 | float size, color[3]; |
1740 | |
1741 | pdf_annot_default_appearance(ctx, annot, &font, &size, color); |
1742 | |
1743 | MK = pdf_dict_get(ctx, annot->obj, PDF_NAME(MK)); |
1744 | CA = pdf_dict_get(ctx, MK, PDF_NAME(CA)); |
1745 | AC = pdf_dict_get(ctx, MK, PDF_NAME(AC)); |
1746 | |
1747 | label = pdf_to_text_string(ctx, CA); |
1748 | ap_n = draw_push_button(ctx, annot, bbox, matrix, w, h, label, font, size, color, 0); |
1749 | |
1750 | label = pdf_to_text_string(ctx, AC ? AC : CA); |
1751 | ap_d = draw_push_button(ctx, annot, bbox, matrix, w, h, label, font, size, color, 1); |
1752 | |
1753 | ap = pdf_dict_put_dict(ctx, annot->obj, PDF_NAME(AP), 2); |
1754 | pdf_dict_put(ctx, ap, PDF_NAME(N), ap_n); |
1755 | pdf_dict_put(ctx, ap, PDF_NAME(D), ap_d); |
1756 | |
1757 | pdf_drop_obj(ctx, annot->ap); |
1758 | if (annot->is_hot && annot->is_active) |
1759 | annot->ap = pdf_keep_obj(ctx, ap_d); |
1760 | else |
1761 | annot->ap = pdf_keep_obj(ctx, ap_n); |
1762 | annot->has_new_ap = 1; |
1763 | } |
1764 | fz_always(ctx) |
1765 | { |
1766 | pdf_drop_obj(ctx, ap_n); |
1767 | pdf_drop_obj(ctx, ap_d); |
1768 | } |
1769 | fz_catch(ctx) |
1770 | fz_rethrow(ctx); |
1771 | } |
1772 | else |
1773 | { |
1774 | pdf_obj *as_yes = NULL; |
1775 | pdf_obj *ap_off = NULL; |
1776 | pdf_obj *ap_yes = NULL; |
1777 | fz_var(ap_off); |
1778 | fz_var(ap_yes); |
1779 | fz_var(as_yes); |
1780 | fz_try(ctx) |
1781 | { |
1782 | pdf_obj *ap, *ap_n, *as; |
1783 | |
1784 | if (w > h) w = h; |
1785 | if (h > w) h = w; |
1786 | |
1787 | if (ff & PDF_BTN_FIELD_IS_RADIO) |
1788 | { |
1789 | ap_off = draw_radio_button(ctx, annot, bbox, matrix, w, h, 0); |
1790 | ap_yes = draw_radio_button(ctx, annot, bbox, matrix, w, h, 1); |
1791 | } |
1792 | else |
1793 | { |
1794 | ap_off = draw_check_button(ctx, annot, bbox, matrix, w, h, 0); |
1795 | ap_yes = draw_check_button(ctx, annot, bbox, matrix, w, h, 1); |
1796 | } |
1797 | |
1798 | as = pdf_dict_get(ctx, annot->obj, PDF_NAME(AS)); |
1799 | if (!as) |
1800 | { |
1801 | pdf_dict_put(ctx, annot->obj, PDF_NAME(AS), PDF_NAME(Off)); |
1802 | as = PDF_NAME(Off); |
1803 | } |
1804 | |
1805 | if (as == PDF_NAME(Off)) |
1806 | as_yes = pdf_keep_obj(ctx, pdf_button_field_on_state(ctx, annot->obj)); |
1807 | else |
1808 | as_yes = pdf_keep_obj(ctx, as); |
1809 | |
1810 | ap = pdf_dict_put_dict(ctx, annot->obj, PDF_NAME(AP), 2); |
1811 | ap_n = pdf_dict_put_dict(ctx, ap, PDF_NAME(N), 2); |
1812 | pdf_dict_put(ctx, ap_n, PDF_NAME(Off), ap_off); |
1813 | pdf_dict_put(ctx, ap_n, as_yes, ap_yes); |
1814 | |
1815 | pdf_drop_obj(ctx, annot->ap); |
1816 | if (as == PDF_NAME(Off)) |
1817 | annot->ap = pdf_keep_obj(ctx, ap_off); |
1818 | else |
1819 | annot->ap = pdf_keep_obj(ctx, ap_yes); |
1820 | annot->has_new_ap = 1; |
1821 | } |
1822 | fz_always(ctx) |
1823 | { |
1824 | pdf_drop_obj(ctx, as_yes); |
1825 | pdf_drop_obj(ctx, ap_yes); |
1826 | pdf_drop_obj(ctx, ap_off); |
1827 | } |
1828 | fz_catch(ctx) |
1829 | { |
1830 | fz_rethrow(ctx); |
1831 | } |
1832 | } |
1833 | } |
1834 | |
1835 | void pdf_update_signature_appearance(fz_context *ctx, pdf_annot *annot, const char *name, const char *dn, const char *date) |
1836 | { |
1837 | pdf_obj *ap, *new_ap_n, *res_font; |
1838 | char tmp[500]; |
1839 | fz_font *helv = NULL; |
1840 | fz_font *zadb = NULL; |
1841 | pdf_obj *res = NULL; |
1842 | fz_buffer *buf; |
1843 | fz_rect rect; |
1844 | float w, h, size, name_w; |
1845 | |
1846 | fz_var(helv); |
1847 | fz_var(zadb); |
1848 | fz_var(res); |
1849 | |
1850 | buf = fz_new_buffer(ctx, 1024); |
1851 | fz_try(ctx) |
1852 | { |
1853 | if (name && dn) |
1854 | { |
1855 | helv = fz_new_base14_font(ctx, "Helvetica" ); |
1856 | zadb = fz_new_base14_font(ctx, "ZapfDingbats" ); |
1857 | |
1858 | res = pdf_new_dict(ctx, annot->page->doc, 1); |
1859 | res_font = pdf_dict_put_dict(ctx, res, PDF_NAME(Font), 1); |
1860 | pdf_dict_put_drop(ctx, res_font, PDF_NAME(Helv), pdf_add_simple_font(ctx, annot->page->doc, helv, 0)); |
1861 | pdf_dict_put_drop(ctx, res_font, PDF_NAME(ZaDb), pdf_add_simple_font(ctx, annot->page->doc, zadb, 0)); |
1862 | |
1863 | rect = pdf_dict_get_rect(ctx, annot->obj, PDF_NAME(Rect)); |
1864 | w = (rect.x1 - rect.x0) / 2; |
1865 | h = (rect.y1 - rect.y0); |
1866 | |
1867 | /* Use flower symbol from ZapfDingbats as sigil */ |
1868 | fz_append_printf(ctx, buf, "q 1 0.8 0.8 rg BT /ZaDb %g Tf %g %g Td (`) Tj ET Q\n" , |
1869 | h*1.1f, |
1870 | rect.x0 + w - (h*0.4f), |
1871 | rect.y0 + h*0.1f); |
1872 | |
1873 | /* Name */ |
1874 | name_w = measure_simple_string(ctx, helv, name); |
1875 | size = fz_min(fz_min((w - 4) / name_w, h), 24); |
1876 | fz_append_string(ctx, buf, "BT\n" ); |
1877 | fz_append_printf(ctx, buf, "/Helv %g Tf\n" , size); |
1878 | fz_append_printf(ctx, buf, "%g %g Td\n" , rect.x0+2, rect.y1 - size*0.8f - (h-size)/2); |
1879 | write_simple_string(ctx, buf, name, name + strlen(name)); |
1880 | fz_append_string(ctx, buf, " Tj\n" ); |
1881 | fz_append_string(ctx, buf, "ET\n" ); |
1882 | |
1883 | /* Information text */ |
1884 | size = fz_min(fz_min((w / 12), h / 6), 16); |
1885 | fz_append_string(ctx, buf, "BT\n" ); |
1886 | fz_append_printf(ctx, buf, "/Helv %g Tf\n" , size); |
1887 | fz_append_printf(ctx, buf, "%g TL\n" , size); |
1888 | fz_append_printf(ctx, buf, "%g %g Td\n" , rect.x0+w+2, rect.y1); |
1889 | fz_snprintf(tmp, sizeof tmp, "Digitally signed by %s" , name); |
1890 | write_simple_string_with_quadding(ctx, buf, helv, size, tmp, w-4, 0); |
1891 | fz_snprintf(tmp, sizeof tmp, "DN: %s" , dn); |
1892 | write_simple_string_with_quadding(ctx, buf, helv, size, tmp, w-4, 0); |
1893 | if (date) |
1894 | { |
1895 | fz_snprintf(tmp, sizeof tmp, "Date: %s" , date); |
1896 | write_simple_string_with_quadding(ctx, buf, helv, size, tmp, w-4, 0); |
1897 | } |
1898 | fz_append_string(ctx, buf, "ET\n" ); |
1899 | } |
1900 | else |
1901 | { |
1902 | rect.x0 = rect.y0 = 0; |
1903 | rect.x1 = rect.y1 = 100; |
1904 | res = pdf_new_dict(ctx, annot->page->doc, 0); |
1905 | fz_append_string(ctx, buf, "% DSBlank\n" ); |
1906 | } |
1907 | |
1908 | /* Update the AP/N stream */ |
1909 | ap = pdf_dict_get(ctx, annot->obj, PDF_NAME(AP)); |
1910 | if (!ap) |
1911 | ap = pdf_dict_put_dict(ctx, annot->obj, PDF_NAME(AP), 1); |
1912 | new_ap_n = pdf_new_xobject(ctx, annot->page->doc, rect, fz_identity, res, buf); |
1913 | pdf_drop_obj(ctx, annot->ap); |
1914 | annot->ap = new_ap_n; |
1915 | annot->has_new_ap = 1; |
1916 | pdf_dict_put(ctx, ap, PDF_NAME(N), new_ap_n); |
1917 | } |
1918 | fz_always(ctx) |
1919 | { |
1920 | fz_drop_font(ctx, helv); |
1921 | fz_drop_font(ctx, zadb); |
1922 | pdf_drop_obj(ctx, res); |
1923 | fz_drop_buffer(ctx, buf); |
1924 | } |
1925 | fz_catch(ctx) |
1926 | { |
1927 | fz_rethrow(ctx); |
1928 | } |
1929 | } |
1930 | |
1931 | /* |
1932 | Recreate the appearance stream for an annotation, if necessary. |
1933 | */ |
1934 | void pdf_update_appearance(fz_context *ctx, pdf_annot *annot) |
1935 | { |
1936 | pdf_obj *subtype; |
1937 | pdf_obj *ap, *ap_n, *as; |
1938 | |
1939 | subtype = pdf_dict_get(ctx, annot->obj, PDF_NAME(Subtype)); |
1940 | if (subtype == PDF_NAME(Popup)) |
1941 | return; |
1942 | if (subtype == PDF_NAME(Link)) |
1943 | return; |
1944 | |
1945 | /* Check if the field is dirtied by JS events */ |
1946 | if (pdf_obj_is_dirty(ctx, annot->obj)) |
1947 | annot->needs_new_ap = 1; |
1948 | |
1949 | /* Check if the current appearance has been swapped */ |
1950 | as = pdf_dict_get(ctx, annot->obj, PDF_NAME(AS)); |
1951 | ap = pdf_dict_get(ctx, annot->obj, PDF_NAME(AP)); |
1952 | ap_n = pdf_dict_get(ctx, ap, PDF_NAME(N)); |
1953 | if (annot->is_hot && annot->is_active && subtype == PDF_NAME(Widget)) |
1954 | { |
1955 | pdf_obj *ap_d = pdf_dict_get(ctx, ap, PDF_NAME(D)); |
1956 | if (ap_d) |
1957 | ap_n = ap_d; |
1958 | } |
1959 | if (!pdf_is_stream(ctx, ap_n)) |
1960 | ap_n = pdf_dict_get(ctx, ap_n, as); |
1961 | if (annot->ap != ap_n) |
1962 | { |
1963 | pdf_drop_obj(ctx, annot->ap); |
1964 | annot->ap = NULL; |
1965 | if (pdf_is_stream(ctx, ap_n)) |
1966 | annot->ap = pdf_keep_obj(ctx, ap_n); |
1967 | annot->has_new_ap = 1; |
1968 | } |
1969 | |
1970 | if (!annot->ap || annot->needs_new_ap) |
1971 | { |
1972 | fz_rect rect, bbox; |
1973 | fz_matrix matrix = fz_identity; |
1974 | fz_buffer *buf; |
1975 | pdf_obj *res = NULL; |
1976 | pdf_obj *new_ap_n = NULL; |
1977 | fz_var(res); |
1978 | fz_var(new_ap_n); |
1979 | |
1980 | annot->needs_new_ap = 0; |
1981 | |
1982 | /* Special case for Btn widgets that need multiple appearance streams. */ |
1983 | if (pdf_name_eq(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME(Subtype)), PDF_NAME(Widget))) |
1984 | { |
1985 | if (pdf_name_eq(ctx, pdf_dict_get_inheritable(ctx, annot->obj, PDF_NAME(FT)), PDF_NAME(Btn))) |
1986 | { |
1987 | pdf_update_button_appearance(ctx, annot); |
1988 | pdf_clean_obj(ctx, annot->obj); |
1989 | return; |
1990 | } |
1991 | } |
1992 | |
1993 | buf = fz_new_buffer(ctx, 1024); |
1994 | fz_try(ctx) |
1995 | { |
1996 | rect = pdf_dict_get_rect(ctx, annot->obj, PDF_NAME(Rect)); |
1997 | pdf_write_appearance(ctx, annot, buf, &rect, &bbox, &matrix, &res); |
1998 | pdf_dict_put_rect(ctx, annot->obj, PDF_NAME(Rect), rect); |
1999 | |
2000 | if (!ap_n) |
2001 | { |
2002 | if (!ap) |
2003 | { |
2004 | ap = pdf_new_dict(ctx, annot->page->doc, 1); |
2005 | pdf_dict_put_drop(ctx, annot->obj, PDF_NAME(AP), ap); |
2006 | } |
2007 | new_ap_n = pdf_new_xobject(ctx, annot->page->doc, bbox, matrix, res, buf); |
2008 | pdf_dict_put(ctx, ap, PDF_NAME(N), new_ap_n); |
2009 | } |
2010 | else |
2011 | { |
2012 | new_ap_n = pdf_keep_obj(ctx, ap_n); |
2013 | pdf_update_xobject(ctx, annot->page->doc, ap_n, bbox, matrix, res, buf); |
2014 | } |
2015 | |
2016 | pdf_drop_obj(ctx, annot->ap); |
2017 | annot->ap = NULL; |
2018 | annot->ap = pdf_keep_obj(ctx, new_ap_n); |
2019 | annot->has_new_ap = 1; |
2020 | } |
2021 | fz_always(ctx) |
2022 | { |
2023 | fz_drop_buffer(ctx, buf); |
2024 | pdf_drop_obj(ctx, res); |
2025 | pdf_drop_obj(ctx, new_ap_n); |
2026 | } |
2027 | fz_catch(ctx) |
2028 | { |
2029 | fz_warn(ctx, "cannot create appearance stream" ); |
2030 | } |
2031 | } |
2032 | |
2033 | pdf_clean_obj(ctx, annot->obj); |
2034 | } |
2035 | |
2036 | /* |
2037 | Regenerate any appearance streams that are out of date and check for |
2038 | cases where a different appearance stream should be selected because of |
2039 | state changes. |
2040 | |
2041 | Note that a call to pdf_pass_event for one page may lead to changes on |
2042 | any other, so an app should call pdf_update_annot for every annotation |
2043 | it currently displays. Also it is important that the pdf_annot object |
2044 | is the one used to last render the annotation. If instead the app were |
2045 | to drop the page or annotations and reload them then a call to |
2046 | pdf_update_annot would not reliably be able to report all changed |
2047 | annotations. |
2048 | |
2049 | Returns true if the annotation appearance has changed since the last time |
2050 | pdf_update_annot was called or the annotation was first loaded. |
2051 | */ |
2052 | int |
2053 | pdf_update_annot(fz_context *ctx, pdf_annot *annot) |
2054 | { |
2055 | int changed; |
2056 | |
2057 | pdf_update_appearance(ctx, annot); |
2058 | |
2059 | changed = annot->has_new_ap; |
2060 | annot->has_new_ap = 0; |
2061 | return changed; |
2062 | } |
2063 | |