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