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
16static 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
23static 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
38static 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
62static 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
77static 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
92static 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
107static 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
122static 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
129static 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
149static 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
157static void
158pdf_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
240static void
241pdf_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
277static void
278pdf_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
297static void
298draw_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
310static void
311draw_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
320static void
321pdf_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
333static void
334pdf_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
371static void
372pdf_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 */
413enum { UL, UR, LL, LR };
414
415static float
416extract_quad(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
432static void
433union_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
443static fz_point
444lerp_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
449static void
450pdf_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
514static void
515pdf_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
549static void
550pdf_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
583static void
584pdf_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
630static void
631pdf_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
665static void
666pdf_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
682static void
683pdf_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
742static float
743measure_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
758static void
759write_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
775static void
776write_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
781static void
782write_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
793static void
794pdf_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
882static float
883break_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
908static void
909write_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
938static void
939write_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
966static void
967layout_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
993static void
994layout_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
1013static void
1014layout_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
1055static 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
1065static void
1066write_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
1145static void
1146layout_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
1216static void
1217pdf_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
1256static void
1257pdf_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
1323fz_layout_block *
1324pdf_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
1382static void
1383pdf_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
1424static void
1425pdf_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
1443static void
1444pdf_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
1486static void
1487pdf_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
1573static 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
1646static 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
1676static 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
1709static 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
1835void 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*/
1934void 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*/
2052int
2053pdf_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