comparison mupdf-source/source/pdf/pdf-appearance.c @ 2:b50eed0cc0ef upstream

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
comparison
equal deleted inserted replaced
1:1d09e1dec1d9 2:b50eed0cc0ef
1 // Copyright (C) 2004-2025 Artifex Software, Inc.
2 //
3 // This file is part of MuPDF.
4 //
5 // MuPDF is free software: you can redistribute it and/or modify it under the
6 // terms of the GNU Affero General Public License as published by the Free
7 // Software Foundation, either version 3 of the License, or (at your option)
8 // any later version.
9 //
10 // MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY
11 // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12 // FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
13 // details.
14 //
15 // You should have received a copy of the GNU Affero General Public License
16 // along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html>
17 //
18 // Alternative licensing terms are available from the licensor.
19 // For commercial licensing, see <https://www.artifex.com/> or contact
20 // Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco,
21 // CA 94129, USA, for further information.
22
23 #include "mupdf/fitz.h"
24 #include "pdf-annot-imp.h"
25 #include "mupdf/ucdn.h"
26
27 #include <float.h>
28 #include <limits.h>
29 #include <string.h>
30 #include <time.h>
31
32 #include <stdio.h>
33
34 #include "annotation-icons.h"
35
36 /* #define PDF_DEBUG_APPEARANCE_SYNTHESIS */
37
38 #define REPLACEMENT 0xB7
39 #define CIRCLE_MAGIC 0.551915f
40
41 static fz_point rect_center(const fz_rect rect)
42 {
43 fz_point c;
44 c.x = (rect.x0 + rect.x1) / 2.0f;
45 c.y = (rect.y0 + rect.y1) / 2.0f;
46 return c;
47 }
48
49 static fz_matrix center_rect_within_rect(const fz_rect tofit, const fz_rect within)
50 {
51 float xscale = (within.x1 - within.x0) / (tofit.x1 - tofit.x0);
52 float yscale = (within.y1 - within.y0) / (tofit.y1 - tofit.y0);
53 float scale = fz_min(xscale, yscale);
54 fz_point tofit_center;
55 fz_point within_center;
56
57 within_center = rect_center(within);
58 tofit_center = rect_center(tofit);
59
60 /* Translate "tofit" to be centered on the origin
61 * Scale "tofit" to a size that fits within "within"
62 * Translate "tofit" to "within's" center
63 * Do all the above in reverse order so that we can use the fz_pre_xx functions */
64 return fz_pre_translate(fz_pre_scale(fz_translate(within_center.x, within_center.y), scale, -scale), -tofit_center.x, -tofit_center.y);
65 }
66
67 static void
68 draw_circle(fz_context *ctx, fz_buffer *buf, float rx, float ry, float cx, float cy)
69 {
70 float mx = rx * CIRCLE_MAGIC;
71 float my = ry * CIRCLE_MAGIC;
72 fz_append_printf(ctx, buf, "%g %g m\n", cx, cy+ry);
73 fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n", cx+mx, cy+ry, cx+rx, cy+my, cx+rx, cy);
74 fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n", cx+rx, cy-my, cx+mx, cy-ry, cx, cy-ry);
75 fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n", cx-mx, cy-ry, cx-rx, cy-my, cx-rx, cy);
76 fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n", cx-rx, cy+my, cx-mx, cy+ry, cx, cy+ry);
77 }
78
79 static void
80 draw_circle_in_box(fz_context *ctx, fz_buffer *buf, float lw, float x0, float y0, float x1, float y1)
81 {
82 float rx = (x1 - x0) / 2 - lw/2;
83 float ry = (y1 - y0) / 2 - lw/2;
84 float cx = x0 + lw/2 + rx;
85 float cy = y0 + lw/2 + ry;
86 draw_circle(ctx, buf, rx, ry, cx, cy);
87 }
88
89 static void
90 draw_arc_seg(fz_context *ctx, fz_buffer *buf, float r, float xc, float yc, float th0, float th1, int move)
91 {
92 float x1 = xc + r * cosf(th0);
93 float y1 = yc + r * sinf(th0);
94 float x4 = xc + r * cosf(th1);
95 float y4 = yc + r * sinf(th1);
96
97 float ax = x1 - xc;
98 float ay = y1 - yc;
99 float bx = x4 - xc;
100 float by = y4 - yc;
101 float q1 = ax * ax + ay * ay;
102 float q2 = q1 + ax * bx + ay * by;
103 float k2 = (4.0f/3.0f) * (sqrtf(2 * q1 * q2) - q2) / (ax * by - ay * bx);
104
105 float x2 = xc + ax - k2 * ay;
106 float y2 = yc + ay + k2 * ax;
107 float x3 = xc + bx + k2 * by;
108 float y3 = yc + by - k2 * bx;
109
110 if (move)
111 fz_append_printf(ctx, buf, "%g %g m\n", x1, y1);
112 fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n", x2, y2, x3, y3, x4, y4);
113 }
114
115 static void
116 draw_arc(fz_context *ctx, fz_buffer *buf, float r, float xc, float yc, float th0, float th1, int move)
117 {
118 float d = th0 - th1;
119 if (d > FZ_PI / 4)
120 {
121 draw_arc(ctx, buf, r, xc, yc, th0, th0 - d / 2, move);
122 draw_arc(ctx, buf, r, xc, yc, th0 - d / 2, th1, 0);
123 }
124 else
125 {
126 draw_arc_seg(ctx, buf, r, xc, yc, th0, th1, move);
127 }
128 }
129
130 static void
131 draw_arc_tail(fz_context *ctx, fz_buffer *buf, float r, float xc, float yc, float th0, float th1, int fill)
132 {
133 draw_arc_seg(ctx, buf, r, xc, yc, th0, th1, 0);
134 if (fill)
135 draw_arc_seg(ctx, buf, r, xc, yc, th1, th0, 0);
136 }
137
138 static void
139 pdf_write_opacity_blend_mode(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, pdf_obj **res, int bm)
140 {
141 pdf_obj *res_egs, *res_egs_h;
142 float opacity = pdf_annot_opacity(ctx, annot);
143
144 if (bm == FZ_BLEND_NORMAL && opacity == 1)
145 return;
146
147 /* /Resources << /ExtGState << /H << /Type/ExtGState /BM/Multiply /CA %g /ca %g >> >> >> */
148
149 if (!*res)
150 *res = pdf_new_dict(ctx, annot->page->doc, 1);
151
152 res_egs = pdf_dict_put_dict(ctx, *res, PDF_NAME(ExtGState), 1);
153 res_egs_h = pdf_dict_put_dict(ctx, res_egs, PDF_NAME(H), 2);
154 pdf_dict_put(ctx, res_egs_h, PDF_NAME(Type), PDF_NAME(ExtGState));
155
156 if (bm == FZ_BLEND_MULTIPLY)
157 {
158 pdf_dict_put(ctx, res_egs_h, PDF_NAME(BM), PDF_NAME(Multiply));
159 }
160
161 if (opacity < 1)
162 {
163 pdf_dict_put_real(ctx, res_egs_h, PDF_NAME(CA), opacity);
164 pdf_dict_put_real(ctx, res_egs_h, PDF_NAME(ca), opacity);
165 }
166
167 fz_append_printf(ctx, buf, "/H gs\n");
168 }
169
170 static void
171 pdf_write_opacity(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, pdf_obj **res)
172 {
173 pdf_write_opacity_blend_mode(ctx, annot, buf, res, FZ_BLEND_NORMAL);
174 }
175
176 static void
177 pdf_write_dash_pattern(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, pdf_obj **res)
178 {
179 int count = pdf_annot_border_dash_count(ctx, annot);
180 int i;
181
182 if (count == 0)
183 return;
184
185 fz_append_printf(ctx, buf, "[");
186 for (i = 0; i < count; ++i)
187 {
188 float length = pdf_annot_border_dash_item(ctx, annot, i);
189 if (i == 0)
190 fz_append_printf(ctx, buf, "%g", length);
191 else
192 fz_append_printf(ctx, buf, " %g", length);
193 }
194 fz_append_printf(ctx, buf, "]0 d\n");
195 }
196
197 static float pdf_write_border_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf)
198 {
199 float w = pdf_annot_border_width(ctx, annot);
200 fz_append_printf(ctx, buf, "%g w\n", w);
201 return w;
202 }
203
204 static int
205 write_color(fz_context *ctx, fz_buffer *buf, int n, float *color, int stroke)
206 {
207 if (n == 4)
208 fz_append_printf(ctx, buf, "%g %g %g %g %c\n", color[0], color[1], color[2], color[3], stroke ? 'K' : 'k');
209 else if (n == 3)
210 fz_append_printf(ctx, buf, "%g %g %g %s\n", color[0], color[1], color[2], stroke ? "RG" : "rg");
211 else if (n == 1)
212 fz_append_printf(ctx, buf, "%g %c\n", color[0], stroke ? 'G' : 'g');
213 else
214 return 0;
215 return 1;
216 }
217
218 static void
219 write_color0(fz_context *ctx, fz_buffer *buf, int n, float *color, int stroke)
220 {
221 if (n == 0)
222 fz_append_printf(ctx, buf, "0 %c\n", stroke ? 'G' : 'g');
223 else
224 write_color(ctx, buf, n, color, stroke);
225 }
226
227
228 static int pdf_write_stroke_color_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf)
229 {
230 float color[4];
231 int n;
232 pdf_annot_color(ctx, annot, &n, color);
233 return write_color(ctx, buf, n, color, 1);
234 }
235
236 static int pdf_is_dark_fill_color(fz_context *ctx, pdf_annot *annot)
237 {
238 float color[4], gray;
239 int n;
240 pdf_annot_color(ctx, annot, &n, color);
241 switch (n)
242 {
243 default:
244 gray = 1;
245 break;
246 case 1:
247 gray = color[0];
248 break;
249 case 3:
250 gray = color[0] * 0.3f + color[1] * 0.59f + color[2] * 0.11f;
251 break;
252 case 4:
253 gray = color[0] * 0.3f + color[1] * 0.59f + color[2] * 0.11f + color[3];
254 gray = 1 - fz_min(gray, 1);
255 break;
256 }
257 return gray < 0.25f;
258 }
259
260 static int pdf_write_fill_color_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf)
261 {
262 float color[4];
263 int n;
264 pdf_annot_color(ctx, annot, &n, color);
265 return write_color(ctx, buf, n, color, 0);
266 }
267
268 static int pdf_write_interior_fill_color_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf)
269 {
270 float color[4];
271 int n;
272 pdf_annot_interior_color(ctx, annot, &n, color);
273 return write_color(ctx, buf, n, color, 0);
274 }
275
276 static int pdf_write_MK_BG_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf)
277 {
278 float color[4];
279 int n;
280 pdf_annot_MK_BG(ctx, annot, &n, color);
281 return write_color(ctx, buf, n, color, 0);
282 }
283
284 static int pdf_write_MK_BC_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf)
285 {
286 float color[4];
287 int n;
288 pdf_annot_MK_BC(ctx, annot, &n, color);
289 return write_color(ctx, buf, n, color, 1);
290 }
291
292 static void maybe_stroke_and_fill(fz_context *ctx, fz_buffer *buf, int sc, int ic)
293 {
294 if (sc)
295 fz_append_string(ctx, buf, ic ? "b\n" : "S\n");
296 else
297 fz_append_string(ctx, buf, ic ? "f\n" : "n\n");
298 }
299
300 static void maybe_stroke(fz_context *ctx, fz_buffer *buf, int sc)
301 {
302 fz_append_string(ctx, buf, sc ? "S\n" : "n\n");
303 }
304
305 static fz_point rotate_vector(float angle, float x, float y)
306 {
307 float ca = cosf(angle);
308 float sa = sinf(angle);
309 return fz_make_point(x*ca - y*sa, x*sa + y*ca);
310 }
311
312 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, int close)
313 {
314 float r = fz_max(1, w);
315 float angle = atan2f(dy, dx);
316 fz_point v, a, b;
317
318 v = rotate_vector(angle, 8.8f*r, 4.5f*r);
319 a = fz_make_point(x + v.x, y + v.y);
320 v = rotate_vector(angle, 8.8f*r, -4.5f*r);
321 b = fz_make_point(x + v.x, y + v.y);
322
323 *rect = fz_include_point_in_rect(*rect, a);
324 *rect = fz_include_point_in_rect(*rect, b);
325 *rect = fz_expand_rect(*rect, w);
326
327 fz_append_printf(ctx, buf, "%g %g m\n", a.x, a.y);
328 fz_append_printf(ctx, buf, "%g %g l\n", x, y);
329 fz_append_printf(ctx, buf, "%g %g l\n", b.x, b.y);
330 if (close)
331 fz_append_printf(ctx, buf, "h\n");
332 }
333
334 static void include_cap(fz_rect *rect, float x, float y, float r)
335 {
336 rect->x0 = fz_min(rect->x0, x-r);
337 rect->y0 = fz_min(rect->y0, y-r);
338 rect->x1 = fz_max(rect->x1, x+r);
339 rect->y1 = fz_max(rect->y1, y+r);
340 }
341
342 static void
343 pdf_write_line_cap_appearance(fz_context *ctx, fz_buffer *buf, fz_rect *rect,
344 float x, float y, float dx, float dy, float w,
345 int sc, int ic, pdf_obj *cap)
346 {
347 float l = sqrtf(dx*dx + dy*dy);
348 dx = dx / l;
349 dy = dy / l;
350
351 if (cap == PDF_NAME(Square))
352 {
353 float r = fz_max(3.0f, w * 3.0f);
354 fz_append_printf(ctx, buf, "%g %g %g %g re\n", x-r, y-r, r*2, r*2);
355 maybe_stroke_and_fill(ctx, buf, sc, ic);
356 include_cap(rect, x, y, r + w/2);
357 }
358 else if (cap == PDF_NAME(Circle))
359 {
360 float r = fz_max(3.0f, w * 3.0f);
361 float m = r * CIRCLE_MAGIC;
362 fz_append_printf(ctx, buf, "%g %g m\n", x, y+r);
363 fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n", x+m, y+r, x+r, y+m, x+r, y);
364 fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n", x+r, y-m, x+m, y-r, x, y-r);
365 fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n", x-m, y-r, x-r, y-m, x-r, y);
366 fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n", x-r, y+m, x-m, y+r, x, y+r);
367 maybe_stroke_and_fill(ctx, buf, sc, ic);
368 include_cap(rect, x, y, r + w/2);
369 }
370 else if (cap == PDF_NAME(Diamond))
371 {
372 float r = fz_max(3.0f, w * 3.0f);
373 fz_append_printf(ctx, buf, "%g %g m\n", x, y+r);
374 fz_append_printf(ctx, buf, "%g %g l\n", x+r, y);
375 fz_append_printf(ctx, buf, "%g %g l\n", x, y-r);
376 fz_append_printf(ctx, buf, "%g %g l\n", x-r, y);
377 fz_append_printf(ctx, buf, "h\n");
378 maybe_stroke_and_fill(ctx, buf, sc, ic);
379 include_cap(rect, x, y, r + w/sqrtf(2));
380 }
381 else if (cap == PDF_NAME(OpenArrow))
382 {
383 pdf_write_arrow_appearance(ctx, buf, rect, x, y, dx, dy, w, 0);
384 maybe_stroke(ctx, buf, sc);
385 }
386 else if (cap == PDF_NAME(ClosedArrow))
387 {
388 pdf_write_arrow_appearance(ctx, buf, rect, x, y, dx, dy, w, 1);
389 maybe_stroke_and_fill(ctx, buf, sc, ic);
390 }
391 /* PDF 1.5 */
392 else if (cap == PDF_NAME(Butt))
393 {
394 float r = fz_max(3, w * 3);
395 fz_point a = { x-dy*r, y+dx*r };
396 fz_point b = { x+dy*r, y-dx*r };
397 fz_append_printf(ctx, buf, "%g %g m\n", a.x, a.y);
398 fz_append_printf(ctx, buf, "%g %g l\n", b.x, b.y);
399 maybe_stroke(ctx, buf, sc);
400 *rect = fz_include_point_in_rect(*rect, a);
401 *rect = fz_include_point_in_rect(*rect, b);
402 *rect = fz_expand_rect(*rect, w);
403 }
404 else if (cap == PDF_NAME(ROpenArrow))
405 {
406 pdf_write_arrow_appearance(ctx, buf, rect, x, y, -dx, -dy, w, 0);
407 maybe_stroke(ctx, buf, sc);
408 }
409 else if (cap == PDF_NAME(RClosedArrow))
410 {
411 pdf_write_arrow_appearance(ctx, buf, rect, x, y, -dx, -dy, w, 1);
412 maybe_stroke_and_fill(ctx, buf, sc, ic);
413 }
414 /* PDF 1.6 */
415 else if (cap == PDF_NAME(Slash))
416 {
417 float r = fz_max(5, w * 5);
418 float angle = atan2f(dy, dx) - (30 * FZ_PI / 180);
419 fz_point a, b, v;
420 v = rotate_vector(angle, 0, r);
421 a = fz_make_point(x + v.x, y + v.y);
422 v = rotate_vector(angle, 0, -r);
423 b = fz_make_point(x + v.x, y + v.y);
424 fz_append_printf(ctx, buf, "%g %g m\n", a.x, a.y);
425 fz_append_printf(ctx, buf, "%g %g l\n", b.x, b.y);
426 maybe_stroke(ctx, buf, sc);
427 *rect = fz_include_point_in_rect(*rect, a);
428 *rect = fz_include_point_in_rect(*rect, b);
429 *rect = fz_expand_rect(*rect, w);
430 }
431 }
432
433 static float pdf_write_line_caption(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, pdf_obj **res, fz_point a, fz_point b);
434
435 static void
436 pdf_write_line_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, pdf_obj **res)
437 {
438 pdf_obj *line, *le;
439 float w;
440 fz_point a, b, aa, ab, ba, bb;
441 float dx, dy, line_length, ll, lle, llo;
442 int sc;
443 int ic;
444
445 // The start and end point of the actual line (a, b)
446 // The start and end points of the leader line (aa, ab, ba, bb)
447
448 pdf_write_opacity(ctx, annot, buf, res);
449 pdf_write_dash_pattern(ctx, annot, buf, res);
450 w = pdf_write_border_appearance(ctx, annot, buf);
451 sc = pdf_write_stroke_color_appearance(ctx, annot, buf);
452 ic = pdf_write_interior_fill_color_appearance(ctx, annot, buf);
453
454 line = pdf_dict_get(ctx, annot->obj, PDF_NAME(L));
455 a.x = pdf_array_get_real(ctx, line, 0);
456 a.y = pdf_array_get_real(ctx, line, 1);
457 b.x = pdf_array_get_real(ctx, line, 2);
458 b.y = pdf_array_get_real(ctx, line, 3);
459
460 /* vector of line */
461 dx = b.x - a.x;
462 dy = b.y - a.y;
463 line_length = hypotf(dx, dy);
464 dx /= line_length;
465 dy /= line_length;
466
467 rect->x0 = fz_min(a.x, b.x);
468 rect->y0 = fz_min(a.y, b.y);
469 rect->x1 = fz_max(a.x, b.x);
470 rect->y1 = fz_max(a.y, b.y);
471
472 ll = pdf_dict_get_real(ctx, annot->obj, PDF_NAME(LL));
473
474 if (ll != 0)
475 {
476 lle = pdf_dict_get_real(ctx, annot->obj, PDF_NAME(LLE));
477 llo = pdf_dict_get_real(ctx, annot->obj, PDF_NAME(LLO));
478 if (ll < 0) {
479 lle = -lle;
480 llo = -llo;
481 }
482
483 aa = fz_make_point(a.x - dy * (llo), a.y + dx * (llo));
484 ba = fz_make_point(b.x - dy * (llo), b.y + dx * (llo));
485 ab = fz_make_point(a.x - dy * (llo + ll + lle), a.y + dx * (llo + ll + lle));
486 bb = fz_make_point(b.x - dy * (llo + ll + lle), b.y + dx * (llo + ll + lle));
487 a = fz_make_point(a.x - dy * (llo + ll), a.y + dx * (llo + ll));
488 b = fz_make_point(b.x - dy * (llo + ll), b.y + dx * (llo + ll));
489
490 fz_append_printf(ctx, buf, "%g %g m\n%g %g l\n", aa.x, aa.y, ab.x, ab.y);
491 fz_append_printf(ctx, buf, "%g %g m\n%g %g l\n", ba.x, ba.y, bb.x, bb.y);
492
493 *rect = fz_include_point_in_rect(*rect, a);
494 *rect = fz_include_point_in_rect(*rect, b);
495 *rect = fz_include_point_in_rect(*rect, ab);
496 *rect = fz_include_point_in_rect(*rect, bb);
497 }
498
499 if (pdf_dict_get_bool(ctx, annot->obj, PDF_NAME(Cap)))
500 {
501 float gap = pdf_write_line_caption(ctx, annot, buf, rect, res, a, b);
502 if (gap > 0)
503 {
504 fz_point m = fz_make_point((a.x + b.x) / 2, (a.y + b.y) / 2);
505 fz_point ca = fz_make_point(m.x - dx * gap, m.y - dy * gap);
506 fz_point cb = fz_make_point(m.x + dx * gap, m.y + dy * gap);
507 fz_append_printf(ctx, buf, "%g %g m\n%g %g l\n", a.x, a.y, ca.x, ca.y);
508 fz_append_printf(ctx, buf, "%g %g m\n%g %g l\n", cb.x, cb.y, b.x, b.y);
509 }
510 else
511 {
512 fz_append_printf(ctx, buf, "%g %g m\n%g %g l\n", a.x, a.y, b.x, b.y);
513 }
514 }
515 else
516 {
517 fz_append_printf(ctx, buf, "%g %g m\n%g %g l\n", a.x, a.y, b.x, b.y);
518 }
519
520 maybe_stroke(ctx, buf, sc);
521
522 le = pdf_dict_get(ctx, annot->obj, PDF_NAME(LE));
523 if (pdf_array_len(ctx, le) == 2)
524 {
525 dx = b.x - a.x;
526 dy = b.y - a.y;
527 pdf_write_line_cap_appearance(ctx, buf, rect, a.x, a.y, dx, dy, w, sc, ic, pdf_array_get(ctx, le, 0));
528 pdf_write_line_cap_appearance(ctx, buf, rect, b.x, b.y, -dx, -dy, w, sc, ic, pdf_array_get(ctx, le, 1));
529 }
530 *rect = fz_expand_rect(*rect, fz_max(1, w));
531 }
532
533 /* The rect diff is NOT an fz_rect. It's differences between
534 * 2 rects. We return it as a rect for convenience. */
535 fz_rect
536 pdf_annot_rect_diff(fz_context *ctx, pdf_annot *annot)
537 {
538 pdf_obj *rd_obj = pdf_dict_get(ctx, annot->obj, PDF_NAME(RD));
539 fz_rect rd;
540
541 if (!pdf_is_array(ctx, rd_obj))
542 return fz_make_rect(0, 0, 0, 0);
543
544 rd.x0 = pdf_array_get_real(ctx, rd_obj, 0);
545 rd.y0 = pdf_array_get_real(ctx, rd_obj, 1);
546 rd.x1 = pdf_array_get_real(ctx, rd_obj, 2);
547 rd.y1 = pdf_array_get_real(ctx, rd_obj, 3);
548
549 return rd;
550 }
551
552 static float
553 cloud_intensity(fz_context *ctx, pdf_annot *annot)
554 {
555 if (pdf_annot_border_effect(ctx, annot) == PDF_BORDER_EFFECT_CLOUDY)
556 return pdf_annot_border_effect_intensity(ctx, annot);
557 return 0;
558 }
559
560 struct cloud_list {
561 // store first 2 and latest 3 points
562 fz_point data[5];
563 int len;
564 int first;
565 int fill;
566 float spacing, radius, phase;
567 };
568
569 static float intersect_cloud(fz_point p0, fz_point p1, float r, int sel)
570 {
571 float dx = p1.x - p0.x;
572 float dy = p1.y - p0.y;
573 float d = sqrtf(dx * dx + dy * dy);
574 float x2, y2, x3, y3;
575 float a, h;
576
577 if (d >= r + r) return 0;
578 if (d <= 0) return 0;
579
580 a = d / 2;
581 h = sqrtf(r * r - a * a);
582
583 x2 = (p0.x + p1.x) / 2;
584 y2 = (p0.y + p1.y) / 2;
585
586 x3 = x2 - h * (p1.y - p0.y) / d;
587 y3 = y2 + h * (p1.x - p0.x) / d;
588
589 if (sel == 0)
590 return atan2(y3 - p1.y, x3 - p1.x);
591 else
592 return atan2(y3 - p0.y, x3 - p0.x);
593 }
594
595 static void start_cloud(fz_context *ctx, struct cloud_list *list, fz_buffer *buf, float border, float intensity, int fill)
596 {
597 // Constants measured from Acrobat Reader
598 list->spacing = intensity * 6.666667f + border * 0.8333333f;
599 list->radius = intensity * 4.0f + border * 0.5f;
600 list->phase = 0;
601 list->len = 0;
602 list->first = 1;
603 list->fill = fill;
604 fz_append_string(ctx, buf, "2 j\n"); // bevel join
605 }
606
607 static void emit_cloud(fz_context *ctx, struct cloud_list *list, fz_buffer *buf, fz_point a, fz_point b, fz_point c)
608 {
609 float th0 = intersect_cloud(a, b, list->radius, 0);
610 float th1 = intersect_cloud(b, c, list->radius, 1);
611 while (th1 > th0)
612 th1 -= FZ_PI * 2;
613 draw_arc(ctx, buf, list->radius, b.x, b.y, th0, th1, list->first || !list->fill);
614 draw_arc_tail(ctx, buf, list->radius, b.x, b.y, th1, th1 - FZ_PI / 8, list->fill);
615 list->first = 0;
616 }
617
618 static void add_cloud(fz_context *ctx, struct cloud_list *list, fz_buffer *buf, float x, float y)
619 {
620 if (list->len < 5)
621 {
622 list->data[list->len].x = x;
623 list->data[list->len].y = y;
624 list->len++;
625 }
626 else
627 {
628 list->data[2] = list->data[3];
629 list->data[3] = list->data[4];
630 list->data[4].x = x;
631 list->data[4].y = y;
632 }
633 if (list->len >= 3)
634 {
635 emit_cloud(ctx, list, buf, list->data[list->len-3], list->data[list->len-2], list->data[list->len-1]);
636 }
637 }
638
639 static void add_cloud_line(fz_context *ctx, struct cloud_list *list, fz_buffer *buf, float x0, float y0, float x1, float y1)
640 {
641 float dx = x1 - x0;
642 float dy = y1 - y0;
643 float total = hypotf(dx, dy);
644 float used = 0;
645 float t;
646
647 if (list->phase == 0)
648 add_cloud(ctx, list, buf, x0, y0);
649
650 while (total - used > list->spacing - list->phase)
651 {
652 used += list->spacing - list->phase;
653 t = used / total;
654 add_cloud(ctx, list, buf, x0 + dx * t, y0 + dy * t);
655 list->phase = 0;
656 }
657
658 list->phase += total - used;
659 }
660
661 static void add_cloud_circle(fz_context *ctx, struct cloud_list *list, fz_buffer *buf, float x0, float y0, float x1, float y1)
662 {
663 float cx = (x0 + x1) / 2;
664 float cy = (y0 + y1) / 2;
665 float rx = (x1 - x0) / 2;
666 float ry = (y1 - y0) / 2;
667 float da = -FZ_PI * 2 / 32;
668 int i;
669 for (i = 1; i <= 32; ++i) {
670 float ax = cx + cosf((i-1) * da) * rx;
671 float ay = cy + sinf((i-1) * da) * ry;
672 float bx = cx + cosf(i * da) * rx;
673 float by = cy + sinf(i * da) * ry;
674 add_cloud_line(ctx, list, buf, ax, ay, bx, by);
675 }
676 }
677
678 static void end_cloud(fz_context *ctx, struct cloud_list *list, fz_buffer *buf)
679 {
680 // Join up with last and first circles.
681 switch (list->len)
682 {
683 case 0:
684 break;
685 case 1:
686 draw_circle(ctx, buf, list->radius, list->radius, list->data[0].x, list->data[0].y);
687 break;
688 case 2:
689 emit_cloud(ctx, list, buf, list->data[0], list->data[1], list->data[0]);
690 emit_cloud(ctx, list, buf, list->data[1], list->data[0], list->data[1]);
691 break;
692 case 3:
693 emit_cloud(ctx, list, buf, list->data[1], list->data[2], list->data[0]);
694 emit_cloud(ctx, list, buf, list->data[2], list->data[0], list->data[1]);
695 break;
696 case 4:
697 emit_cloud(ctx, list, buf, list->data[2], list->data[3], list->data[0]);
698 emit_cloud(ctx, list, buf, list->data[3], list->data[0], list->data[1]);
699 break;
700 case 5:
701 emit_cloud(ctx, list, buf, list->data[3], list->data[4], list->data[0]);
702 emit_cloud(ctx, list, buf, list->data[4], list->data[0], list->data[1]);
703 break;
704 }
705 }
706
707 static void
708 pdf_write_square_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, pdf_obj **res)
709 {
710 struct cloud_list cloud_list;
711 fz_rect orect, rd;
712 float x, y, w, h;
713 float lw;
714 int sc;
715 int ic;
716 float cloud;
717 float exp;
718
719 pdf_write_opacity(ctx, annot, buf, res);
720 pdf_write_dash_pattern(ctx, annot, buf, res);
721 lw = pdf_write_border_appearance(ctx, annot, buf);
722 sc = pdf_write_stroke_color_appearance(ctx, annot, buf);
723 ic = pdf_write_interior_fill_color_appearance(ctx, annot, buf);
724 orect = pdf_dict_get_rect(ctx, annot->obj, PDF_NAME(Rect));
725 rd = pdf_annot_rect_diff(ctx, annot);
726
727 /* We have various rules that we need to follow here:
728 * 1) No part of what we draw should extend outside of 'Rect'.
729 * 2) The 'centre' of the border should be on 'Rect+RD'.
730 * 3) RD and linewidth will therefore have problems if they are too large.
731 * We do our best to cope with all of these.
732 */
733
734 exp = lw/2;
735 if (rd.x0 < exp)
736 rd.x0 = exp;
737 if (rd.x1 < exp)
738 rd.x1 = exp;
739 if (rd.y0 < exp)
740 rd.y0 = exp;
741 if (rd.y1 < exp)
742 rd.y1 = exp;
743
744 x = orect.x0 + rd.x0;
745 y = orect.y0 + rd.y0;
746 w = orect.x1 - orect.x0 - rd.x0 - rd.x1;
747 h = orect.y1 - orect.y0 - rd.y0 - rd.y1;
748
749 if (w < 1) w = 1;
750 if (h < 1) h = 1;
751
752 cloud = cloud_intensity(ctx, annot);
753 if (cloud > 0)
754 {
755 start_cloud(ctx, &cloud_list, buf, lw, cloud, ic);
756
757 add_cloud_line(ctx, &cloud_list, buf, x, y, x, y+h);
758 cloud_list.phase = 0;
759 add_cloud_line(ctx, &cloud_list, buf, x, y+h, x+w, y+h);
760 cloud_list.phase = 0;
761 add_cloud_line(ctx, &cloud_list, buf, x+w, y+h, x+w, y);
762 cloud_list.phase = 0;
763 add_cloud_line(ctx, &cloud_list, buf, x+w, y, x, y);
764
765 end_cloud(ctx, &cloud_list, buf);
766 exp += cloud_list.radius;
767
768 if (rd.x0 < exp)
769 rd.x0 = exp;
770 if (rd.x1 < exp)
771 rd.x1 = exp;
772 if (rd.y0 < exp)
773 rd.y0 = exp;
774 if (rd.y1 < exp)
775 rd.y1 = exp;
776 }
777 else
778 {
779 fz_append_printf(ctx, buf, "%g %g %g %g re\n", x, y, w, h);
780 }
781 maybe_stroke_and_fill(ctx, buf, sc, ic);
782
783 pdf_dict_put_rect(ctx, annot->obj, PDF_NAME(RD), rd);
784 rect->x0 = x - rd.x0;
785 rect->y0 = y - rd.y0;
786 rect->x1 = x + w + rd.x1;
787 rect->y1 = y + h + rd.y1;
788 }
789
790 static void
791 pdf_write_circle_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, pdf_obj **res)
792 {
793 struct cloud_list cloud_list;
794 fz_rect orect, rd;
795 float x, y, w, h;
796 float lw;
797 int sc;
798 int ic;
799 float cloud;
800 float exp;
801
802 pdf_write_opacity(ctx, annot, buf, res);
803 pdf_write_dash_pattern(ctx, annot, buf, res);
804 lw = pdf_write_border_appearance(ctx, annot, buf);
805 sc = pdf_write_stroke_color_appearance(ctx, annot, buf);
806 ic = pdf_write_interior_fill_color_appearance(ctx, annot, buf);
807 orect = pdf_dict_get_rect(ctx, annot->obj, PDF_NAME(Rect));
808 rd = pdf_annot_rect_diff(ctx, annot);
809
810 /* We have various rules that we need to follow here:
811 * 1) No part of what we draw should extend outside of 'Rect'.
812 * 2) The 'centre' of the border should be on 'Rect+RD'.
813 * 3) RD and linewidth will therefore have problems if they are too large.
814 * We do our best to cope with all of these.
815 */
816
817 exp = lw/2;
818 if (rd.x0 < exp)
819 rd.x0 = exp;
820 if (rd.x1 < exp)
821 rd.x1 = exp;
822 if (rd.y0 < exp)
823 rd.y0 = exp;
824 if (rd.y1 < exp)
825 rd.y1 = exp;
826
827 x = orect.x0 + rd.x0;
828 y = orect.y0 + rd.y0;
829 w = orect.x1 - orect.x0 - rd.x0 - rd.x1;
830 h = orect.y1 - orect.y0 - rd.y0 - rd.y1;
831
832 if (w < 1) w = 1;
833 if (h < 1) h = 1;
834
835 cloud = cloud_intensity(ctx, annot);
836 if (cloud > 0)
837 {
838 start_cloud(ctx, &cloud_list, buf, lw, cloud, ic);
839 add_cloud_circle(ctx, &cloud_list, buf, x, y, x+w, y+h);
840 end_cloud(ctx, &cloud_list, buf);
841 exp += cloud_list.radius;
842
843 if (rd.x0 < exp)
844 rd.x0 = exp;
845 if (rd.x1 < exp)
846 rd.x1 = exp;
847 if (rd.y0 < exp)
848 rd.y0 = exp;
849 if (rd.y1 < exp)
850 rd.y1 = exp;
851 }
852 else
853 {
854 draw_circle(ctx, buf, w / 2, h / 2, x + w / 2, y + h / 2);
855 }
856 maybe_stroke_and_fill(ctx, buf, sc, ic);
857
858 pdf_dict_put_rect(ctx, annot->obj, PDF_NAME(RD), rd);
859 rect->x0 = x - rd.x0;
860 rect->y0 = y - rd.x1;
861 rect->x1 = x + w + rd.x1;
862 rect->y1 = y + h + rd.y1;
863 }
864
865 /*
866 * This doesn't quite match Acrobat, but I haven't really got a clue
867 * what magic algorithm Acrobat is using. It'll match for all 'simple'
868 * polygons, and makes a reasonable stab for complex ones.
869 *
870 * Find the y position of the centre of the polygon. Sum the
871 * area * winding_number across that line. (Effectively an integration?)
872 * Then pick cw or ccw according to what will be leave the largest
873 * area correct.
874 */
875 static int
876 polygon_winding(fz_context *ctx, pdf_obj *v, int n)
877 {
878 int i;
879 float mid_y = 0;
880 float min_x = 0;
881 fz_point q, r;
882 float area = 0;
883
884 /* Find the centre for y, and min x. */
885 for (i = 0; i < n; i++)
886 {
887 float x = pdf_array_get_real(ctx, v, 2*i);
888 if (x < min_x || i == 0)
889 min_x = x;
890 mid_y += pdf_array_get_real(ctx, v, 2*i+1);
891 }
892 mid_y /= n;
893
894 /* Now run through finding the weighted area across that middle line. */
895 q.x = pdf_array_get_real(ctx, v, 0);
896 q.y = pdf_array_get_real(ctx, v, 1);
897 for (i = 1; i < n; i++)
898 {
899 r.x = pdf_array_get_real(ctx, v, 2*i);
900 r.y = pdf_array_get_real(ctx, v, 2*i+1);
901 if ((r.y > mid_y && mid_y > q.y) || (r.y < mid_y && mid_y < q.y))
902 {
903 int w = (r.y > mid_y) ? 1 : -1;
904 float x = r.x + (q.x - r.x) * (r.y - mid_y) / (r.y - q.y);
905
906 area += (x - min_x) * w;
907 }
908 q = r;
909 }
910
911 return area < 0;
912 }
913
914 static void
915 pdf_write_polygon_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, pdf_obj **res, int close)
916 {
917 struct cloud_list cloud_list;
918 float cloud = 0;
919 pdf_obj *verts, *le;
920 fz_point p, first = {0,0}, last = {0,0};
921 int i, n;
922 float lw;
923 int sc, ic;
924 int i0, i1, is;
925 float exp = 0;
926
927 pdf_write_opacity(ctx, annot, buf, res);
928 pdf_write_dash_pattern(ctx, annot, buf, res);
929 lw = pdf_write_border_appearance(ctx, annot, buf);
930 sc = pdf_write_stroke_color_appearance(ctx, annot, buf);
931 ic = pdf_write_interior_fill_color_appearance(ctx, annot, buf);
932
933 *rect = fz_empty_rect;
934
935 if (close)
936 cloud = cloud_intensity(ctx, annot);
937
938 verts = pdf_dict_get(ctx, annot->obj, PDF_NAME(Vertices));
939 n = pdf_array_len(ctx, verts) / 2;
940 if (n > 0)
941 {
942 if (polygon_winding(ctx, verts, n))
943 i0 = 0, i1 = n, is = 1;
944 else
945 i0 = n-1, i1 = -1, is = -1;
946
947 if (cloud > 0)
948 {
949 start_cloud(ctx, &cloud_list, buf, lw, cloud, ic);
950 }
951
952 for (i = i0; i != i1; i += is)
953 {
954 p.x = pdf_array_get_real(ctx, verts, i*2+0);
955 p.y = pdf_array_get_real(ctx, verts, i*2+1);
956 if (i == i0)
957 {
958 rect->x0 = rect->x1 = p.x;
959 rect->y0 = rect->y1 = p.y;
960 }
961 else
962 {
963 *rect = fz_include_point_in_rect(*rect, p);
964 }
965 if (cloud > 0)
966 {
967 if (i == i0)
968 first = p;
969 else
970 add_cloud_line(ctx, &cloud_list, buf, last.x, last.y, p.x, p.y);
971 last = p;
972 }
973 else
974 {
975 if (i == i0)
976 fz_append_printf(ctx, buf, "%g %g m\n", p.x, p.y);
977 else
978 fz_append_printf(ctx, buf, "%g %g l\n", p.x, p.y);
979 }
980 }
981
982 if (cloud > 0)
983 {
984 add_cloud_line(ctx, &cloud_list, buf, last.x, last.y, first.x, first.y);
985 end_cloud(ctx, &cloud_list, buf);
986 }
987 else
988 {
989 if (close)
990 fz_append_string(ctx, buf, "h\n");
991 }
992
993 if (close)
994 maybe_stroke_and_fill(ctx, buf, sc, ic);
995 else
996 maybe_stroke(ctx, buf, sc);
997
998 exp = lw;
999 if (cloud > 0)
1000 exp += cloud_list.radius;
1001
1002 *rect = fz_expand_rect(*rect, exp);
1003 }
1004
1005 le = pdf_dict_get(ctx, annot->obj, PDF_NAME(LE));
1006 if (!close && n >= 2 && pdf_array_len(ctx, le) == 2)
1007 {
1008 float dx, dy;
1009 fz_point a, b;
1010
1011 a.x = pdf_array_get_real(ctx, verts, 0*2+0);
1012 a.y = pdf_array_get_real(ctx, verts, 0*2+1);
1013 b.x = pdf_array_get_real(ctx, verts, 1*2+0);
1014 b.y = pdf_array_get_real(ctx, verts, 1*2+1);
1015
1016 dx = b.x - a.x;
1017 dy = b.y - a.y;
1018
1019 pdf_write_line_cap_appearance(ctx, buf, rect, a.x, a.y, dx, dy, lw, sc, ic, pdf_array_get(ctx, le, 0));
1020
1021 a.x = pdf_array_get_real(ctx, verts, (n-1)*2+0);
1022 a.y = pdf_array_get_real(ctx, verts, (n-1)*2+1);
1023 b.x = pdf_array_get_real(ctx, verts, (n-2)*2+0);
1024 b.y = pdf_array_get_real(ctx, verts, (n-2)*2+1);
1025
1026 dx = b.x - a.x;
1027 dy = b.y - a.y;
1028
1029 pdf_write_line_cap_appearance(ctx, buf, rect, a.x, a.y, dx, dy, lw, sc, ic, pdf_array_get(ctx, le, 1));
1030 }
1031
1032 if (exp == 0)
1033 pdf_dict_del(ctx, annot->obj, PDF_NAME(RD));
1034 else
1035 pdf_dict_put_rect(ctx, annot->obj, PDF_NAME(RD), fz_make_rect(exp, exp, exp, exp));
1036 }
1037
1038 static void
1039 pdf_write_ink_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, pdf_obj **res)
1040 {
1041 pdf_obj *ink_list, *stroke;
1042 int i, n, k, m;
1043 float lw;
1044 fz_point p;
1045 int sc;
1046
1047 pdf_write_opacity(ctx, annot, buf, res);
1048 pdf_write_dash_pattern(ctx, annot, buf, res);
1049 lw = pdf_write_border_appearance(ctx, annot, buf);
1050 sc = pdf_write_stroke_color_appearance(ctx, annot, buf);
1051
1052 *rect = fz_empty_rect;
1053
1054 fz_append_printf(ctx, buf, "1 J\n1 j\n");
1055
1056 ink_list = pdf_dict_get(ctx, annot->obj, PDF_NAME(InkList));
1057 n = pdf_array_len(ctx, ink_list);
1058 for (i = 0; i < n; ++i)
1059 {
1060 stroke = pdf_array_get(ctx, ink_list, i);
1061 m = pdf_array_len(ctx, stroke) / 2;
1062 for (k = 0; k < m; ++k)
1063 {
1064 p.x = pdf_array_get_real(ctx, stroke, k*2+0);
1065 p.y = pdf_array_get_real(ctx, stroke, k*2+1);
1066 if (i == 0 && k == 0)
1067 {
1068 rect->x0 = rect->x1 = p.x;
1069 rect->y0 = rect->y1 = p.y;
1070 }
1071 else
1072 *rect = fz_include_point_in_rect(*rect, p);
1073 fz_append_printf(ctx, buf, "%g %g %c\n", p.x, p.y, k == 0 ? 'm' : 'l');
1074 }
1075
1076 if (m == 1)
1077 fz_append_printf(ctx, buf, "%g %g %c\n", p.x, p.y, 'l');
1078 }
1079 maybe_stroke(ctx, buf, sc);
1080
1081 /* Account for line width and add an extra 6 points of whitespace padding.
1082 * In case the annotation is a dot or a perfectly horizontal/vertical line,
1083 * we need some extra size to allow selecting it easily.
1084 */
1085 *rect = fz_expand_rect(*rect, lw + 6);
1086
1087 pdf_dict_put_rect(ctx, annot->obj, PDF_NAME(RD), fz_make_rect(lw + 6, lw + 6, lw + 6, lw + 6));
1088 }
1089
1090 /* Contrary to the specification, the points within a QuadPoint are NOT
1091 * ordered in a counter-clockwise fashion starting with the lower left.
1092 * Experiments with Adobe's implementation indicates a cross-wise
1093 * ordering is intended: ul, ur, ll, lr.
1094 */
1095 enum { UL, UR, LL, LR };
1096
1097 static float
1098 extract_quad(fz_context *ctx, fz_point *quad, pdf_obj *obj, int i)
1099 {
1100 float dx, dy;
1101 quad[0].x = pdf_array_get_real(ctx, obj, i+0);
1102 quad[0].y = pdf_array_get_real(ctx, obj, i+1);
1103 quad[1].x = pdf_array_get_real(ctx, obj, i+2);
1104 quad[1].y = pdf_array_get_real(ctx, obj, i+3);
1105 quad[2].x = pdf_array_get_real(ctx, obj, i+4);
1106 quad[2].y = pdf_array_get_real(ctx, obj, i+5);
1107 quad[3].x = pdf_array_get_real(ctx, obj, i+6);
1108 quad[3].y = pdf_array_get_real(ctx, obj, i+7);
1109 dx = quad[UL].x - quad[LL].x;
1110 dy = quad[UL].y - quad[LL].y;
1111 return sqrtf(dx * dx + dy * dy);
1112 }
1113
1114 static void
1115 union_quad(fz_rect *rect, const fz_point quad[4], float lw)
1116 {
1117 fz_rect qbox;
1118 qbox.x0 = fz_min(fz_min(quad[0].x, quad[1].x), fz_min(quad[2].x, quad[3].x));
1119 qbox.y0 = fz_min(fz_min(quad[0].y, quad[1].y), fz_min(quad[2].y, quad[3].y));
1120 qbox.x1 = fz_max(fz_max(quad[0].x, quad[1].x), fz_max(quad[2].x, quad[3].x));
1121 qbox.y1 = fz_max(fz_max(quad[0].y, quad[1].y), fz_max(quad[2].y, quad[3].y));
1122 *rect = fz_union_rect(*rect, fz_expand_rect(qbox, lw));
1123 }
1124
1125 static fz_point
1126 lerp_point(fz_point a, fz_point b, float t)
1127 {
1128 return fz_make_point(a.x + t * (b.x - a.x), a.y + t * (b.y - a.y));
1129 }
1130
1131 static void
1132 pdf_write_highlight_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, pdf_obj **res)
1133 {
1134 pdf_obj *qp;
1135 fz_point quad[4], mquad[4], v;
1136 float h, m, dx, dy, vn;
1137 int i, n;
1138
1139 *rect = fz_empty_rect;
1140
1141 pdf_write_opacity_blend_mode(ctx, annot, buf, res, FZ_BLEND_MULTIPLY);
1142 pdf_write_fill_color_appearance(ctx, annot, buf);
1143
1144 qp = pdf_dict_get(ctx, annot->obj, PDF_NAME(QuadPoints));
1145 n = pdf_array_len(ctx, qp);
1146 if (n > 0)
1147 {
1148 for (i = 0; i < n; i += 8)
1149 {
1150 h = extract_quad(ctx, quad, qp, i);
1151 m = h / 4.2425f; /* magic number that matches adobe's appearance */
1152 dx = quad[LR].x - quad[LL].x;
1153 dy = quad[LR].y - quad[LL].y;
1154 vn = sqrtf(dx * dx + dy * dy);
1155 v = fz_make_point(dx * m / vn, dy * m / vn);
1156
1157 mquad[LL].x = quad[LL].x - v.x - v.y;
1158 mquad[LL].y = quad[LL].y - v.y + v.x;
1159 mquad[UL].x = quad[UL].x - v.x + v.y;
1160 mquad[UL].y = quad[UL].y - v.y - v.x;
1161 mquad[LR].x = quad[LR].x + v.x - v.y;
1162 mquad[LR].y = quad[LR].y + v.y + v.x;
1163 mquad[UR].x = quad[UR].x + v.x + v.y;
1164 mquad[UR].y = quad[UR].y + v.y - v.x;
1165
1166 fz_append_printf(ctx, buf, "%g %g m\n", quad[LL].x, quad[LL].y);
1167 fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n",
1168 mquad[LL].x, mquad[LL].y,
1169 mquad[UL].x, mquad[UL].y,
1170 quad[UL].x, quad[UL].y);
1171 fz_append_printf(ctx, buf, "%g %g l\n", quad[UR].x, quad[UR].y);
1172 fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n",
1173 mquad[UR].x, mquad[UR].y,
1174 mquad[LR].x, mquad[LR].y,
1175 quad[LR].x, quad[LR].y);
1176 fz_append_printf(ctx, buf, "f\n");
1177
1178 union_quad(rect, quad, h/16);
1179 union_quad(rect, mquad, 0);
1180 }
1181 }
1182 }
1183
1184 static void
1185 pdf_write_underline_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, pdf_obj **res)
1186 {
1187 fz_point quad[4], a, b;
1188 float h;
1189 pdf_obj *qp;
1190 int i, n;
1191
1192 *rect = fz_empty_rect;
1193
1194 pdf_write_opacity(ctx, annot, buf, res);
1195 pdf_write_stroke_color_appearance(ctx, annot, buf);
1196
1197 qp = pdf_dict_get(ctx, annot->obj, PDF_NAME(QuadPoints));
1198 n = pdf_array_len(ctx, qp);
1199 if (n > 0)
1200 {
1201 for (i = 0; i < n; i += 8)
1202 {
1203 /* Acrobat draws the line at 1/7 of the box width from the bottom
1204 * of the box and 1/16 thick of the box width. */
1205
1206 h = extract_quad(ctx, quad, qp, i);
1207 a = lerp_point(quad[LL], quad[UL], 1/7.0f);
1208 b = lerp_point(quad[LR], quad[UR], 1/7.0f);
1209
1210 fz_append_printf(ctx, buf, "%g w\n", h/16);
1211 fz_append_printf(ctx, buf, "%g %g m\n", a.x, a.y);
1212 fz_append_printf(ctx, buf, "%g %g l\n", b.x, b.y);
1213 fz_append_printf(ctx, buf, "S\n");
1214
1215 union_quad(rect, quad, h/16);
1216 }
1217 }
1218 }
1219
1220 static void
1221 pdf_write_strike_out_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, pdf_obj **res)
1222 {
1223 fz_point quad[4], a, b;
1224 float h;
1225 pdf_obj *qp;
1226 int i, n;
1227
1228 pdf_write_opacity(ctx, annot, buf, res);
1229 pdf_write_stroke_color_appearance(ctx, annot, buf);
1230
1231 qp = pdf_dict_get(ctx, annot->obj, PDF_NAME(QuadPoints));
1232 n = pdf_array_len(ctx, qp);
1233 if (n > 0)
1234 {
1235 *rect = fz_empty_rect;
1236 for (i = 0; i < n; i += 8)
1237 {
1238 /* Acrobat draws the line at 3/7 of the box width from the bottom
1239 * of the box and 1/16 thick of the box width. */
1240
1241 h = extract_quad(ctx, quad, qp, i);
1242 a = lerp_point(quad[LL], quad[UL], 3/7.0f);
1243 b = lerp_point(quad[LR], quad[UR], 3/7.0f);
1244
1245 fz_append_printf(ctx, buf, "%g w\n", h/16);
1246 fz_append_printf(ctx, buf, "%g %g m\n", a.x, a.y);
1247 fz_append_printf(ctx, buf, "%g %g l\n", b.x, b.y);
1248 fz_append_printf(ctx, buf, "S\n");
1249
1250 union_quad(rect, quad, h/16);
1251 }
1252 }
1253 }
1254
1255 static void
1256 pdf_write_squiggly_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, pdf_obj **res)
1257 {
1258 fz_point quad[4], a, b, c, v;
1259 float h, x, w;
1260 pdf_obj *qp;
1261 int i, n;
1262
1263 *rect = fz_empty_rect;
1264
1265 pdf_write_opacity(ctx, annot, buf, res);
1266 pdf_write_stroke_color_appearance(ctx, annot, buf);
1267
1268 qp = pdf_dict_get(ctx, annot->obj, PDF_NAME(QuadPoints));
1269 n = pdf_array_len(ctx, qp);
1270 if (n > 0)
1271 {
1272 for (i = 0; i < n; i += 8)
1273 {
1274 int up = 1;
1275 h = extract_quad(ctx, quad, qp, i);
1276 v = fz_make_point(quad[LR].x - quad[LL].x, quad[LR].y - quad[LL].y);
1277 w = sqrtf(v.x * v.x + v.y * v.y);
1278 x = 0;
1279
1280 fz_append_printf(ctx, buf, "%g w\n", h/16);
1281 fz_append_printf(ctx, buf, "%g %g m\n", quad[LL].x, quad[LL].y);
1282 while (x < w)
1283 {
1284 x += h/7;
1285 a = lerp_point(quad[LL], quad[LR], x/w);
1286 if (up)
1287 {
1288 b = lerp_point(quad[UL], quad[UR], x/w);
1289 c = lerp_point(a, b, 1/7.0f);
1290 fz_append_printf(ctx, buf, "%g %g l\n", c.x, c.y);
1291 }
1292 else
1293 fz_append_printf(ctx, buf, "%g %g l\n", a.x, a.y);
1294 up = !up;
1295 }
1296 fz_append_printf(ctx, buf, "S\n");
1297
1298 union_quad(rect, quad, h/16);
1299 }
1300 }
1301 }
1302
1303 static void
1304 pdf_write_redact_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, pdf_obj **res)
1305 {
1306 fz_point quad[4];
1307 pdf_obj *qp;
1308 int i, n;
1309
1310 pdf_write_opacity(ctx, annot, buf, res);
1311
1312 fz_append_printf(ctx, buf, "1 0 0 RG\n");
1313
1314 qp = pdf_dict_get(ctx, annot->obj, PDF_NAME(QuadPoints));
1315 n = pdf_array_len(ctx, qp);
1316 if (n > 0)
1317 {
1318 *rect = fz_empty_rect;
1319 for (i = 0; i < n; i += 8)
1320 {
1321 extract_quad(ctx, quad, qp, i);
1322 fz_append_printf(ctx, buf, "%g %g m\n", quad[LL].x, quad[LL].y);
1323 fz_append_printf(ctx, buf, "%g %g l\n", quad[LR].x, quad[LR].y);
1324 fz_append_printf(ctx, buf, "%g %g l\n", quad[UR].x, quad[UR].y);
1325 fz_append_printf(ctx, buf, "%g %g l\n", quad[UL].x, quad[UL].y);
1326 fz_append_printf(ctx, buf, "s\n");
1327 union_quad(rect, quad, 1);
1328 }
1329 }
1330 else
1331 {
1332 fz_append_printf(ctx, buf, "%g %g m\n", rect->x0+1, rect->y0+1);
1333 fz_append_printf(ctx, buf, "%g %g l\n", rect->x1-1, rect->y0+1);
1334 fz_append_printf(ctx, buf, "%g %g l\n", rect->x1-1, rect->y1-1);
1335 fz_append_printf(ctx, buf, "%g %g l\n", rect->x0+1, rect->y1-1);
1336 fz_append_printf(ctx, buf, "s\n");
1337 }
1338 }
1339
1340 static void
1341 pdf_write_caret_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, fz_rect *bbox, pdf_obj **res)
1342 {
1343 float xc = (rect->x0 + rect->x1) / 2;
1344 float yc = (rect->y0 + rect->y1) / 2;
1345
1346 pdf_write_opacity(ctx, annot, buf, res);
1347 pdf_write_fill_color_appearance(ctx, annot, buf);
1348
1349 fz_append_string(ctx, buf, "0 0 m\n");
1350 fz_append_string(ctx, buf, "10 0 10 7 10 14 c\n");
1351 fz_append_string(ctx, buf, "10 7 10 0 20 0 c\n");
1352 fz_append_string(ctx, buf, "f\n");
1353
1354 *rect = fz_make_rect(xc - 10, yc - 7, xc + 10, yc + 7);
1355 *bbox = fz_make_rect(0, 0, 20, 14);
1356 }
1357
1358 static void
1359 pdf_write_icon_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, fz_rect *bbox, pdf_obj **res)
1360 {
1361 const char *name;
1362 float xc = (rect->x0 + rect->x1) / 2;
1363 float yc = (rect->y0 + rect->y1) / 2;
1364
1365 pdf_write_opacity(ctx, annot, buf, res);
1366
1367 if (!pdf_write_fill_color_appearance(ctx, annot, buf))
1368 fz_append_string(ctx, buf, "1 g\n");
1369
1370 fz_append_string(ctx, buf, "1 w\n0.5 0.5 15 15 re\nb\n");
1371 fz_append_string(ctx, buf, "1 0 0 -1 4 12 cm\n");
1372
1373 if (pdf_is_dark_fill_color(ctx, annot))
1374 fz_append_string(ctx, buf, "1 g\n");
1375 else
1376 fz_append_string(ctx, buf, "0 g\n");
1377
1378 name = pdf_annot_icon_name(ctx, annot);
1379
1380 /* Text names */
1381 if (!strcmp(name, "Comment"))
1382 fz_append_string(ctx, buf, icon_comment);
1383 else if (!strcmp(name, "Key"))
1384 fz_append_string(ctx, buf, icon_key);
1385 else if (!strcmp(name, "Note"))
1386 fz_append_string(ctx, buf, icon_note);
1387 else if (!strcmp(name, "Help"))
1388 fz_append_string(ctx, buf, icon_help);
1389 else if (!strcmp(name, "NewParagraph"))
1390 fz_append_string(ctx, buf, icon_new_paragraph);
1391 else if (!strcmp(name, "Paragraph"))
1392 fz_append_string(ctx, buf, icon_paragraph);
1393 else if (!strcmp(name, "Insert"))
1394 fz_append_string(ctx, buf, icon_insert);
1395
1396 /* FileAttachment names */
1397 else if (!strcmp(name, "Graph"))
1398 fz_append_string(ctx, buf, icon_graph);
1399 else if (!strcmp(name, "PushPin"))
1400 fz_append_string(ctx, buf, icon_push_pin);
1401 else if (!strcmp(name, "Paperclip"))
1402 fz_append_string(ctx, buf, icon_paperclip);
1403 else if (!strcmp(name, "Tag"))
1404 fz_append_string(ctx, buf, icon_tag);
1405
1406 /* Sound names */
1407 else if (!strcmp(name, "Speaker"))
1408 fz_append_string(ctx, buf, icon_speaker);
1409 else if (!strcmp(name, "Mic"))
1410 fz_append_string(ctx, buf, icon_mic);
1411
1412 /* Unknown */
1413 else
1414 fz_append_string(ctx, buf, icon_star);
1415
1416 *rect = fz_make_rect(xc - 9, yc - 9, xc + 9, yc + 9);
1417 *bbox = fz_make_rect(0, 0, 16, 16);
1418 }
1419
1420 static float
1421 measure_stamp_string(fz_context *ctx, fz_font *font, const char *text)
1422 {
1423 float w = 0;
1424 while (*text)
1425 {
1426 int c, g;
1427 text += fz_chartorune(&c, text);
1428 if (fz_windows_1252_from_unicode(c) < 0)
1429 c = REPLACEMENT;
1430 g = fz_encode_character(ctx, font, c);
1431 w += fz_advance_glyph(ctx, font, g, 0);
1432 }
1433 return w;
1434 }
1435
1436 static void
1437 write_stamp_string(fz_context *ctx, fz_buffer *buf, fz_font *font, const char *text)
1438 {
1439 fz_append_byte(ctx, buf, '(');
1440 while (*text)
1441 {
1442 int c;
1443 text += fz_chartorune(&c, text);
1444 c = fz_windows_1252_from_unicode(c);
1445 if (c < 0) c = REPLACEMENT;
1446 if (c == '(' || c == ')' || c == '\\')
1447 fz_append_byte(ctx, buf, '\\');
1448 fz_append_byte(ctx, buf, c);
1449 }
1450 fz_append_byte(ctx, buf, ')');
1451 }
1452
1453 static void
1454 write_stamp(fz_context *ctx, fz_buffer *buf, fz_font *font, const char *text, float y, float h)
1455 {
1456 float tw = measure_stamp_string(ctx, font, text) * h;
1457 fz_append_string(ctx, buf, "BT\n");
1458 fz_append_printf(ctx, buf, "/Times %g Tf\n", h);
1459 fz_append_printf(ctx, buf, "%g %g Td\n", (190-tw)/2, y);
1460 write_stamp_string(ctx, buf, font, text);
1461 fz_append_string(ctx, buf, " Tj\n");
1462 fz_append_string(ctx, buf, "ET\n");
1463 }
1464
1465 static void
1466 pdf_write_stamp_appearance_rubber(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, fz_rect *bbox, fz_matrix *matrix, pdf_obj **res)
1467 {
1468 fz_font *font;
1469 pdf_obj *res_font;
1470 pdf_obj *name;
1471 float w, h, xs, ys;
1472 fz_matrix rotate;
1473
1474 name = pdf_dict_get(ctx, annot->obj, PDF_NAME(Name));
1475 if (!name)
1476 name = PDF_NAME(Draft);
1477
1478 h = rect->y1 - rect->y0;
1479 w = rect->x1 - rect->x0;
1480 xs = w / 190;
1481 ys = h / 50;
1482
1483 font = fz_new_base14_font(ctx, "Times-Bold");
1484 fz_try(ctx)
1485 {
1486 /* /Resources << /Font << /Times %d 0 R >> >> */
1487 if (!*res)
1488 *res = pdf_new_dict(ctx, annot->page->doc, 1);
1489 res_font = pdf_dict_put_dict(ctx, *res, PDF_NAME(Font), 1);
1490 pdf_dict_put_drop(ctx, res_font, PDF_NAME(Times), pdf_add_simple_font(ctx, annot->page->doc, font, 0));
1491
1492 pdf_write_opacity(ctx, annot, buf, res);
1493 pdf_write_fill_color_appearance(ctx, annot, buf);
1494 pdf_write_stroke_color_appearance(ctx, annot, buf);
1495 rotate = fz_rotate(0.6f);
1496 fz_append_printf(ctx, buf, "%M cm\n", &rotate);
1497 fz_append_string(ctx, buf, "2 w\n2 2 186 44 re\nS\n");
1498
1499 if (name == PDF_NAME(Approved))
1500 write_stamp(ctx, buf, font, "APPROVED", 13, 30);
1501 else if (name == PDF_NAME(AsIs))
1502 write_stamp(ctx, buf, font, "AS IS", 13, 30);
1503 else if (name == PDF_NAME(Confidential))
1504 write_stamp(ctx, buf, font, "CONFIDENTIAL", 17, 20);
1505 else if (name == PDF_NAME(Departmental))
1506 write_stamp(ctx, buf, font, "DEPARTMENTAL", 17, 20);
1507 else if (name == PDF_NAME(Experimental))
1508 write_stamp(ctx, buf, font, "EXPERIMENTAL", 17, 20);
1509 else if (name == PDF_NAME(Expired))
1510 write_stamp(ctx, buf, font, "EXPIRED", 13, 30);
1511 else if (name == PDF_NAME(Final))
1512 write_stamp(ctx, buf, font, "FINAL", 13, 30);
1513 else if (name == PDF_NAME(ForComment))
1514 write_stamp(ctx, buf, font, "FOR COMMENT", 17, 20);
1515 else if (name == PDF_NAME(ForPublicRelease))
1516 {
1517 write_stamp(ctx, buf, font, "FOR PUBLIC", 26, 18);
1518 write_stamp(ctx, buf, font, "RELEASE", 8.5f, 18);
1519 }
1520 else if (name == PDF_NAME(NotApproved))
1521 write_stamp(ctx, buf, font, "NOT APPROVED", 17, 20);
1522 else if (name == PDF_NAME(NotForPublicRelease))
1523 {
1524 write_stamp(ctx, buf, font, "NOT FOR", 26, 18);
1525 write_stamp(ctx, buf, font, "PUBLIC RELEASE", 8.5, 18);
1526 }
1527 else if (name == PDF_NAME(Sold))
1528 write_stamp(ctx, buf, font, "SOLD", 13, 30);
1529 else if (name == PDF_NAME(TopSecret))
1530 write_stamp(ctx, buf, font, "TOP SECRET", 14, 26);
1531 else if (name == PDF_NAME(Draft))
1532 write_stamp(ctx, buf, font, "DRAFT", 13, 30);
1533 else
1534 write_stamp(ctx, buf, font, pdf_to_name(ctx, name), 17, 20);
1535 }
1536 fz_always(ctx)
1537 fz_drop_font(ctx, font);
1538 fz_catch(ctx)
1539 fz_rethrow(ctx);
1540
1541 *bbox = fz_make_rect(0, 0, 190, 50);
1542 if (xs > ys)
1543 {
1544 float xc = (rect->x1+rect->x0) / 2;
1545 rect->x0 = xc - 95 * ys;
1546 rect->x1 = xc + 95 * ys;
1547 }
1548 else
1549 {
1550 float yc = (rect->y1+rect->y0) / 2;
1551 rect->y0 = yc - 25 * xs;
1552 rect->y1 = yc + 25 * xs;
1553 }
1554
1555 *matrix = fz_identity;
1556 }
1557
1558 static void
1559 pdf_write_stamp_appearance_image(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, fz_rect *bbox, fz_matrix *matrix, pdf_obj **res, pdf_obj *img)
1560 {
1561 pdf_obj *res_xobj;
1562
1563 if (!*res)
1564 *res = pdf_new_dict(ctx, annot->page->doc, 1);
1565 res_xobj = pdf_dict_put_dict(ctx, *res, PDF_NAME(XObject), 1);
1566 pdf_dict_put(ctx, res_xobj, PDF_NAME(I), img);
1567
1568 pdf_write_opacity(ctx, annot, buf, res);
1569
1570 fz_append_string(ctx, buf, "/I Do\n");
1571
1572 *bbox = fz_unit_rect;
1573 *matrix = fz_identity;
1574 }
1575
1576 static void
1577 pdf_write_stamp_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, fz_rect *bbox, fz_matrix *matrix, pdf_obj **res)
1578 {
1579 pdf_obj *img = pdf_annot_stamp_image_obj(ctx, annot);
1580 if (img)
1581 pdf_write_stamp_appearance_image(ctx, annot, buf, rect, bbox, matrix, res, img);
1582 else
1583 pdf_write_stamp_appearance_rubber(ctx, annot, buf, rect, bbox, matrix, res);
1584 }
1585
1586 static void
1587 add_required_fonts(fz_context *ctx, pdf_document *doc, pdf_obj *res_font,
1588 fz_text_language lang, fz_font *font, const char *fontname, const char *text)
1589 {
1590 fz_font *cjk_font;
1591 char buf[40];
1592
1593 int add_latin = 0;
1594 int add_greek = 0;
1595 int add_cyrillic = 0;
1596 int add_korean = 0;
1597 int add_japanese = 0;
1598 int add_bopomofo = 0;
1599 int add_han = 0;
1600 int add_hans = 0;
1601 int add_hant = 0;
1602
1603 while (*text)
1604 {
1605 int c;
1606 text += fz_chartorune(&c, text);
1607 switch (ucdn_get_script(c))
1608 {
1609 default: add_latin = 1; /* for fallback bullet character */ break;
1610 case UCDN_SCRIPT_COMMON: break;
1611 case UCDN_SCRIPT_INHERITED: break;
1612 case UCDN_SCRIPT_LATIN: add_latin = 1; break;
1613 case UCDN_SCRIPT_GREEK: add_greek = 1; break;
1614 case UCDN_SCRIPT_CYRILLIC: add_cyrillic = 1; break;
1615 case UCDN_SCRIPT_HANGUL: add_korean = 1; break;
1616 case UCDN_SCRIPT_HIRAGANA: add_japanese = 1; break;
1617 case UCDN_SCRIPT_KATAKANA: add_japanese = 1; break;
1618 case UCDN_SCRIPT_BOPOMOFO: add_bopomofo = 1; break;
1619 case UCDN_SCRIPT_HAN: add_han = 1; break;
1620 }
1621 }
1622
1623 if (add_han)
1624 {
1625 switch (lang)
1626 {
1627 case FZ_LANG_ko: add_korean = 1; break;
1628 default: /* fall through */
1629 case FZ_LANG_ja: add_japanese = 1; break;
1630 case FZ_LANG_zh: /* fall through */
1631 case FZ_LANG_zh_Hant: add_hant = 1; break;
1632 case FZ_LANG_zh_Hans: add_hans = 1; break;
1633 }
1634 }
1635
1636 if (add_bopomofo)
1637 {
1638 if (lang == FZ_LANG_zh_Hans)
1639 add_hans = 1;
1640 else
1641 add_hant = 1;
1642 }
1643
1644 if (!add_greek && !add_cyrillic && !add_korean && !add_japanese && !add_hant && !add_hans)
1645 add_latin = 1;
1646
1647 if (add_latin)
1648 {
1649 if (!pdf_dict_gets(ctx, res_font, fontname))
1650 pdf_dict_puts_drop(ctx, res_font, fontname,
1651 pdf_add_simple_font(ctx, doc, font, PDF_SIMPLE_ENCODING_LATIN));
1652 }
1653 if (add_greek)
1654 {
1655 fz_snprintf(buf, sizeof buf, "%sGRK", fontname);
1656 if (!pdf_dict_gets(ctx, res_font, buf))
1657 pdf_dict_puts_drop(ctx, res_font, buf,
1658 pdf_add_simple_font(ctx, doc, font, PDF_SIMPLE_ENCODING_GREEK));
1659 }
1660 if (add_cyrillic)
1661 {
1662 fz_snprintf(buf, sizeof buf, "%sCYR", fontname);
1663 if (!pdf_dict_gets(ctx, res_font, buf))
1664 pdf_dict_puts_drop(ctx, res_font, buf,
1665 pdf_add_simple_font(ctx, doc, font, PDF_SIMPLE_ENCODING_CYRILLIC));
1666 }
1667 if (add_korean && !pdf_dict_gets(ctx, res_font, "Batang"))
1668 {
1669 cjk_font = fz_new_cjk_font(ctx, FZ_ADOBE_KOREA);
1670 pdf_dict_puts_drop(ctx, res_font, "Batang",
1671 pdf_add_cjk_font(ctx, doc, font, FZ_ADOBE_KOREA, 0, 1));
1672 fz_drop_font(ctx, cjk_font);
1673 }
1674 if (add_japanese && !pdf_dict_gets(ctx, res_font, "Mincho"))
1675 {
1676 cjk_font = fz_new_cjk_font(ctx, FZ_ADOBE_JAPAN);
1677 pdf_dict_puts_drop(ctx, res_font, "Mincho",
1678 pdf_add_cjk_font(ctx, doc, font, FZ_ADOBE_JAPAN, 0, 1));
1679 fz_drop_font(ctx, cjk_font);
1680 }
1681 if (add_hant && !pdf_dict_gets(ctx, res_font, "Ming"))
1682 {
1683 cjk_font = fz_new_cjk_font(ctx, FZ_ADOBE_CNS);
1684 pdf_dict_puts_drop(ctx, res_font, "Ming",
1685 pdf_add_cjk_font(ctx, doc, font, FZ_ADOBE_CNS, 0, 1));
1686 fz_drop_font(ctx, cjk_font);
1687 }
1688 if (add_hans && !pdf_dict_gets(ctx, res_font, "Song"))
1689 {
1690 cjk_font = fz_new_cjk_font(ctx, FZ_ADOBE_GB);
1691 pdf_dict_puts_drop(ctx, res_font, "Song",
1692 pdf_add_cjk_font(ctx, doc, font, FZ_ADOBE_GB, 0, 1));
1693 fz_drop_font(ctx, cjk_font);
1694 }
1695 }
1696
1697 static int find_initial_script(const char *text)
1698 {
1699 int script = UCDN_SCRIPT_COMMON;
1700 int c;
1701 while (*text)
1702 {
1703 text += fz_chartorune(&c, text);
1704 script = ucdn_get_script(c);
1705 if (script != UCDN_SCRIPT_COMMON && script != UCDN_SCRIPT_INHERITED)
1706 break;
1707 }
1708 if (script == UCDN_SCRIPT_COMMON || script == UCDN_SCRIPT_INHERITED)
1709 script = UCDN_SCRIPT_LATIN;
1710 return script;
1711 }
1712
1713 enum { ENC_LATIN = 1, ENC_GREEK, ENC_CYRILLIC, ENC_KOREAN, ENC_JAPANESE, ENC_HANT, ENC_HANS };
1714
1715 struct text_walk_state
1716 {
1717 const char *text, *end;
1718 fz_font *font;
1719 fz_text_language lang;
1720 int enc, u, c, n, last_script;
1721 float w;
1722 };
1723
1724 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)
1725 {
1726 state->text = text;
1727 state->end = end ? end : text + strlen(text);
1728 state->lang = lang;
1729 state->font = font;
1730 state->last_script = find_initial_script(text);
1731 state->n = 0;
1732 }
1733
1734 static int next_text_walk(fz_context *ctx, struct text_walk_state *state)
1735 {
1736 int script, g;
1737
1738 state->text += state->n;
1739 if (state->text >= state->end)
1740 {
1741 state->n = 0;
1742 return 0;
1743 }
1744
1745 state->n = fz_chartorune(&state->u, state->text);
1746 script = ucdn_get_script(state->u);
1747 if (script == UCDN_SCRIPT_COMMON || script == UCDN_SCRIPT_INHERITED)
1748 script = state->last_script;
1749 state->last_script = script;
1750
1751 switch (script)
1752 {
1753 default:
1754 state->enc = ENC_LATIN;
1755 state->c = REPLACEMENT;
1756 break;
1757 case UCDN_SCRIPT_LATIN:
1758 state->enc = ENC_LATIN;
1759 state->c = fz_windows_1252_from_unicode(state->u);
1760 break;
1761 case UCDN_SCRIPT_GREEK:
1762 state->enc = ENC_GREEK;
1763 state->c = fz_iso8859_7_from_unicode(state->u);
1764 break;
1765 case UCDN_SCRIPT_CYRILLIC:
1766 state->enc = ENC_CYRILLIC;
1767 state->c = fz_koi8u_from_unicode(state->u);
1768 break;
1769 case UCDN_SCRIPT_HANGUL:
1770 state->enc = ENC_KOREAN;
1771 state->c = state->u;
1772 break;
1773 case UCDN_SCRIPT_HIRAGANA:
1774 case UCDN_SCRIPT_KATAKANA:
1775 state->enc = ENC_JAPANESE;
1776 state->c = state->u;
1777 break;
1778 case UCDN_SCRIPT_BOPOMOFO:
1779 state->enc = (state->lang == FZ_LANG_zh_Hans) ? ENC_HANS : ENC_HANT;
1780 state->c = state->u;
1781 break;
1782 case UCDN_SCRIPT_HAN:
1783 switch (state->lang)
1784 {
1785 case FZ_LANG_ko: state->enc = ENC_KOREAN; break;
1786 default: /* fall through */
1787 case FZ_LANG_ja: state->enc = ENC_JAPANESE; break;
1788 case FZ_LANG_zh: /* fall through */
1789 case FZ_LANG_zh_Hant: state->enc = ENC_HANT; break;
1790 case FZ_LANG_zh_Hans: state->enc = ENC_HANS; break;
1791 }
1792 state->c = state->u;
1793 break;
1794 }
1795
1796 /* TODO: check that character is encodable with ENC_KOREAN/etc */
1797 if (state->c < 0)
1798 {
1799 state->enc = ENC_LATIN;
1800 state->c = REPLACEMENT;
1801 }
1802
1803 if (state->enc >= ENC_KOREAN)
1804 {
1805 state->w = 1;
1806 }
1807 else
1808 {
1809 if (state->font != NULL)
1810 {
1811 g = fz_encode_character(ctx, state->font, state->u);
1812 state->w = fz_advance_glyph(ctx, state->font, g, 0);
1813 }
1814 }
1815
1816 return 1;
1817 }
1818
1819 static float
1820 measure_string(fz_context *ctx, fz_text_language lang, fz_font *font, const char *a)
1821 {
1822 struct text_walk_state state;
1823 float w = 0;
1824 init_text_walk(ctx, &state, lang, font, a, NULL);
1825 while (next_text_walk(ctx, &state))
1826 w += state.w;
1827 return w;
1828 }
1829
1830
1831 static float
1832 break_string(fz_context *ctx, fz_text_language lang, fz_font *font, float size, const char *text, const char **endp, float maxw)
1833 {
1834 struct text_walk_state state;
1835 const char *space = NULL;
1836 float space_x, x = 0;
1837 init_text_walk(ctx, &state, lang, font, text, NULL);
1838 while (next_text_walk(ctx, &state))
1839 {
1840 if (state.u == '\n' || state.u == '\r')
1841 break;
1842 if (state.u == ' ')
1843 {
1844 space = state.text + state.n;
1845 space_x = x;
1846 }
1847 x += state.w * size;
1848 if (space && x > maxw)
1849 return *endp = space, space_x;
1850 }
1851 return *endp = state.text + state.n, x;
1852 }
1853
1854 static void
1855 write_string(fz_context *ctx, fz_buffer *buf,
1856 fz_text_language lang, fz_font *font, const char *fontname, float size, const char *text, const char *end)
1857 {
1858 struct text_walk_state state;
1859 int last_enc = 0;
1860 init_text_walk(ctx, &state, lang, font, text, end);
1861 while (next_text_walk(ctx, &state))
1862 {
1863 if (state.enc != last_enc)
1864 {
1865 if (last_enc)
1866 {
1867 if (last_enc < ENC_KOREAN)
1868 fz_append_byte(ctx, buf, ')');
1869 else
1870 fz_append_byte(ctx, buf, '>');
1871 fz_append_string(ctx, buf, " Tj\n");
1872 }
1873
1874 switch (state.enc)
1875 {
1876 case ENC_LATIN: fz_append_printf(ctx, buf, "/%s %g Tf\n", fontname, size); break;
1877 case ENC_GREEK: fz_append_printf(ctx, buf, "/%sGRK %g Tf\n", fontname, size); break;
1878 case ENC_CYRILLIC: fz_append_printf(ctx, buf, "/%sCYR %g Tf\n", fontname, size); break;
1879 case ENC_KOREAN: fz_append_printf(ctx, buf, "/Batang %g Tf\n", size); break;
1880 case ENC_JAPANESE: fz_append_printf(ctx, buf, "/Mincho %g Tf\n", size); break;
1881 case ENC_HANT: fz_append_printf(ctx, buf, "/Ming %g Tf\n", size); break;
1882 case ENC_HANS: fz_append_printf(ctx, buf, "/Song %g Tf\n", size); break;
1883 }
1884
1885 if (state.enc < ENC_KOREAN)
1886 fz_append_byte(ctx, buf, '(');
1887 else
1888 fz_append_byte(ctx, buf, '<');
1889
1890 last_enc = state.enc;
1891 }
1892
1893 if (state.enc < ENC_KOREAN)
1894 {
1895 if (state.c == '(' || state.c == ')' || state.c == '\\')
1896 fz_append_byte(ctx, buf, '\\');
1897 fz_append_byte(ctx, buf, state.c);
1898 }
1899 else
1900 {
1901 fz_append_printf(ctx, buf, "%04x", state.c);
1902 }
1903 }
1904
1905 if (last_enc)
1906 {
1907 if (last_enc < ENC_KOREAN)
1908 fz_append_byte(ctx, buf, ')');
1909 else
1910 fz_append_byte(ctx, buf, '>');
1911 fz_append_string(ctx, buf, " Tj\n");
1912 }
1913 }
1914
1915 static void
1916 write_string_with_quadding(fz_context *ctx, fz_buffer *buf,
1917 fz_text_language lang, const char *fontname,
1918 fz_font *font, float size, float lineheight,
1919 const char *a, float maxw, int q)
1920 {
1921 const char *b;
1922 float px = 0, x = 0, w;
1923 while (*a)
1924 {
1925 w = break_string(ctx, lang, font, size, a, &b, maxw);
1926 if (b > a)
1927 {
1928 if (q == 0)
1929 x = 0;
1930 else if (q == 1)
1931 x = (maxw - w) / 2;
1932 else
1933 x = (maxw - w);
1934 fz_append_printf(ctx, buf, "%g %g Td\n", x - px, -lineheight);
1935 if (b[-1] == '\n' || b[-1] == '\r')
1936 write_string(ctx, buf, lang, font, fontname, size, a, b-1);
1937 else
1938 write_string(ctx, buf, lang, font, fontname, size, a, b);
1939 a = b;
1940 px = x;
1941 }
1942 }
1943 }
1944
1945 static void
1946 write_comb_string(fz_context *ctx, fz_buffer *buf,
1947 fz_text_language lang, const char *fontname,
1948 fz_font *font, float size, const char *text, float cell_w)
1949 {
1950 struct text_walk_state state;
1951 int last_enc = 0;
1952 float pad, carry = 0;
1953
1954 init_text_walk(ctx, &state, lang, font, text, text + strlen(text));
1955
1956 while (next_text_walk(ctx, &state))
1957 {
1958 if (state.enc != last_enc)
1959 {
1960 if (last_enc)
1961 fz_append_string(ctx, buf, "] TJ\n");
1962
1963 switch (state.enc)
1964 {
1965 case ENC_LATIN: fz_append_printf(ctx, buf, "/%s %g Tf\n", fontname, size); break;
1966 case ENC_GREEK: fz_append_printf(ctx, buf, "/%sGRK %g Tf\n", fontname, size); break;
1967 case ENC_CYRILLIC: fz_append_printf(ctx, buf, "/%sCYR %g Tf\n", fontname, size); break;
1968 case ENC_KOREAN: fz_append_printf(ctx, buf, "/Batang %g Tf\n", size); break;
1969 case ENC_JAPANESE: fz_append_printf(ctx, buf, "/Mincho %g Tf\n", size); break;
1970 case ENC_HANT: fz_append_printf(ctx, buf, "/Ming %g Tf\n", size); break;
1971 case ENC_HANS: fz_append_printf(ctx, buf, "/Song %g Tf\n", size); break;
1972 }
1973
1974 fz_append_byte(ctx, buf, '[');
1975
1976 last_enc = state.enc;
1977 }
1978
1979 pad = (cell_w - state.w * 1000) / 2;
1980 fz_append_printf(ctx, buf, "%g", -(carry + pad));
1981 carry = pad;
1982
1983 if (state.enc < ENC_KOREAN)
1984 {
1985 fz_append_byte(ctx, buf, '(');
1986 if (state.c == '(' || state.c == ')' || state.c == '\\')
1987 fz_append_byte(ctx, buf, '\\');
1988 fz_append_byte(ctx, buf, state.c);
1989 fz_append_byte(ctx, buf, ')');
1990 }
1991 else
1992 {
1993 fz_append_printf(ctx, buf, "<%04x>", state.c);
1994 }
1995 }
1996 if (last_enc)
1997 fz_append_string(ctx, buf, "] TJ\n");
1998 }
1999
2000 static void
2001 layout_comb_string(fz_context *ctx, fz_layout_block *out, float x, float y,
2002 const char *a, const char *b, fz_font *font, float size, float cell_w)
2003 {
2004 int n, c, g;
2005 int first = 1;
2006 float w;
2007 if (a == b)
2008 fz_add_layout_line(ctx, out, x + cell_w / 2, y, size, a);
2009 while (a < b)
2010 {
2011 n = fz_chartorune(&c, a);
2012 c = fz_windows_1252_from_unicode(c);
2013 if (c < 0) c = REPLACEMENT;
2014 g = fz_encode_character(ctx, font, c);
2015 w = fz_advance_glyph(ctx, font, g, 0) * size;
2016 if (first)
2017 {
2018 fz_add_layout_line(ctx, out, x + (cell_w - w) / 2, y, size, a);
2019 first = 0;
2020 }
2021 fz_add_layout_char(ctx, out, x + (cell_w - w) / 2, w, a);
2022 a += n;
2023 x += cell_w;
2024 }
2025 }
2026
2027 static void
2028 layout_string(fz_context *ctx, fz_layout_block *out,
2029 fz_text_language lang, fz_font *font, float size,
2030 float x, float y, const char *a, const char *b)
2031 {
2032 struct text_walk_state state;
2033 fz_add_layout_line(ctx, out, x, y, size, a);
2034 init_text_walk(ctx, &state, lang, font, a, b);
2035 while (next_text_walk(ctx, &state))
2036 {
2037 fz_add_layout_char(ctx, out, x, state.w * size, state.text);
2038 x += state.w * size;
2039 }
2040 }
2041
2042 static void
2043 layout_string_with_quadding(fz_context *ctx, fz_layout_block *out,
2044 fz_text_language lang, fz_font *font, float size, float lineheight,
2045 float xorig, float y, const char *a, float maxw, int q)
2046 {
2047 const char *b;
2048 float x = 0, w;
2049 int add_line_at_end = 0;
2050
2051 if (!*a)
2052 add_line_at_end = 1;
2053
2054 while (*a)
2055 {
2056 w = break_string(ctx, lang, font, size, a, &b, maxw);
2057 if (b > a)
2058 {
2059 if (q > 0)
2060 {
2061 if (q == 1)
2062 x = (maxw - w) / 2;
2063 else
2064 x = (maxw - w);
2065 }
2066 if (b[-1] == '\n' || b[-1] == '\r')
2067 {
2068 layout_string(ctx, out, lang, font, size, xorig+x, y, a, b-1);
2069 add_line_at_end = 1;
2070 }
2071 else
2072 {
2073 layout_string(ctx, out, lang, font, size, xorig+x, y, a, b);
2074 add_line_at_end = 0;
2075 }
2076 a = b;
2077 y -= lineheight;
2078 }
2079 }
2080 if (add_line_at_end)
2081 fz_add_layout_line(ctx, out, xorig, y, size, a);
2082 }
2083
2084 static const char *full_font_name(const char **name)
2085 {
2086 if (!strcmp(*name, "Cour")) return "Courier";
2087 if (!strcmp(*name, "Helv")) return "Helvetica";
2088 if (!strcmp(*name, "TiRo")) return "Times-Roman";
2089 if (!strcmp(*name, "Symb")) return "Symbol";
2090 if (!strcmp(*name, "ZaDb")) return "ZapfDingbats";
2091 return *name = "Helv", "Helvetica";
2092 }
2093
2094 static void
2095 write_variable_text(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, pdf_obj **res,
2096 fz_text_language lang, const char *text,
2097 const char *fontname, float size, int n, float *color, int q,
2098 float w, float h, float padding, float baseline, float lineheight,
2099 int multiline, int comb, int adjust_baseline)
2100 {
2101 fz_font *font;
2102 pdf_obj *res_font;
2103
2104 w -= padding * 2;
2105 h -= padding * 2;
2106
2107 font = fz_new_base14_font(ctx, full_font_name(&fontname));
2108 fz_try(ctx)
2109 {
2110 if (!*res)
2111 *res = pdf_new_dict(ctx, annot->page->doc, 1);
2112 res_font = pdf_dict_put_dict(ctx, *res, PDF_NAME(Font), 1);
2113 add_required_fonts(ctx, annot->page->doc, res_font, lang, font, fontname, text);
2114
2115 if (size == 0)
2116 {
2117 if (multiline)
2118 size = 12;
2119 else
2120 {
2121 size = w / measure_string(ctx, lang, font, text);
2122 if (size > h)
2123 size = h;
2124 }
2125 }
2126
2127 lineheight = size * lineheight;
2128 baseline = size * baseline;
2129
2130 if (adjust_baseline)
2131 {
2132 /* Make sure baseline is inside rectangle */
2133 if (baseline + 0.2f * size > h)
2134 baseline = h - 0.2f * size;
2135 }
2136
2137 fz_append_string(ctx, buf, "BT\n");
2138 write_color0(ctx, buf, n, color, 0);
2139 if (multiline)
2140 {
2141 fz_append_printf(ctx, buf, "%g %g Td\n", padding, padding+h-baseline+lineheight);
2142 write_string_with_quadding(ctx, buf, lang, fontname, font, size, lineheight, text, w, q);
2143 }
2144 else if (comb > 0)
2145 {
2146 float ty = (h - size) / 2;
2147 fz_append_printf(ctx, buf, "%g %g Td\n", padding, padding+h-baseline-ty);
2148 write_comb_string(ctx, buf, lang, fontname, font, size, text, (w * 1000 / size) / comb);
2149 }
2150 else
2151 {
2152 float tx = 0, ty = (h - size) / 2;
2153 if (q > 0)
2154 {
2155 float tw = measure_string(ctx, lang, font, text) * size;
2156 if (q == 1)
2157 tx = (w - tw) / 2;
2158 else
2159 tx = (w - tw);
2160 }
2161 fz_append_printf(ctx, buf, "%g %g Td\n", padding+tx, padding+h-baseline-ty);
2162 write_string(ctx, buf, lang, font, fontname, size, text, text + strlen(text));
2163 }
2164 fz_append_string(ctx, buf, "ET\n");
2165 }
2166 fz_always(ctx)
2167 fz_drop_font(ctx, font);
2168 fz_catch(ctx)
2169 fz_rethrow(ctx);
2170 }
2171
2172 static void
2173 layout_variable_text(fz_context *ctx, fz_layout_block *out,
2174 const char *text, fz_text_language lang, const char *fontname, float size, int q,
2175 float x, float y, float w, float h, float padding, float baseline, float lineheight,
2176 int multiline, int comb, int adjust_baseline)
2177 {
2178 fz_font *font;
2179
2180 w -= padding * 2;
2181 h -= padding * 2;
2182
2183 font = fz_new_base14_font(ctx, full_font_name(&fontname));
2184 fz_try(ctx)
2185 {
2186 if (size == 0)
2187 {
2188 if (multiline)
2189 size = 12;
2190 else
2191 {
2192 size = w / measure_string(ctx, lang, font, text);
2193 if (size > h)
2194 size = h;
2195 }
2196 }
2197
2198 lineheight = size * lineheight;
2199 baseline = size * baseline;
2200
2201 if (adjust_baseline)
2202 {
2203 /* Make sure baseline is inside rectangle */
2204 if (baseline + 0.2f * size > h)
2205 baseline = h - 0.2f * size;
2206 }
2207
2208 if (multiline)
2209 {
2210 x += padding;
2211 y += padding + h - baseline;
2212 layout_string_with_quadding(ctx, out, lang, font, size, lineheight, x, y, text, w, q);
2213 }
2214 else if (comb > 0)
2215 {
2216 float ty = (h - size) / 2;
2217 x += padding;
2218 y += padding + h - baseline - ty;
2219 layout_comb_string(ctx, out, x, y, text, text + strlen(text), font, size, w / comb);
2220 }
2221 else
2222 {
2223 float tx = 0, ty = (h - size) / 2;
2224 if (q > 0)
2225 {
2226 float tw = measure_string(ctx, lang, font, text) * size;
2227 if (q == 1)
2228 tx = (w - tw) / 2;
2229 else
2230 tx = (w - tw);
2231 }
2232 x += padding + tx;
2233 y += padding + h - baseline - ty;
2234 layout_string(ctx, out, lang, font, size, x, y, text, text + strlen(text));
2235 }
2236 }
2237 fz_always(ctx)
2238 fz_drop_font(ctx, font);
2239 fz_catch(ctx)
2240 fz_rethrow(ctx);
2241 }
2242
2243 #if FZ_ENABLE_HTML_ENGINE
2244 static void
2245 write_rich_content(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, pdf_obj **res, const char *rc, const char *ds, float size, float w, float h, float b, int multiline)
2246 {
2247 /* border_box is the actual rectangle that we are writing into.
2248 * We use this to feed to the pdfwriting device. */
2249 fz_rect border_box = { 0, 0, w, h };
2250
2251 /* content_box is adjusted for padding and has added height.
2252 * We know a clipping rectangle will have been set to the proper rectangle
2253 * so we can allow text to flow out the bottom of the rectangle rather than
2254 * just missing it out. This matches adobe. */
2255 fz_rect content_box = fz_make_rect(b, b, w - b*2, h + size * 2);
2256
2257 fz_buffer *inbuf = fz_new_buffer_from_copied_data(ctx, (const unsigned char *)rc, strlen(rc)+1);
2258 fz_story *story = NULL;
2259 fz_device *dev = NULL;
2260 fz_buffer *buf2 = NULL;
2261 const char *default_css = "@page{margin:0} body{margin:0;line-height:1.2;white-space:pre-wrap;} p{margin:0}";
2262 char *css = NULL;
2263
2264 fz_var(story);
2265 fz_var(dev);
2266 fz_var(res);
2267 fz_var(buf2);
2268 fz_var(css);
2269
2270 // single-line should be centered in the box. adjust content box accordingly.
2271 // this matches the math in write_variable_text.
2272 if (!multiline)
2273 {
2274 float ty = ((h - b * 2) - size) / 2;
2275 content_box.y0 = h - b - 0.8f * size + ty;
2276 content_box.y1 = content_box.y0 + size * 2;
2277 }
2278
2279 fz_try(ctx)
2280 {
2281 if (ds)
2282 css = fz_asprintf(ctx, "%s body{%s}", default_css, ds);
2283 story = fz_new_story(ctx, inbuf, css ? css : default_css, size, NULL);
2284 dev = pdf_page_write(ctx, annot->page->doc, border_box, res, &buf2);
2285 fz_place_story(ctx, story, content_box, NULL);
2286 fz_draw_story(ctx, story, dev, fz_identity);
2287 fz_close_device(ctx, dev);
2288
2289 fz_append_buffer(ctx, buf, buf2);
2290 }
2291 fz_always(ctx)
2292 {
2293 fz_drop_device(ctx, dev);
2294 fz_drop_buffer(ctx, buf2);
2295 fz_drop_story(ctx, story);
2296 fz_drop_buffer(ctx, inbuf);
2297 fz_free(ctx, css);
2298 }
2299 fz_catch(ctx)
2300 fz_rethrow(ctx);
2301 }
2302 #endif
2303
2304 #if FZ_ENABLE_HTML_ENGINE
2305
2306 static char *
2307 escape_text(fz_context *ctx, const char *s)
2308 {
2309 size_t len = 1;
2310 char c;
2311 const char *s2;
2312 char *d, *d2;
2313
2314 for (s2 = s; (c = *s2++) != 0; len++)
2315 {
2316 if (c == '<')
2317 len += 3; /* &lt; */
2318 else if (c == '>')
2319 len += 3; /* &gt; */
2320 else if (c == '&')
2321 len += 4; /* &amp; */
2322 }
2323
2324 d = d2 = fz_malloc(ctx, len);
2325
2326 for (s2 = s; (c = *s2++) != 0; )
2327 {
2328 if (c == '<')
2329 {
2330 *d++ = '&';
2331 *d++ = 'l';
2332 *d++ = 't';
2333 *d++ = ';';
2334 }
2335 else if (c == '>')
2336 {
2337 *d++ = '&';
2338 *d++ = 'g';
2339 *d++ = 't';
2340 *d++ = ';';
2341 }
2342 else if (c == '&')
2343 {
2344 *d++ = '&';
2345 *d++ = 'a';
2346 *d++ = 'm';
2347 *d++ = 'p';
2348 *d++ = ';';
2349 }
2350 else
2351 *d++ = c;
2352 }
2353 *d++ = 0;
2354
2355 return d2;
2356 }
2357
2358 int text_needs_rich_layout(fz_context *ctx, const char *s)
2359 {
2360 int c, script;
2361 while (*s)
2362 {
2363 s += fz_chartorune(&c, s);
2364
2365 // base 14 fonts
2366 if (fz_windows_1252_from_unicode(c) > 0)
2367 continue;
2368 if (fz_iso8859_7_from_unicode(c) > 0)
2369 continue;
2370 if (fz_koi8u_from_unicode(c) > 0)
2371 continue;
2372
2373 // cjk fonts
2374 script = ucdn_get_script(c);
2375 if (
2376 script == UCDN_SCRIPT_HANGUL ||
2377 script == UCDN_SCRIPT_HIRAGANA ||
2378 script == UCDN_SCRIPT_KATAKANA ||
2379 script == UCDN_SCRIPT_BOPOMOFO ||
2380 script == UCDN_SCRIPT_HAN
2381 )
2382 continue;
2383
2384 return 1;
2385 }
2386 return 0;
2387 }
2388
2389 static unsigned int hex_from_color(fz_context *ctx, int n, float color[4])
2390 {
2391 float rgb[4];
2392 int r, g, b;
2393 switch (n)
2394 {
2395 default:
2396 r = g = b = 0;
2397 break;
2398 case 1:
2399 r = g = b = color[0] * 255;
2400 break;
2401 case 3:
2402 r = color[0] * 255;
2403 g = color[1] * 255;
2404 b = color[2] * 255;
2405 break;
2406 case 4:
2407 fz_convert_color(ctx, fz_device_cmyk(ctx), color, fz_device_rgb(ctx), rgb, NULL, fz_default_color_params);
2408 r = rgb[0] * 255;
2409 g = rgb[1] * 255;
2410 b = rgb[2] * 255;
2411 break;
2412 }
2413 return (r<<16) | (g<<8) | b;
2414 }
2415
2416 #endif
2417
2418 static float
2419 pdf_write_line_caption(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, pdf_obj **res, fz_point a, fz_point b)
2420 {
2421 float dx, dy, line_length;
2422 fz_point co;
2423 float size;
2424 const char *text;
2425 pdf_obj *res_font;
2426 fz_font *font = NULL;
2427 fz_matrix tm;
2428 int lang;
2429 int top;
2430 float tw;
2431
2432 fz_var(font);
2433
2434 fz_try(ctx)
2435 {
2436 // vector of line
2437 dx = b.x - a.x;
2438 dy = b.y - a.y;
2439 line_length = hypotf(dx, dy);
2440 dx /= line_length;
2441 dy /= line_length;
2442
2443 text = pdf_annot_contents(ctx, annot);
2444 lang = pdf_annot_language(ctx, annot);
2445 co = pdf_dict_get_point(ctx, annot->obj, PDF_NAME(CO));
2446
2447 font = fz_new_base14_font(ctx, "Helvetica");
2448 if (!*res)
2449 *res = pdf_new_dict(ctx, annot->page->doc, 1);
2450 res_font = pdf_dict_put_dict(ctx, *res, PDF_NAME(Font), 1);
2451 add_required_fonts(ctx, annot->page->doc, res_font, lang, font, "Helv", text);
2452 size = 12;
2453
2454 tw = measure_string(ctx, lang, font, text) * size;
2455
2456 // don't inline if CP says so
2457 top = 0;
2458 if (pdf_dict_get(ctx, annot->obj, PDF_NAME(CP)) == PDF_NAME(Top))
2459 top = 1;
2460
2461 // don't inline if caption wouldn't fit
2462 if (tw + size > line_length)
2463 top = 1;
2464
2465 tm = fz_rotate(atan2(dy, dx) * 180 / M_PI);
2466 tm.e = (a.x + b.x) / 2 - dx * (tw / 2);
2467 tm.f = (a.y + b.y) / 2 - dy * (tw / 2);
2468
2469 // caption offset
2470 if (co.x || co.y)
2471 {
2472 // don't write text inline
2473 top = 1;
2474
2475 if (co.y < 0)
2476 co.y -= size;
2477
2478 tm.e += co.x * dx - co.y * dy;
2479 tm.f += co.x * dy + co.y * dx;
2480 }
2481 else if (top)
2482 {
2483 tm.e -= dy * size * 0.2f;
2484 tm.f += dx * size * 0.2f;
2485 }
2486 else
2487 {
2488 tm.e += dy * size * 0.3f;
2489 tm.f -= dx * size * 0.3f;
2490 }
2491
2492 fz_append_printf(ctx, buf, "q\n%M cm\n", &tm);
2493 fz_append_string(ctx, buf, "0 g\n"); // Acrobat always draws captions in black
2494 fz_append_printf(ctx, buf, "BT\n");
2495 write_string(ctx, buf, lang, font, "Helv", size, text, text + strlen(text));
2496 fz_append_printf(ctx, buf, "ET\n");
2497 fz_append_printf(ctx, buf, "Q\n");
2498
2499 *rect = fz_include_point_in_rect(*rect, fz_make_point(tm.e - dx * tw/2, tm.f - dy * tw/2));
2500 *rect = fz_include_point_in_rect(*rect, fz_make_point(tm.e + dx * tw/2, tm.f + dy * tw/2));
2501 *rect = fz_expand_rect(*rect, size);
2502 }
2503 fz_always(ctx)
2504 {
2505 fz_drop_font(ctx, font);
2506 }
2507 fz_catch(ctx)
2508 fz_rethrow(ctx);
2509
2510 if (!top)
2511 return (tw + size / 2) / 2;
2512 return 0;
2513 }
2514
2515 static void
2516 pdf_write_free_text_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf,
2517 fz_rect *rect, fz_rect *bbox, fz_matrix *matrix, pdf_obj **res)
2518 {
2519 const char *font;
2520 float size, color[4];
2521 const char *text;
2522 float w, h, b;
2523 int q, r, n;
2524 int lang;
2525 fz_rect rd;
2526 pdf_obj *le;
2527 int ic;
2528 fz_rect text_box;
2529 fz_matrix tfm;
2530 #if FZ_ENABLE_HTML_ENGINE
2531 const char *rc, *ds;
2532 char *free_rc = NULL;
2533 char ds_buf[400];
2534 #endif
2535
2536 text = pdf_annot_contents(ctx, annot);
2537 q = pdf_annot_quadding(ctx, annot);
2538 pdf_annot_default_appearance(ctx, annot, &font, &size, &n, color);
2539 lang = pdf_annot_language(ctx, annot);
2540 rd = pdf_annot_rect_diff(ctx, annot);
2541
2542 /* /Rotate is an undocumented annotation property supported by Adobe.
2543 * When Rotate is used, neither the box, nor the arrow move at all.
2544 * Only the position of the text moves within the box. Thus we don't
2545 * need to adjust rd at all! */
2546 r = pdf_dict_get_int(ctx, annot->obj, PDF_NAME(Rotate));
2547
2548 // Adjust input Rect for RD to get the bounds of the text box area
2549 text_box = *rect;
2550 text_box.x0 += rd.x0;
2551 text_box.y0 += rd.y0;
2552 text_box.x1 -= rd.x1;
2553 text_box.y1 -= rd.y1;
2554
2555 *rect = text_box;
2556
2557 // Size of text box area (including padding and border)
2558 w = text_box.x1 - text_box.x0;
2559 h = text_box.y1 - text_box.y0;
2560
2561 pdf_write_opacity(ctx, annot, buf, res);
2562 pdf_write_dash_pattern(ctx, annot, buf, res);
2563
2564 // Set stroke and fill colors for box and callout line
2565 ic = pdf_write_fill_color_appearance(ctx, annot, buf);
2566 write_color0(ctx, buf, n, color, 1);
2567 b = pdf_write_border_appearance(ctx, annot, buf);
2568
2569 // Draw Callout line
2570 if (pdf_name_eq(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME(IT)), PDF_NAME(FreeTextCallout)))
2571 {
2572 pdf_obj *cl = pdf_dict_get(ctx, annot->obj, PDF_NAME(CL));
2573 int i, len = pdf_array_len(ctx, cl);
2574 if (len == 4 || len == 6)
2575 {
2576 float xy[6];
2577
2578 for (i = 0; i < len; i += 2)
2579 {
2580 float x = xy[i+0] = pdf_array_get_real(ctx, cl, i+0);
2581 float y = xy[i+1] = pdf_array_get_real(ctx, cl, i+1);
2582 if (x < rect->x0) rect->x0 = x;
2583 if (y < rect->y0) rect->y0 = y;
2584 if (x > rect->x1) rect->x1 = x;
2585 if (y > rect->y1) rect->y1 = y;
2586 }
2587
2588 fz_append_printf(ctx, buf, "%g %g m\n", xy[0], xy[1]);
2589 for (i = 2; i < len; i += 2)
2590 fz_append_printf(ctx, buf, "%g %g l\n", xy[i+0], xy[i+1]);
2591 fz_append_printf(ctx, buf, "S\n");
2592
2593 le = pdf_dict_get(ctx, annot->obj, PDF_NAME(LE));
2594 pdf_write_line_cap_appearance(ctx, buf, rect,
2595 xy[0], xy[1],
2596 xy[2] - xy[0], xy[3] - xy[1], b,
2597 1, ic, le);
2598 }
2599 }
2600
2601 // Draw text box background
2602 if (ic)
2603 fz_append_printf(ctx, buf, "%g %g %g %g re\nf\n", text_box.x0, text_box.y0, w, h);
2604
2605 // Draw text box border
2606 if (b > 0)
2607 fz_append_printf(ctx, buf, "%g %g %g %g re\nS\n", text_box.x0 + b/2, text_box.y0 + b/2, w - b, h - b);
2608
2609 // Clip text to box
2610 fz_append_printf(ctx, buf, "%g %g %g %g re\nW\nn\n", text_box.x0 + b, text_box.y0 + b, w - b * 2, h - b * 2);
2611
2612 // Recompute Rect and RD to account for Callout line
2613 rd.x0 = text_box.x0 - rect->x0;
2614 rd.y0 = text_box.y0 - rect->y0;
2615 rd.x1 = rect->x1 - text_box.x1;
2616 rd.y1 = rect->y1 - text_box.y1;
2617
2618 // Compute rotation (and offset) transform for text
2619 if (r == 90 || r == 270)
2620 {
2621 float t = w; w = h; h = t;
2622 }
2623 tfm = fz_rotate(r);
2624 if (r == 270)
2625 tfm.e += 0, tfm.f += w;
2626 else if (r == 90)
2627 tfm.e += h, tfm.f -= 0;
2628 else if (r == 180)
2629 tfm.e += w, tfm.f += h;
2630 tfm.e += text_box.x0;
2631 tfm.f += text_box.y0;
2632 fz_append_printf(ctx, buf, "q\n%g %g %g %g %g %g cm\n", tfm.a, tfm.b, tfm.c, tfm.d, tfm.e, tfm.f);
2633
2634 #if FZ_ENABLE_HTML_ENGINE
2635 ds = pdf_dict_get_text_string_opt(ctx, annot->obj, PDF_NAME(DS));
2636 rc = pdf_dict_get_text_string_opt(ctx, annot->obj, PDF_NAME(RC));
2637 if (!rc && (ds || text_needs_rich_layout(ctx, text)))
2638 {
2639 rc = free_rc = escape_text(ctx, text);
2640 if (!ds)
2641 {
2642 fz_snprintf(ds_buf, sizeof ds_buf,
2643 "font-family:%s;font-size:%gpt;color:#%06x;text-align:%s;",
2644 full_font_name(&font),
2645 size,
2646 hex_from_color(ctx, n, color),
2647 (q == 0 ? "left" : q == 1 ? "center" : "right")
2648 );
2649 ds = ds_buf;
2650 }
2651 }
2652 if (rc)
2653 {
2654 fz_try(ctx)
2655 write_rich_content(ctx, annot, buf, res, rc ? rc : text, ds, size, w, h, b * 2, 1);
2656 fz_always(ctx)
2657 fz_free(ctx, free_rc);
2658 fz_catch(ctx)
2659 fz_rethrow(ctx);
2660 }
2661 else
2662 #endif
2663 {
2664 write_variable_text(ctx, annot, buf, res, lang, text, font, size, n, color, q, w, h, b*2,
2665 0.8f, 1.2f, 1, 0, 0);
2666 }
2667
2668 fz_append_printf(ctx, buf, "Q\n");
2669
2670 pdf_dict_put_rect(ctx, annot->obj, PDF_NAME(RD), rd);
2671
2672 *matrix = fz_identity;
2673 *bbox = *rect;
2674 }
2675
2676 static void
2677 pdf_write_tx_widget_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf,
2678 const fz_rect *rect, fz_rect *bbox, fz_matrix *matrix, pdf_obj **res,
2679 const char *text, int ff)
2680 {
2681 fz_text_language lang;
2682 const char *font;
2683 float size, color[4];
2684 float w, h, t, b;
2685 int has_bc = 0;
2686 int q, r, n;
2687
2688 #if FZ_ENABLE_HTML_ENGINE
2689 const char *rc, *ds;
2690 char *free_rc = NULL;
2691 char ds_buf[400];
2692 #endif
2693
2694 r = pdf_dict_get_int(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME(MK)), PDF_NAME(R));
2695 q = pdf_annot_quadding(ctx, annot);
2696 pdf_annot_default_appearance(ctx, annot, &font, &size, &n, color);
2697 lang = pdf_annot_language(ctx, annot);
2698
2699 w = rect->x1 - rect->x0;
2700 h = rect->y1 - rect->y0;
2701 r = r % 360;
2702 if (r == 90 || r == 270)
2703 t = h, h = w, w = t;
2704 *matrix = fz_rotate(r);
2705 *bbox = fz_make_rect(0, 0, w, h);
2706
2707 fz_append_string(ctx, buf, "/Tx BMC\nq\n");
2708
2709 if (pdf_write_MK_BG_appearance(ctx, annot, buf))
2710 fz_append_printf(ctx, buf, "0 0 %g %g re\nf\n", w, h);
2711
2712 b = pdf_write_border_appearance(ctx, annot, buf);
2713 if (b > 0 && pdf_write_MK_BC_appearance(ctx, annot, buf))
2714 {
2715 fz_append_printf(ctx, buf, "%g %g %g %g re\ns\n", b/2, b/2, w-b, h-b);
2716 has_bc = 1;
2717 }
2718
2719 fz_append_printf(ctx, buf, "%g %g %g %g re\nW\nn\n", b, b, w-b*2, h-b*2);
2720
2721 #if FZ_ENABLE_HTML_ENGINE
2722 ds = pdf_dict_get_text_string_opt(ctx, annot->obj, PDF_NAME(DS));
2723 rc = pdf_dict_get_text_string_opt(ctx, annot->obj, PDF_NAME(RV));
2724 if (!rc && (ds || text_needs_rich_layout(ctx, text)))
2725 {
2726 rc = free_rc = escape_text(ctx, text);
2727 if (!ds)
2728 {
2729 fz_snprintf(ds_buf, sizeof ds_buf,
2730 "font-family:%s;font-size:%gpt;color:#%06x;text-align:%s",
2731 full_font_name(&font),
2732 size,
2733 hex_from_color(ctx, n, color),
2734 (q == 0 ? "left" : q == 1 ? "center" : "right")
2735 );
2736 ds = ds_buf;
2737 }
2738 }
2739 if (rc)
2740 {
2741 fz_try(ctx)
2742 write_rich_content(ctx, annot, buf, res, rc ? rc : text, ds, size, w, h, b * 2,
2743 (ff & PDF_TX_FIELD_IS_MULTILINE));
2744 fz_always(ctx)
2745 fz_free(ctx, free_rc);
2746 fz_catch(ctx)
2747 fz_rethrow(ctx);
2748 }
2749 else
2750 #endif
2751
2752 if (ff & PDF_TX_FIELD_IS_MULTILINE)
2753 {
2754 write_variable_text(ctx, annot, buf, res, lang, text, font, size, n, color, q, w, h, b*2,
2755 1.116f, 1.116f, 1, 0, 1);
2756 }
2757 else if (ff & PDF_TX_FIELD_IS_COMB)
2758 {
2759 int maxlen = pdf_dict_get_inheritable_int(ctx, annot->obj, PDF_NAME(MaxLen));
2760 if (has_bc && maxlen > 1)
2761 {
2762 float cell_w = (w - 2 * b) / maxlen;
2763 int i;
2764 for (i = 1; i < maxlen; ++i)
2765 {
2766 float x = b + cell_w * i;
2767 fz_append_printf(ctx, buf, "%g %g m %g %g l s\n", x, b, x, h-b);
2768 }
2769 }
2770 write_variable_text(ctx, annot, buf, res, lang, text, font, size, n, color, q, w, h, 0,
2771 0.8f, 1.2f, 0, maxlen, 0);
2772 }
2773 else
2774 {
2775 write_variable_text(ctx, annot, buf, res, lang, text, font, size, n, color, q, w, h, b*2,
2776 0.8f, 1.2f, 0, 0, 0);
2777 }
2778
2779 fz_append_string(ctx, buf, "Q\nEMC\n");
2780 }
2781
2782 fz_layout_block *
2783 pdf_layout_text_widget(fz_context *ctx, pdf_annot *annot)
2784 {
2785 fz_text_language lang;
2786 fz_layout_block *out;
2787 const char *font;
2788 const char *text;
2789 fz_rect rect;
2790 float size, color[4];
2791 float w, h, t, b, x, y;
2792 int q, r, n;
2793 int ff;
2794
2795 rect = pdf_dict_get_rect(ctx, annot->obj, PDF_NAME(Rect));
2796 text = pdf_field_value(ctx, annot->obj);
2797 ff = pdf_field_flags(ctx, annot->obj);
2798
2799 b = pdf_annot_border_width(ctx, annot);
2800 r = pdf_dict_get_int(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME(MK)), PDF_NAME(R));
2801 q = pdf_annot_quadding(ctx, annot);
2802 pdf_annot_default_appearance(ctx, annot, &font, &size, &n, color);
2803 lang = pdf_annot_language(ctx, annot);
2804
2805 w = rect.x1 - rect.x0;
2806 h = rect.y1 - rect.y0;
2807 r = r % 360;
2808 if (r == 90 || r == 270)
2809 t = h, h = w, w = t;
2810
2811 x = rect.x0;
2812 y = rect.y0;
2813
2814 out = fz_new_layout(ctx);
2815 fz_try(ctx)
2816 {
2817 pdf_page_transform(ctx, annot->page, NULL, &out->matrix);
2818 out->matrix = fz_concat(out->matrix, fz_rotate(r));
2819 out->inv_matrix = fz_invert_matrix(out->matrix);
2820
2821 if (ff & PDF_TX_FIELD_IS_MULTILINE)
2822 {
2823 layout_variable_text(ctx, out, text, lang, font, size, q, x, y, w, h, b*2, 1.116f, 1.116f, 1, 0, 1);
2824 }
2825 else if (ff & PDF_TX_FIELD_IS_COMB)
2826 {
2827 int maxlen = pdf_dict_get_inheritable_int(ctx, annot->obj, PDF_NAME(MaxLen));
2828 layout_variable_text(ctx, out, text, lang, font, size, q, x, y, w, h, 0, 0.8f, 1.2f, 0, maxlen, 0);
2829 }
2830 else
2831 {
2832 layout_variable_text(ctx, out, text, lang, font, size, q, x, y, w, h, b*2, 0.8f, 1.2f, 0, 0, 0);
2833 }
2834 }
2835 fz_catch(ctx)
2836 {
2837 fz_drop_layout(ctx, out);
2838 fz_rethrow(ctx);
2839 }
2840 return out;
2841 }
2842
2843 static void
2844 pdf_write_ch_widget_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf,
2845 const fz_rect *rect, fz_rect *bbox, fz_matrix *matrix, pdf_obj **res)
2846 {
2847 int ff = pdf_field_flags(ctx, annot->obj);
2848 if (ff & PDF_CH_FIELD_IS_COMBO)
2849 {
2850 /* TODO: Pop-down arrow */
2851 pdf_write_tx_widget_appearance(ctx, annot, buf, rect, bbox, matrix, res,
2852 pdf_field_value(ctx, annot->obj), 0);
2853 }
2854 else
2855 {
2856 fz_buffer *text = fz_new_buffer(ctx, 1024);
2857 fz_try(ctx)
2858 {
2859 pdf_obj *opt = pdf_dict_get(ctx, annot->obj, PDF_NAME(Opt));
2860 int i = pdf_dict_get_int(ctx, annot->obj, PDF_NAME(TI));
2861 int n = pdf_array_len(ctx, opt);
2862 /* TODO: Scrollbar */
2863 /* TODO: Highlight selected items */
2864 if (i < 0)
2865 i = 0;
2866 for (; i < n; ++i)
2867 {
2868 pdf_obj *val = pdf_array_get(ctx, opt, i);
2869 if (pdf_is_array(ctx, val))
2870 fz_append_string(ctx, text, pdf_array_get_text_string(ctx, val, 1));
2871 else
2872 fz_append_string(ctx, text, pdf_to_text_string(ctx, val));
2873 fz_append_byte(ctx, text, '\n');
2874 }
2875 pdf_write_tx_widget_appearance(ctx, annot, buf, rect, bbox, matrix, res,
2876 fz_string_from_buffer(ctx, text), PDF_TX_FIELD_IS_MULTILINE);
2877 }
2878 fz_always(ctx)
2879 fz_drop_buffer(ctx, text);
2880 fz_catch(ctx)
2881 fz_rethrow(ctx);
2882 }
2883 }
2884
2885 static void
2886 pdf_write_sig_widget_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf,
2887 const fz_rect *rect, fz_rect *bbox, fz_matrix *matrix, pdf_obj **res)
2888 {
2889 float x0 = rect->x0 + 1;
2890 float y0 = rect->y0 + 1;
2891 float x1 = rect->x1 - 1;
2892 float y1 = rect->y1 - 1;
2893 float w = x1 - x0;
2894 float h = y1 - y0;
2895 fz_append_printf(ctx, buf, "1 w\n0 G\n");
2896 fz_append_printf(ctx, buf, "%g %g %g %g re\n", x0, y0, w, h);
2897 fz_append_printf(ctx, buf, "%g %g m %g %g l\n", x0, y0, x1, y1);
2898 fz_append_printf(ctx, buf, "%g %g m %g %g l\n", x1, y0, x0, y1);
2899 fz_append_printf(ctx, buf, "s\n");
2900 *bbox = *rect;
2901 *matrix = fz_identity;
2902 }
2903
2904 static void
2905 pdf_write_widget_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf,
2906 fz_rect *rect, fz_rect *bbox, fz_matrix *matrix, pdf_obj **res)
2907 {
2908 pdf_obj *ft = pdf_dict_get_inheritable(ctx, annot->obj, PDF_NAME(FT));
2909 if (pdf_name_eq(ctx, ft, PDF_NAME(Tx)))
2910 {
2911 int ff = pdf_field_flags(ctx, annot->obj);
2912 char *format = NULL;
2913 const char *text = NULL;
2914 if (!annot->ignore_trigger_events)
2915 {
2916 format = pdf_field_event_format(ctx, annot->page->doc, annot->obj);
2917 if (format)
2918 text = format;
2919 else
2920 text = pdf_field_value(ctx, annot->obj);
2921 }
2922 else
2923 {
2924 text = pdf_field_value(ctx, annot->obj);
2925 }
2926 fz_try(ctx)
2927 pdf_write_tx_widget_appearance(ctx, annot, buf, rect, bbox, matrix, res, text, ff);
2928 fz_always(ctx)
2929 fz_free(ctx, format);
2930 fz_catch(ctx)
2931 fz_rethrow(ctx);
2932 }
2933 else if (pdf_name_eq(ctx, ft, PDF_NAME(Ch)))
2934 {
2935 pdf_write_ch_widget_appearance(ctx, annot, buf, rect, bbox, matrix, res);
2936 }
2937 else if (pdf_name_eq(ctx, ft, PDF_NAME(Sig)))
2938 {
2939 pdf_write_sig_widget_appearance(ctx, annot, buf, rect, bbox, matrix, res);
2940 }
2941 else
2942 {
2943 fz_throw(ctx, FZ_ERROR_ARGUMENT, "cannot create appearance stream for %s widgets", pdf_to_name(ctx, ft));
2944 }
2945 }
2946
2947 static void
2948 pdf_write_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf,
2949 fz_rect *rect, fz_rect *bbox, fz_matrix *matrix, pdf_obj **res)
2950 {
2951 switch (pdf_annot_type(ctx, annot))
2952 {
2953 default:
2954 fz_throw(ctx, FZ_ERROR_UNSUPPORTED, "cannot create appearance stream for %s annotations",
2955 pdf_dict_get_name(ctx, annot->obj, PDF_NAME(Subtype)));
2956 case PDF_ANNOT_WIDGET:
2957 pdf_write_widget_appearance(ctx, annot, buf, rect, bbox, matrix, res);
2958 break;
2959 case PDF_ANNOT_INK:
2960 pdf_write_ink_appearance(ctx, annot, buf, rect, res);
2961 *matrix = fz_identity;
2962 *bbox = *rect;
2963 break;
2964 case PDF_ANNOT_POLYGON:
2965 pdf_write_polygon_appearance(ctx, annot, buf, rect, res, 1);
2966 *matrix = fz_identity;
2967 *bbox = *rect;
2968 break;
2969 case PDF_ANNOT_POLY_LINE:
2970 pdf_write_polygon_appearance(ctx, annot, buf, rect, res, 0);
2971 *matrix = fz_identity;
2972 *bbox = *rect;
2973 break;
2974 case PDF_ANNOT_LINE:
2975 pdf_write_line_appearance(ctx, annot, buf, rect, res);
2976 *matrix = fz_identity;
2977 *bbox = *rect;
2978 break;
2979 case PDF_ANNOT_SQUARE:
2980 pdf_write_square_appearance(ctx, annot, buf, rect, res);
2981 *matrix = fz_identity;
2982 *bbox = *rect;
2983 break;
2984 case PDF_ANNOT_CIRCLE:
2985 pdf_write_circle_appearance(ctx, annot, buf, rect, res);
2986 *matrix = fz_identity;
2987 *bbox = *rect;
2988 break;
2989 case PDF_ANNOT_CARET:
2990 pdf_write_caret_appearance(ctx, annot, buf, rect, bbox, res);
2991 *matrix = fz_identity;
2992 break;
2993 case PDF_ANNOT_TEXT:
2994 case PDF_ANNOT_FILE_ATTACHMENT:
2995 case PDF_ANNOT_SOUND:
2996 pdf_write_icon_appearance(ctx, annot, buf, rect, bbox, res);
2997 *matrix = fz_identity;
2998 break;
2999 case PDF_ANNOT_HIGHLIGHT:
3000 pdf_write_highlight_appearance(ctx, annot, buf, rect, res);
3001 *matrix = fz_identity;
3002 *bbox = *rect;
3003 break;
3004 case PDF_ANNOT_UNDERLINE:
3005 pdf_write_underline_appearance(ctx, annot, buf, rect, res);
3006 *matrix = fz_identity;
3007 *bbox = *rect;
3008 break;
3009 case PDF_ANNOT_STRIKE_OUT:
3010 pdf_write_strike_out_appearance(ctx, annot, buf, rect, res);
3011 *matrix = fz_identity;
3012 *bbox = *rect;
3013 break;
3014 case PDF_ANNOT_SQUIGGLY:
3015 pdf_write_squiggly_appearance(ctx, annot, buf, rect, res);
3016 *matrix = fz_identity;
3017 *bbox = *rect;
3018 break;
3019 case PDF_ANNOT_REDACT:
3020 pdf_write_redact_appearance(ctx, annot, buf, rect, res);
3021 *matrix = fz_identity;
3022 *bbox = *rect;
3023 break;
3024 case PDF_ANNOT_STAMP:
3025 pdf_write_stamp_appearance(ctx, annot, buf, rect, bbox, matrix, res);
3026 break;
3027 case PDF_ANNOT_FREE_TEXT:
3028 pdf_write_free_text_appearance(ctx, annot, buf, rect, bbox, matrix, res);
3029 break;
3030 }
3031 }
3032
3033 static pdf_obj *draw_push_button(fz_context *ctx, pdf_annot *annot, fz_rect bbox, fz_matrix matrix, float w, float h,
3034 const char *caption, const char *font, float size, int n, float *color,
3035 int down)
3036 {
3037 pdf_obj *ap, *res = NULL;
3038 fz_buffer *buf;
3039 float bc[3] = { 0, 0, 0 };
3040 float bg[3] = { 0.8f, 0.8f, 0.8f };
3041 float hi[3], sh[3];
3042 int has_bg, has_bc;
3043 float b;
3044 int i;
3045
3046 buf = fz_new_buffer(ctx, 1024);
3047 fz_var(res);
3048 fz_try(ctx)
3049 {
3050 b = pdf_annot_border_width(ctx, annot);
3051 has_bc = pdf_annot_MK_BC_rgb(ctx, annot, bc);
3052 has_bg = pdf_annot_MK_BG_rgb(ctx, annot, bg);
3053
3054 for (i = 0; i < 3; ++i)
3055 {
3056 if (down)
3057 {
3058 sh[i] = 1 - (1 - bg[i]) / 2;
3059 hi[i] = bg[i] / 2;
3060 }
3061 else
3062 {
3063 hi[i] = 1 - (1 - bg[i]) / 2;
3064 sh[i] = bg[i] / 2;
3065 }
3066 }
3067
3068 fz_append_string(ctx, buf, "q\n");
3069 fz_append_printf(ctx, buf, "%g w\n", b);
3070 if (has_bg)
3071 {
3072 fz_append_printf(ctx, buf, "%g %g %g rg\n", bg[0], bg[1], bg[2]);
3073 fz_append_printf(ctx, buf, "0 0 %g %g re\nf\n", 0, 0, w, h);
3074 }
3075 if (has_bc && b > 0)
3076 {
3077 fz_append_printf(ctx, buf, "%g %g %g RG\n", bc[0], bc[1], bc[2]);
3078 fz_append_printf(ctx, buf, "%g %g %g %g re\nS\n", b/2, b/2, w-b, h-b);
3079 }
3080 if (has_bg)
3081 {
3082 fz_append_printf(ctx, buf, "%g %g %g rg\n", hi[0], hi[1], hi[2]);
3083 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",
3084 b, b, b, h-b, w-b, h-b, w-b-2, h-b-2, b+2, h-b-2, b+2, b+2);
3085 fz_append_printf(ctx, buf, "%g %g %g rg\n", sh[0], sh[1], sh[2]);
3086 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",
3087 b, b, b+2, b+2, w-b-2, b+2, w-b-2, h-b-2, w-b, h-b, w-b, b);
3088 }
3089 if (down)
3090 fz_append_string(ctx, buf, "1 0 0 1 2 -2 cm\n");
3091 write_variable_text(ctx, annot, buf, &res, FZ_LANG_UNSET, caption, font, size, n, color, 1, w, h, b+6, 0.8f, 1.2f, 0, 0, 0);
3092 fz_append_string(ctx, buf, "Q\n");
3093
3094 ap = pdf_new_xobject(ctx, annot->page->doc, bbox, matrix, res, buf);
3095 }
3096 fz_always(ctx)
3097 {
3098 pdf_drop_obj(ctx, res);
3099 fz_drop_buffer(ctx, buf);
3100 }
3101 fz_catch(ctx)
3102 fz_rethrow(ctx);
3103 return ap;
3104 }
3105
3106 static pdf_obj *draw_radio_button(fz_context *ctx, pdf_annot *annot, fz_rect bbox, fz_matrix matrix, float w, float h, int yes)
3107 {
3108 pdf_obj *ap;
3109 fz_buffer *buf;
3110 float b;
3111
3112 buf = fz_new_buffer(ctx, 1024);
3113 fz_try(ctx)
3114 {
3115 fz_append_string(ctx, buf, "q\n");
3116 if (pdf_write_MK_BG_appearance(ctx, annot, buf))
3117 {
3118 draw_circle_in_box(ctx, buf, 0, 0, 0, w, h);
3119 fz_append_string(ctx, buf, "f\n");
3120 }
3121 b = pdf_write_border_appearance(ctx, annot, buf);
3122 if (b > 0 && pdf_write_MK_BC_appearance(ctx, annot, buf))
3123 {
3124 draw_circle_in_box(ctx, buf, b, 0, 0, w, h);
3125 fz_append_string(ctx, buf, "s\n");
3126 }
3127 if (yes)
3128 {
3129 fz_append_string(ctx, buf, "0 g\n");
3130 draw_circle(ctx, buf, (w-b*2)/4, (h-b*2)/4, w/2, h/2);
3131 fz_append_string(ctx, buf, "f\n");
3132 }
3133 fz_append_string(ctx, buf, "Q\n");
3134 ap = pdf_new_xobject(ctx, annot->page->doc, bbox, matrix, NULL, buf);
3135 }
3136 fz_always(ctx)
3137 fz_drop_buffer(ctx, buf);
3138 fz_catch(ctx)
3139 fz_rethrow(ctx);
3140 return ap;
3141 }
3142
3143 static pdf_obj *draw_check_button(fz_context *ctx, pdf_annot *annot, fz_rect bbox, fz_matrix matrix, float w, float h, int yes)
3144 {
3145 float black[1] = { 0 };
3146 pdf_obj *ap, *res = NULL;
3147 fz_buffer *buf;
3148 float b;
3149
3150 fz_var(res);
3151
3152 buf = fz_new_buffer(ctx, 1024);
3153 fz_try(ctx)
3154 {
3155 fz_append_string(ctx, buf, "q\n");
3156 if (pdf_write_MK_BG_appearance(ctx, annot, buf))
3157 fz_append_printf(ctx, buf, "0 0 %g %g re\nf\n", w, h);
3158 b = pdf_write_border_appearance(ctx, annot, buf);
3159 if (b > 0 && pdf_write_MK_BC_appearance(ctx, annot, buf))
3160 fz_append_printf(ctx, buf, "%g %g %g %g re\nS\n", b/2, b/2, w-b, h-b);
3161 if (yes)
3162 write_variable_text(ctx, annot, buf, &res, FZ_LANG_UNSET, "3", "ZaDb", h, nelem(black), black, 0, w, h, b+h/10, 0.8f, 1.2f, 0, 0, 0);
3163 fz_append_string(ctx, buf, "Q\n");
3164 ap = pdf_new_xobject(ctx, annot->page->doc, bbox, matrix, res, buf);
3165 }
3166 fz_always(ctx)
3167 {
3168 pdf_drop_obj(ctx, res);
3169 fz_drop_buffer(ctx, buf);
3170 }
3171 fz_catch(ctx)
3172 fz_rethrow(ctx);
3173 return ap;
3174 }
3175
3176 static void pdf_update_button_appearance(fz_context *ctx, pdf_annot *annot)
3177 {
3178 int ff = pdf_field_flags(ctx, annot->obj);
3179 fz_rect rect = pdf_dict_get_rect(ctx, annot->obj, PDF_NAME(Rect));
3180 fz_matrix matrix;
3181 fz_rect bbox;
3182 float w, h, t;
3183 int r;
3184
3185 r = pdf_dict_get_int(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME(MK)), PDF_NAME(R));
3186 w = rect.x1 - rect.x0;
3187 h = rect.y1 - rect.y0;
3188 r = r % 360;
3189 if (r == 90 || r == 270)
3190 t = h, h = w, w = t;
3191 matrix = fz_rotate(r);
3192 bbox = fz_make_rect(0, 0, w, h);
3193
3194
3195 if (ff & PDF_BTN_FIELD_IS_PUSHBUTTON)
3196 {
3197 pdf_obj *ap_n = NULL;
3198 pdf_obj *ap_d = NULL;
3199 fz_var(ap_n);
3200 fz_var(ap_d);
3201 fz_try(ctx)
3202 {
3203 pdf_obj *ap, *MK, *CA, *AC;
3204 const char *font;
3205 const char *label;
3206 float size, color[4];
3207 int n;
3208
3209 pdf_annot_default_appearance(ctx, annot, &font, &size, &n, color);
3210
3211 MK = pdf_dict_get(ctx, annot->obj, PDF_NAME(MK));
3212 CA = pdf_dict_get(ctx, MK, PDF_NAME(CA));
3213 AC = pdf_dict_get(ctx, MK, PDF_NAME(AC));
3214
3215 label = pdf_to_text_string(ctx, CA);
3216 ap_n = draw_push_button(ctx, annot, bbox, matrix, w, h, label, font, size, n, color, 0);
3217
3218 label = pdf_to_text_string(ctx, AC ? AC : CA);
3219 ap_d = draw_push_button(ctx, annot, bbox, matrix, w, h, label, font, size, n, color, 1);
3220
3221 ap = pdf_dict_put_dict(ctx, annot->obj, PDF_NAME(AP), 2);
3222 pdf_dict_put(ctx, ap, PDF_NAME(N), ap_n);
3223 pdf_dict_put(ctx, ap, PDF_NAME(D), ap_d);
3224 }
3225 fz_always(ctx)
3226 {
3227 pdf_drop_obj(ctx, ap_n);
3228 pdf_drop_obj(ctx, ap_d);
3229 }
3230 fz_catch(ctx)
3231 fz_rethrow(ctx);
3232 }
3233 else
3234 {
3235 pdf_obj *as_yes = NULL;
3236 pdf_obj *ap_off = NULL;
3237 pdf_obj *ap_yes = NULL;
3238 fz_var(ap_off);
3239 fz_var(ap_yes);
3240 fz_var(as_yes);
3241 fz_try(ctx)
3242 {
3243 pdf_obj *ap, *ap_n, *as;
3244
3245 if (w > h) w = h;
3246 if (h > w) h = w;
3247
3248 if (ff & PDF_BTN_FIELD_IS_RADIO)
3249 {
3250 ap_off = draw_radio_button(ctx, annot, bbox, matrix, w, h, 0);
3251 ap_yes = draw_radio_button(ctx, annot, bbox, matrix, w, h, 1);
3252 }
3253 else
3254 {
3255 ap_off = draw_check_button(ctx, annot, bbox, matrix, w, h, 0);
3256 ap_yes = draw_check_button(ctx, annot, bbox, matrix, w, h, 1);
3257 }
3258
3259 as = pdf_dict_get(ctx, annot->obj, PDF_NAME(AS));
3260 if (!as)
3261 {
3262 pdf_dict_put(ctx, annot->obj, PDF_NAME(AS), PDF_NAME(Off));
3263 as = PDF_NAME(Off);
3264 }
3265 else
3266 as = pdf_resolve_indirect_chain(ctx, as);
3267
3268 if (as == PDF_NAME(Off))
3269 as_yes = pdf_keep_obj(ctx, pdf_button_field_on_state(ctx, annot->obj));
3270 else
3271 as_yes = pdf_keep_obj(ctx, as);
3272
3273 ap = pdf_dict_put_dict(ctx, annot->obj, PDF_NAME(AP), 2);
3274 ap_n = pdf_dict_put_dict(ctx, ap, PDF_NAME(N), 2);
3275 pdf_dict_put(ctx, ap_n, PDF_NAME(Off), ap_off);
3276 pdf_dict_put(ctx, ap_n, as_yes, ap_yes);
3277 }
3278 fz_always(ctx)
3279 {
3280 pdf_drop_obj(ctx, as_yes);
3281 pdf_drop_obj(ctx, ap_yes);
3282 pdf_drop_obj(ctx, ap_off);
3283 }
3284 fz_catch(ctx)
3285 {
3286 fz_rethrow(ctx);
3287 }
3288 }
3289 pdf_set_annot_resynthesised(ctx, annot);
3290 }
3291
3292 static void draw_logo(fz_context *ctx, fz_path *path)
3293 {
3294 /* Use mupdf logo for signature appearance background. */
3295 fz_moveto(ctx, path, 122.25f, 0.0f);
3296 fz_lineto(ctx, path, 122.25f, 14.249f);
3297 fz_curveto(ctx, path, 125.98f, 13.842f, 129.73f, 13.518f, 133.5f, 13.277f);
3298 fz_lineto(ctx, path, 133.5f, 0.0f);
3299 fz_lineto(ctx, path, 122.25f, 0.0f);
3300 fz_closepath(ctx, path);
3301 fz_moveto(ctx, path, 140.251f, 0.0f);
3302 fz_lineto(ctx, path, 140.251f, 12.935f);
3303 fz_curveto(ctx, path, 152.534f, 12.477f, 165.03f, 12.899f, 177.75f, 14.249f);
3304 fz_lineto(ctx, path, 177.75f, 21.749f);
3305 fz_curveto(ctx, path, 165.304f, 20.413f, 152.809f, 19.871f, 140.251f, 20.348f);
3306 fz_lineto(ctx, path, 140.251f, 39.0f);
3307 fz_lineto(ctx, path, 133.5f, 39.0f);
3308 fz_lineto(ctx, path, 133.5f, 20.704f);
3309 fz_curveto(ctx, path, 129.756f, 20.956f, 126.006f, 21.302f, 122.25f, 21.749f);
3310 fz_lineto(ctx, path, 122.25f, 50.999f);
3311 fz_lineto(ctx, path, 177.751f, 50.999f);
3312 fz_lineto(ctx, path, 177.751f, 0.0f);
3313 fz_lineto(ctx, path, 140.251f, 0.0f);
3314 fz_closepath(ctx, path);
3315 fz_moveto(ctx, path, 23.482f, 129.419f);
3316 fz_curveto(ctx, path, -20.999f, 199.258f, -0.418f, 292.039f, 69.42f, 336.519f);
3317 fz_curveto(ctx, path, 139.259f, 381.0f, 232.04f, 360.419f, 276.52f, 290.581f);
3318 fz_curveto(ctx, path, 321.001f, 220.742f, 300.42f, 127.961f, 230.582f, 83.481f);
3319 fz_curveto(ctx, path, 160.743f, 39.0f, 67.962f, 59.581f, 23.482f, 129.419f);
3320 fz_closepath(ctx, path);
3321 fz_moveto(ctx, path, 254.751f, 128.492f);
3322 fz_curveto(ctx, path, 303.074f, 182.82f, 295.364f, 263.762f, 237.541f, 309.165f);
3323 fz_curveto(ctx, path, 179.718f, 354.568f, 93.57f, 347.324f, 45.247f, 292.996f);
3324 fz_curveto(ctx, path, -3.076f, 238.668f, 4.634f, 157.726f, 62.457f, 112.323f);
3325 fz_curveto(ctx, path, 120.28f, 66.92f, 206.428f, 74.164f, 254.751f, 128.492f);
3326 fz_closepath(ctx, path);
3327 fz_moveto(ctx, path, 111.0f, 98.999f);
3328 fz_curveto(ctx, path, 87.424f, 106.253f, 68.25f, 122.249f, 51.75f, 144.749f);
3329 fz_lineto(ctx, path, 103.5f, 297.749f);
3330 fz_lineto(ctx, path, 213.75f, 298.499f);
3331 fz_curveto(ctx, path, 206.25f, 306.749f, 195.744f, 311.478f, 185.25f, 314.249f);
3332 fz_curveto(ctx, path, 164.22f, 319.802f, 141.22f, 319.775f, 120.0f, 314.999f);
3333 fz_curveto(ctx, path, 96.658f, 309.745f, 77.25f, 298.499f, 55.5f, 283.499f);
3334 fz_curveto(ctx, path, 69.75f, 299.249f, 84.617f, 311.546f, 102.75f, 319.499f);
3335 fz_curveto(ctx, path, 117.166f, 325.822f, 133.509f, 327.689f, 149.25f, 327.749f);
3336 fz_curveto(ctx, path, 164.21f, 327.806f, 179.924f, 326.532f, 193.5f, 320.249f);
3337 fz_curveto(ctx, path, 213.95f, 310.785f, 232.5f, 294.749f, 245.25f, 276.749f);
3338 fz_lineto(ctx, path, 227.25f, 276.749f);
3339 fz_curveto(ctx, path, 213.963f, 276.749f, 197.25f, 263.786f, 197.25f, 250.499f);
3340 fz_lineto(ctx, path, 197.25f, 112.499f);
3341 fz_curveto(ctx, path, 213.75f, 114.749f, 228.0f, 127.499f, 241.5f, 140.999f);
3342 fz_curveto(ctx, path, 231.75f, 121.499f, 215.175f, 109.723f, 197.25f, 101.249f);
3343 fz_curveto(ctx, path, 181.5f, 95.249f, 168.412f, 94.775f, 153.0f, 94.499f);
3344 fz_curveto(ctx, path, 139.42f, 94.256f, 120.75f, 95.999f, 111.0f, 98.999f);
3345 fz_closepath(ctx, path);
3346 fz_moveto(ctx, path, 125.25f, 105.749f);
3347 fz_lineto(ctx, path, 125.25f, 202.499f);
3348 fz_lineto(ctx, path, 95.25f, 117.749f);
3349 fz_curveto(ctx, path, 105.75f, 108.749f, 114.0f, 105.749f, 125.25f, 105.749f);
3350 fz_closepath(ctx, path);
3351 };
3352
3353 static float logo_color[3] = { (float)0xa4 / (float)0xFF, (float)0xca / (float)0xFF, (float)0xf5 / (float)0xFF };
3354
3355 fz_display_list *
3356 pdf_signature_appearance_signed(fz_context *ctx, fz_rect rect, fz_text_language lang, fz_image *img, const char *left_text, const char *right_text, int include_logo)
3357 {
3358 fz_display_list *dlist = NULL;
3359 fz_device *dev = NULL;
3360 fz_text *text = NULL;
3361 fz_colorspace *cs = NULL;
3362 fz_path *path = NULL;
3363 fz_font *font = NULL;
3364
3365 fz_var(path);
3366 fz_var(dlist);
3367 fz_var(dev);
3368 fz_var(text);
3369 fz_var(font);
3370 fz_try(ctx)
3371 {
3372 fz_rect prect;
3373 fz_rect logo_bounds;
3374 fz_matrix logo_tm;
3375 float color[] = { 0.0, 0.0, 0.0 };
3376
3377 font = fz_new_base14_font(ctx, "Helvetica");
3378
3379 dlist = fz_new_display_list(ctx, rect);
3380 dev = fz_new_list_device(ctx, dlist);
3381 cs = fz_device_rgb(ctx);
3382
3383 if (include_logo)
3384 {
3385 path = fz_new_path(ctx);
3386 draw_logo(ctx, path);
3387 logo_bounds = fz_bound_path(ctx, path, NULL, fz_identity);
3388 logo_tm = center_rect_within_rect(logo_bounds, rect);
3389 fz_fill_path(ctx, dev, path, 0, logo_tm, cs, logo_color, 1.0f, fz_default_color_params);
3390 }
3391
3392 prect = rect;
3393 /* If there is to be info on the right then use only the left half of the rectangle for
3394 * what is intended for the left */
3395 if (right_text)
3396 prect.x1 = (prect.x0 + prect.x1) / 2.0f;
3397
3398 if (img)
3399 {
3400 float img_aspect = (float) img->w / img->h;
3401 float rectw = prect.x1 - prect.x0;
3402 float recth = prect.y1 - prect.y0;
3403 float midx = (prect.x0 + prect.x1) / 2.0f;
3404 float midy = (prect.y0 + prect.y1) / 2.0f;
3405 float rect_aspect = rectw / recth;
3406 float scale = img_aspect > rect_aspect ? rectw / img->w : recth / img->h;
3407 fz_matrix ctm = fz_pre_translate(fz_pre_scale(fz_translate(midx, midy), scale * img->w, scale * img->h), -0.5, -0.5);
3408 fz_fill_image(ctx, dev, img, ctm, 1.0f, fz_default_color_params);
3409 }
3410
3411 if (left_text)
3412 {
3413 text = pdf_layout_fit_text(ctx, font, lang, left_text, prect);
3414 fz_fill_text(ctx, dev, text, fz_identity, cs, color, 1.0f, fz_default_color_params);
3415 fz_drop_text(ctx, text);
3416 text = NULL;
3417 }
3418
3419 prect = rect;
3420 /* If there is to be info on the left then use only the right half of the rectangle for
3421 * what is intended for the right */
3422 if (img || left_text)
3423 prect.x0 = (prect.x0 + prect.x1) / 2.0f;
3424
3425 if (right_text)
3426 {
3427 text = pdf_layout_fit_text(ctx, font, lang, right_text, prect);
3428 fz_fill_text(ctx, dev, text, fz_identity, cs, color, 1.0f, fz_default_color_params);
3429 }
3430 }
3431 fz_always(ctx)
3432 {
3433 fz_drop_device(ctx, dev);
3434 fz_drop_path(ctx, path);
3435 fz_drop_text(ctx, text);
3436 fz_drop_font(ctx, font);
3437 }
3438 fz_catch(ctx)
3439 {
3440 fz_drop_display_list(ctx, dlist);
3441 fz_rethrow(ctx);
3442 }
3443
3444 return dlist;
3445 }
3446
3447 fz_display_list *
3448 pdf_signature_appearance_unsigned(fz_context *ctx, fz_rect rect, fz_text_language lang)
3449 {
3450 fz_display_list *dlist = NULL;
3451 fz_device *dev = NULL;
3452 fz_text *text = NULL;
3453 fz_colorspace *cs = NULL;
3454 fz_path *path = NULL;
3455 fz_font *font = NULL;
3456
3457 fz_var(path);
3458 fz_var(dlist);
3459 fz_var(dev);
3460 fz_var(text);
3461 fz_var(font);
3462 fz_try(ctx)
3463 {
3464 float text_color[] = { 1.0f, 1.0f, 1.0f };
3465 float arrow_color[] = { 0.95f, 0.33f, 0.18f };
3466
3467 dlist = fz_new_display_list(ctx, rect);
3468 dev = fz_new_list_device(ctx, dlist);
3469
3470 rect.y1 = rect.y0 + (rect.y1 - rect.y0) / 6;
3471 rect.x1 = rect.x0 + (rect.y1 - rect.y0) * 4;
3472 font = fz_new_base14_font(ctx, "Helvetica");
3473
3474 path = fz_new_path(ctx);
3475 /* Draw a rectangle with a protrusion to the right [xxxxx> */
3476 fz_moveto(ctx, path, rect.x0, rect.y0);
3477 fz_lineto(ctx, path, rect.x1, rect.y0);
3478 fz_lineto(ctx, path, rect.x1 + (rect.y1 - rect.y0) / 2.0f, (rect.y0 + rect.y1) / 2.0f);
3479 fz_lineto(ctx, path, rect.x1, rect.y1);
3480 fz_lineto(ctx, path, rect.x0, rect.y1);
3481 fz_closepath(ctx, path);
3482 cs = fz_device_rgb(ctx);
3483 fz_fill_path(ctx, dev, path, 0, fz_identity, cs, arrow_color, 1.0f, fz_default_color_params);
3484
3485 text = pdf_layout_fit_text(ctx, font, lang, "SIGN", rect);
3486 fz_fill_text(ctx, dev, text, fz_identity, cs, text_color, 1.0f, fz_default_color_params);
3487 fz_drop_text(ctx, text);
3488 text = NULL;
3489 }
3490 fz_always(ctx)
3491 {
3492 fz_drop_device(ctx, dev);
3493 fz_drop_path(ctx, path);
3494 fz_drop_text(ctx, text);
3495 fz_drop_font(ctx, font);
3496 }
3497 fz_catch(ctx)
3498 {
3499 fz_drop_display_list(ctx, dlist);
3500 fz_rethrow(ctx);
3501 }
3502
3503 return dlist;
3504 }
3505
3506 char *
3507 pdf_signature_info(fz_context *ctx, const char *name, pdf_pkcs7_distinguished_name *dn, const char *reason, const char *location, int64_t date, int include_labels)
3508 {
3509 fz_buffer *fzbuf = NULL;
3510 char *dn_str = NULL;
3511 char *full_str = NULL;
3512 time_t tdate = (time_t)date;
3513
3514 fz_var(fzbuf);
3515 fz_var(dn_str);
3516 fz_try(ctx)
3517 {
3518 #ifdef _POSIX_SOURCE
3519 struct tm tmbuf, *tm = localtime_r(&tdate, &tmbuf);
3520 #else
3521 struct tm *tm = localtime(&tdate);
3522 #endif
3523 char now_str[40];
3524 size_t len = 0;
3525 #ifdef CLUSTER
3526 memset(&date, 0, sizeof(date));
3527 memset(tm, 0, sizeof(*tm));
3528 #endif
3529
3530 fzbuf = fz_new_buffer(ctx, 256);
3531 if (name && strlen(name) > 0)
3532 {
3533 if (include_labels)
3534 fz_append_string(ctx, fzbuf, "Digitally signed by ");
3535 fz_append_string(ctx, fzbuf, name);
3536 }
3537
3538 if (dn)
3539 {
3540 fz_append_string(ctx, fzbuf, "\n");
3541 if (include_labels)
3542 fz_append_string(ctx, fzbuf, "DN: ");
3543 dn_str = pdf_signature_format_distinguished_name(ctx, dn);
3544 fz_append_string(ctx, fzbuf, dn_str);
3545 }
3546
3547 if (reason && strlen(reason) > 0)
3548 {
3549 fz_append_string(ctx, fzbuf, "\n");
3550 if (include_labels)
3551 fz_append_string(ctx, fzbuf, "Reason: ");
3552 fz_append_string(ctx, fzbuf, reason);
3553 }
3554
3555 if (location && strlen(location) > 0)
3556 {
3557 fz_append_string(ctx, fzbuf, "\n");
3558 if (include_labels)
3559 fz_append_string(ctx, fzbuf, "Location: ");
3560 fz_append_string(ctx, fzbuf, location);
3561 }
3562
3563 if (date >= 0)
3564 {
3565 len = strftime(now_str, sizeof now_str, "%FT%T%z", tm);
3566 if (len)
3567 {
3568 fz_append_string(ctx, fzbuf, "\n");
3569 if (include_labels)
3570 fz_append_string(ctx, fzbuf, "Date: ");
3571 fz_append_string(ctx, fzbuf, now_str);
3572 }
3573 }
3574
3575 fz_terminate_buffer(ctx, fzbuf);
3576 (void)fz_buffer_extract(ctx, fzbuf, (unsigned char **)&full_str);
3577 }
3578 fz_always(ctx)
3579 {
3580 fz_drop_buffer(ctx, fzbuf);
3581 fz_free(ctx, dn_str);
3582 }
3583 fz_catch(ctx)
3584 {
3585 fz_rethrow(ctx);
3586 }
3587
3588 return full_str;
3589 }
3590
3591 void
3592 pdf_annot_push_local_xref(fz_context *ctx, pdf_annot *annot)
3593 {
3594 pdf_document *doc;
3595
3596 if (!annot->page)
3597 fz_throw(ctx, FZ_ERROR_ARGUMENT, "annotation not bound to any page");
3598
3599 doc = annot->page->doc;
3600
3601 #ifdef PDF_DEBUG_APPEARANCE_SYNTHESIS
3602 if (doc->local_xref_nesting == 0 && doc->local_xref)
3603 fz_write_printf(ctx, fz_stddbg(ctx), "push local_xref for annot\n");
3604 #endif
3605 doc->local_xref_nesting++;
3606 }
3607
3608 void
3609 pdf_annot_ensure_local_xref(fz_context *ctx, pdf_annot *annot)
3610 {
3611 pdf_document *doc = annot->page->doc;
3612
3613 if (doc->local_xref != NULL)
3614 return;
3615
3616 #ifdef PDF_DEBUG_APPEARANCE_SYNTHESIS
3617 fz_write_printf(ctx, fz_stddbg(ctx), "creating local_xref\n");
3618 #endif
3619
3620 /* We have no local_xref, but we want to be using one. */
3621 /* First off, create one. */
3622 doc->local_xref = pdf_new_local_xref(ctx, doc);
3623 }
3624
3625 void
3626 pdf_annot_pop_local_xref(fz_context *ctx, pdf_annot *annot)
3627 {
3628 pdf_document *doc = annot->page->doc;
3629
3630 --doc->local_xref_nesting;
3631 #ifdef PDF_DEBUG_APPEARANCE_SYNTHESIS
3632 if (doc->local_xref_nesting == 0 && doc->local_xref)
3633 fz_write_printf(ctx, fz_stddbg(ctx), "pop local_xref for annot\n");
3634 #endif
3635 }
3636
3637 void pdf_annot_pop_and_discard_local_xref(fz_context *ctx, pdf_annot *annot)
3638 {
3639 pdf_document *doc = annot->page->doc;
3640
3641 #ifdef PDF_DEBUG_APPEARANCE_SYNTHESIS
3642 if (doc->local_xref)
3643 fz_write_printf(ctx, fz_stddbg(ctx), "pop and discard local_xref for annot\n");
3644 #endif
3645 --doc->local_xref_nesting;
3646 assert(doc->local_xref_nesting == 0);
3647 pdf_drop_local_xref_and_resources(ctx, doc);
3648 }
3649
3650 static void pdf_update_appearance(fz_context *ctx, pdf_annot *annot)
3651 {
3652 pdf_obj *subtype;
3653 pdf_obj *ft = NULL;
3654 pdf_obj *ap_n;
3655 int pop_local_xref = 1;
3656
3657 retry_after_repair:
3658 /* Must have any local xref in place in order to check if it's dirty. */
3659 pdf_annot_push_local_xref(ctx, annot);
3660
3661 pdf_begin_implicit_operation(ctx, annot->page->doc);
3662 fz_start_throw_on_repair(ctx);
3663
3664 fz_var(pop_local_xref);
3665
3666 fz_try(ctx)
3667 {
3668 int needs_resynth;
3669 int local_synthesis = 0;
3670
3671 /* Never update Popup and Link annotations */
3672 subtype = pdf_dict_get(ctx, annot->obj, PDF_NAME(Subtype));
3673 if (subtype == PDF_NAME(Popup) || subtype == PDF_NAME(Link))
3674 {
3675 pdf_end_operation(ctx, annot->page->doc);
3676 break;
3677 }
3678
3679 /* Never update signed Signature widgets */
3680 if (subtype == PDF_NAME(Widget))
3681 {
3682 ft = pdf_dict_get_inheritable(ctx, annot->obj, PDF_NAME(FT));
3683 if (ft == PDF_NAME(Sig))
3684 {
3685 /* We cannot synthesise an appearance for a signed Sig, so don't even try. */
3686 if (pdf_signature_is_signed(ctx, annot->page->doc, annot->obj))
3687 {
3688 pdf_end_operation(ctx, annot->page->doc);
3689 break;
3690 }
3691 }
3692 }
3693
3694 /* Check if the field is dirtied by JS events */
3695 if (pdf_obj_is_dirty(ctx, annot->obj))
3696 pdf_annot_request_resynthesis(ctx, annot);
3697
3698 /* Find the current appearance stream, if one exists. */
3699 ap_n = pdf_annot_ap(ctx, annot);
3700
3701 /* If there is no appearance stream, we need to create a local one for display purposes. */
3702 if (!ap_n)
3703 local_synthesis = 1;
3704
3705 /* Ignore appearance streams not created by us (so not local)
3706 * for unsigned digital signature widgets. They are often blank
3707 * and we want the "sign" arrow to be visible. Never write back
3708 * the forced appearance stream for unsigned signatures. */
3709 if (subtype == PDF_NAME(Widget) && ft == PDF_NAME(Sig))
3710 {
3711 if (ap_n && !pdf_is_local_object(ctx, annot->page->doc, ap_n))
3712 local_synthesis = 1;
3713 }
3714
3715 /* We need to put this appearance stream back into the document. */
3716 needs_resynth = pdf_annot_needs_resynthesis(ctx, annot);
3717 if (needs_resynth)
3718 local_synthesis = 0;
3719
3720 /* Some appearances can NEVER be resynthesised. Spot those here. */
3721 if (needs_resynth)
3722 {
3723 if (pdf_name_eq(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME(Subtype)), PDF_NAME(Stamp)))
3724 {
3725 /* Don't resynthesize Stamps with non-standard names if
3726 * they already have an appearance. These usually have
3727 * custom images already set for their appearance.
3728 */
3729 if (!pdf_annot_is_standard_stamp(ctx, annot) && ap_n)
3730 {
3731 /* However, we allow changing the Rect even if we don't
3732 * resynthesize the appearance. This should also count
3733 * as having a changed appearance. */
3734 pdf_set_annot_resynthesised(ctx, annot);
3735 needs_resynth = 0;
3736 }
3737 }
3738 }
3739
3740 if (local_synthesis || needs_resynth)
3741 {
3742 fz_display_list *dlist;
3743 fz_rect rect, bbox;
3744 fz_buffer *buf;
3745 pdf_obj *res = NULL;
3746 pdf_obj *new_ap_n = NULL;
3747 fz_var(res);
3748 fz_var(new_ap_n);
3749
3750 #ifdef PDF_DEBUG_APPEARANCE_SYNTHESIS
3751 fz_write_printf(ctx, fz_stddbg(ctx), "Update Appearance: %d\n", pdf_to_num(ctx, annot->obj));
3752 pdf_debug_obj(ctx, annot->obj);
3753 fz_write_printf(ctx, fz_stddbg(ctx), "\n");
3754 #endif
3755
3756 if (local_synthesis)
3757 {
3758 #ifdef PDF_DEBUG_APPEARANCE_SYNTHESIS
3759 fz_write_printf(ctx, fz_stddbg(ctx), "Local synthesis\n");
3760 #endif
3761 pdf_annot_ensure_local_xref(ctx, annot);
3762 }
3763 else
3764 {
3765 #ifdef PDF_DEBUG_APPEARANCE_SYNTHESIS
3766 fz_write_printf(ctx, fz_stddbg(ctx), "Non-Local synthesis\n");
3767 #endif
3768 /* We don't want to be using any local xref, so
3769 * bin any that we have. */
3770 pdf_annot_pop_and_discard_local_xref(ctx, annot);
3771 /* Binning the xref may leave ap_n holding an invalid pointer.
3772 * We don't use it from this point onwards anymore, but beware
3773 * in future code changes. */
3774 pop_local_xref = 0;
3775 }
3776
3777 if (subtype == PDF_NAME(Widget) && ft == PDF_NAME(Btn))
3778 {
3779 /* Special case for Btn widgets that need multiple appearance streams. */
3780 pdf_update_button_appearance(ctx, annot);
3781 }
3782 else if (subtype == PDF_NAME(Widget) && ft == PDF_NAME(Sig))
3783 {
3784 /* Special case for unsigned signature widgets,
3785 * which are most easily created via a display list. */
3786 rect = pdf_annot_rect(ctx, annot);
3787 dlist = pdf_signature_appearance_unsigned(ctx, rect, pdf_annot_language(ctx, annot));
3788 fz_try(ctx)
3789 pdf_set_annot_appearance_from_display_list(ctx, annot, "N", NULL, fz_identity, dlist);
3790 fz_always(ctx)
3791 fz_drop_display_list(ctx, dlist);
3792 fz_catch(ctx)
3793 fz_rethrow(ctx);
3794 }
3795 else
3796 {
3797 buf = fz_new_buffer(ctx, 1024);
3798 fz_try(ctx)
3799 {
3800 fz_matrix matrix = fz_identity;
3801 rect = pdf_dict_get_rect(ctx, annot->obj, PDF_NAME(Rect));
3802 pdf_write_appearance(ctx, annot, buf, &rect, &bbox, &matrix, &res);
3803 pdf_dict_put_rect(ctx, annot->obj, PDF_NAME(Rect), rect);
3804 pdf_set_annot_appearance(ctx, annot, "N", NULL, matrix, bbox, res, buf);
3805 }
3806 fz_always(ctx)
3807 {
3808 fz_drop_buffer(ctx, buf);
3809 pdf_drop_obj(ctx, res);
3810 pdf_drop_obj(ctx, new_ap_n);
3811 }
3812 fz_catch(ctx)
3813 {
3814 fz_rethrow_if(ctx, FZ_ERROR_REPAIRED);
3815 fz_rethrow_if(ctx, FZ_ERROR_SYSTEM);
3816 fz_report_error(ctx);
3817 fz_warn(ctx, "cannot create appearance stream");
3818 }
3819 }
3820
3821 #ifdef PDF_DEBUG_APPEARANCE_SYNTHESIS
3822 fz_write_printf(ctx, fz_stddbg(ctx), "Annot obj after synthesis\n");
3823 pdf_debug_obj(ctx, annot->obj);
3824 fz_write_printf(ctx, fz_stddbg(ctx), "\nAppearance after synthesis\n");
3825 pdf_debug_obj(ctx, pdf_dict_getp(ctx, annot->obj, "AP/N"));
3826 fz_write_printf(ctx, fz_stddbg(ctx), "\n");
3827 #endif
3828 }
3829
3830 pdf_clean_obj(ctx, annot->obj);
3831 pdf_end_operation(ctx, annot->page->doc);
3832 }
3833 fz_always(ctx)
3834 {
3835 if (pop_local_xref)
3836 pdf_annot_pop_local_xref(ctx, annot);
3837 fz_end_throw_on_repair(ctx);
3838 }
3839 fz_catch(ctx)
3840 {
3841 pdf_abandon_operation(ctx, annot->page->doc);
3842 /* If we hit a repair while synthesising, we need to give it another
3843 * go. Do that directly here, rather than waiting for the next time
3844 * we are called, because we don't want to risk discarding any
3845 * local_xrefs on the second pass through the list of annotations.
3846 * Repairs only ever happen once for a document, so no infinite
3847 * loop potential here. */
3848 if (fz_caught(ctx) == FZ_ERROR_REPAIRED)
3849 {
3850 fz_report_error(ctx);
3851 goto retry_after_repair;
3852 }
3853 fz_rethrow(ctx);
3854 }
3855 }
3856
3857 static void *
3858 update_appearances(fz_context *ctx, fz_page *page_, void *state)
3859 {
3860 pdf_page *page = (pdf_page *)page_;
3861 pdf_annot *annot;
3862
3863 for (annot = pdf_first_annot(ctx, page); annot; annot = pdf_next_annot(ctx, annot))
3864 pdf_update_appearance(ctx, annot);
3865 for (annot = pdf_first_widget(ctx, page); annot; annot = pdf_next_widget(ctx, annot))
3866 pdf_update_appearance(ctx, annot);
3867
3868 return NULL;
3869 }
3870
3871 static void
3872 update_all_appearances(fz_context *ctx, pdf_page *page)
3873 {
3874 pdf_document *doc = page->doc;
3875
3876 /* Update all the annotations on all the pages open in the document.
3877 * At least one annotation should be resynthesised because we'll
3878 * only reach here if resynth_required was set. Any such resynthesis
3879 * that changes the document will throw away any local_xref. */
3880 fz_process_opened_pages(ctx, &doc->super, update_appearances, NULL);
3881 /* If the page isn't linked in yet (as is the case whilst loading
3882 * the annots for a page), process that too. */
3883 if (page->super.prev == NULL && page->super.next == NULL)
3884 update_appearances(ctx, &page->super, NULL);
3885
3886 /* Run it a second time, so that any annotations whose synthesised
3887 * appearances live in the local_xref (such as unsigned sigs) can
3888 * be regenerated too. Running this for annots which are up to date
3889 * should be fast. */
3890 fz_process_opened_pages(ctx, &doc->super, update_appearances, NULL);
3891 /* And cope with a non-linked in page again. */
3892 if (page->super.prev == NULL && page->super.next == NULL)
3893 update_appearances(ctx, &page->super, NULL);
3894
3895 doc->resynth_required = 0;
3896 }
3897
3898 int
3899 pdf_update_annot(fz_context *ctx, pdf_annot *annot)
3900 {
3901 int changed;
3902
3903 if (!annot->page)
3904 fz_throw(ctx, FZ_ERROR_ARGUMENT, "annotation not bound to any page");
3905
3906 if (annot->page->doc->resynth_required)
3907 update_all_appearances(ctx, annot->page);
3908
3909 changed = annot->has_new_ap;
3910 annot->has_new_ap = 0;
3911 return changed;
3912 }