Coverage Report

Created: 2025-12-03 07:00

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