Coverage Report

Created: 2026-11-12 07:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/mupdf/source/pdf/pdf-annot.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
26
#include <string.h>
27
#include <float.h>
28
29
pdf_annot *
30
pdf_keep_annot(fz_context *ctx, pdf_annot *annot)
31
0
{
32
0
  return fz_keep_imp(ctx, annot, &annot->refs);
33
0
}
34
35
void
36
pdf_drop_annot(fz_context *ctx, pdf_annot *annot)
37
445
{
38
445
  if (fz_drop_imp(ctx, annot, &annot->refs))
39
445
  {
40
445
    pdf_drop_obj(ctx, annot->obj);
41
445
    fz_free(ctx, annot);
42
445
  }
43
445
}
44
45
void
46
pdf_drop_annots(fz_context *ctx, pdf_annot *annot)
47
326
{
48
411
  while (annot)
49
85
  {
50
85
    pdf_annot *next = annot->next;
51
85
    pdf_drop_annot(ctx, annot);
52
85
    annot = next;
53
85
  }
54
326
}
55
56
pdf_obj *
57
pdf_annot_ap(fz_context *ctx, pdf_annot *annot)
58
2.61k
{
59
2.61k
  int flags = pdf_annot_flags(ctx, annot);
60
2.61k
  int readonly = flags & PDF_ANNOT_IS_READ_ONLY;
61
62
2.61k
  pdf_obj *ap = pdf_dict_get(ctx, annot->obj, PDF_NAME(AP));
63
2.61k
  pdf_obj *ap_n = pdf_dict_get(ctx, ap, PDF_NAME(N));
64
2.61k
  pdf_obj *ap_r = pdf_dict_get(ctx, ap, PDF_NAME(R));
65
2.61k
  pdf_obj *ap_d = pdf_dict_get(ctx, ap, PDF_NAME(D));
66
67
2.61k
  if (!readonly && annot->is_hot && annot->is_active && ap_d)
68
0
    ap = ap_d;
69
2.61k
  else if (!readonly && annot->is_hot && ap_r)
70
0
    ap = ap_r;
71
2.61k
  else
72
2.61k
    ap = ap_n;
73
74
  /* AP/N, AP/R and AP/D may be streams, or dictionaries of streams. */
75
76
  /* If it's a stream, we have a winner! */
77
2.61k
  if (pdf_is_indirect(ctx, ap) && pdf_obj_num_is_stream(ctx, annot->page->doc, pdf_to_num(ctx, ap)))
78
2.37k
    return ap;
79
80
  /* If it's not a stream, it may be a dictionary containing
81
   * a range of possible values, that should be indexed by
82
   * AS. */
83
240
  return pdf_dict_get(ctx, ap, pdf_resolve_indirect_chain(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME(AS))));
84
2.61k
}
85
86
int pdf_annot_active(fz_context *ctx, pdf_annot *annot)
87
0
{
88
0
  return annot ? annot->is_active : 0;
89
0
}
90
91
void pdf_set_annot_active(fz_context *ctx, pdf_annot *annot, int active)
92
0
{
93
0
  int old;
94
95
0
  if (!annot)
96
0
    return;
97
98
0
  old = annot->is_active;
99
0
  annot->is_active = !!active;
100
0
  if (old != annot->is_active)
101
0
    pdf_set_annot_has_changed(ctx, annot);
102
0
}
103
104
int pdf_annot_hot(fz_context *ctx, pdf_annot *annot)
105
0
{
106
0
  return annot ? annot->is_hot : 0;
107
0
}
108
109
void pdf_set_annot_hot(fz_context *ctx, pdf_annot *annot, int hot)
110
0
{
111
0
  int old;
112
113
0
  if (!annot)
114
0
    return;
115
116
0
  old = annot->is_hot;
117
0
  annot->is_hot = !!hot;
118
0
  if (old != annot->is_hot)
119
0
    pdf_set_annot_has_changed(ctx, annot);
120
0
}
121
122
int
123
pdf_annot_display_rotate(fz_context *ctx, pdf_annot *annot)
124
445
{
125
445
  int flags;
126
445
  int rotate;
127
128
445
  pdf_annot_push_local_xref(ctx, annot);
129
890
  fz_try(ctx)
130
890
  {
131
445
    flags = pdf_annot_flags(ctx, annot);
132
445
    if (flags & PDF_ANNOT_IS_NO_ROTATE)
133
0
      rotate = pdf_dict_get_inheritable_int(ctx, annot->page->obj, PDF_NAME(Rotate));
134
445
    else
135
445
      rotate = 0;
136
445
  }
137
890
  fz_always(ctx)
138
445
    pdf_annot_pop_local_xref(ctx, annot);
139
445
  fz_catch(ctx)
140
0
    fz_rethrow(ctx);
141
142
445
  return rotate;
143
445
}
144
145
fz_rect
146
pdf_annot_display_rect(fz_context *ctx, pdf_annot *annot)
147
445
{
148
445
  fz_rect rect;
149
445
  int flags;
150
151
445
  pdf_annot_push_local_xref(ctx, annot);
152
890
  fz_try(ctx)
153
890
  {
154
445
    rect = pdf_dict_get_rect(ctx, annot->obj, PDF_NAME(Rect));
155
445
    flags = pdf_annot_flags(ctx, annot);
156
157
445
    if (flags & PDF_ANNOT_IS_NO_ZOOM)
158
0
    {
159
0
      pdf_obj *ap = pdf_annot_ap(ctx, annot);
160
0
      if (ap)
161
0
      {
162
0
        fz_rect bbox = pdf_xobject_bbox(ctx, ap);
163
0
        rect = fz_make_rect(
164
0
          rect.x0,
165
0
          rect.y1 - (bbox.y1 - bbox.y0),
166
0
          rect.x0 + (bbox.x1 - bbox.x0),
167
0
          rect.y1
168
0
        );
169
0
      }
170
0
    }
171
172
445
    if (flags & PDF_ANNOT_IS_NO_ROTATE)
173
0
    {
174
0
      int rotate = pdf_dict_get_inheritable_int(ctx, annot->page->obj, PDF_NAME(Rotate));
175
0
      if (rotate != 0)
176
0
      {
177
0
        fz_point tp = fz_make_point(rect.x0, rect.y1);
178
0
        fz_matrix rotmat = fz_translate(-tp.x, -tp.y);
179
0
        rotmat = fz_concat(rotmat, fz_rotate(rotate));
180
0
        rotmat = fz_concat(rotmat, fz_translate(tp.x, tp.y));
181
0
        rect = fz_transform_rect(rect, rotmat);
182
0
      }
183
0
    }
184
445
  }
185
890
  fz_always(ctx)
186
445
    pdf_annot_pop_local_xref(ctx, annot);
187
445
  fz_catch(ctx)
188
0
    fz_rethrow(ctx);
189
190
445
  return rect;
191
445
}
192
193
fz_matrix
194
pdf_annot_transform(fz_context *ctx, pdf_annot *annot)
195
445
{
196
445
  fz_rect bbox, rect;
197
445
  fz_matrix matrix, rotmat;
198
445
  float w, h, sx, sy, x, y;
199
445
  int rotate;
200
201
445
  pdf_obj *ap = pdf_annot_ap(ctx, annot);
202
445
  rect = pdf_annot_display_rect(ctx, annot);
203
445
  rotate = pdf_annot_display_rotate(ctx, annot);
204
205
445
  matrix = pdf_xobject_matrix(ctx, ap);
206
445
  bbox = pdf_xobject_bbox(ctx, ap);
207
445
  bbox = fz_transform_rect(bbox, matrix);
208
209
445
  w = (bbox.x1 - bbox.x0);
210
445
  h = (bbox.y1 - bbox.y0);
211
212
445
  if (rotate == 90)
213
0
  {
214
0
    rotmat = fz_pre_rotate(fz_scale(w/h, h/w), 90);
215
0
    rotmat.e = w;
216
0
  }
217
445
  else if (rotate == 180)
218
0
  {
219
0
    rotmat = fz_rotate(180);
220
0
    rotmat.e = w;
221
0
    rotmat.f = h;
222
0
  }
223
445
  else if (rotate == 270)
224
0
  {
225
0
    rotmat = fz_pre_rotate(fz_scale(w/h, h/w), 270);
226
0
    rotmat.f = h;
227
0
  }
228
445
  else
229
445
  {
230
445
    rotmat = fz_identity;
231
445
  }
232
233
445
  if (bbox.x1 == bbox.x0)
234
0
    sx = 0;
235
445
  else
236
445
    sx = (rect.x1 - rect.x0) / w;
237
445
  if (bbox.y1 == bbox.y0)
238
0
    sy = 0;
239
445
  else
240
445
    sy = (rect.y1 - rect.y0) / h;
241
445
  x = rect.x0 - (bbox.x0 * sx);
242
445
  y = rect.y0 - (bbox.y0 * sy);
243
244
445
  return fz_concat(rotmat, fz_pre_scale(fz_translate(x, y), sx, sy));
245
445
}
246
247
/*
248
  Internal function for creating a new pdf annotation.
249
*/
250
static pdf_annot *
251
pdf_new_annot(fz_context *ctx, pdf_page *page, pdf_obj *obj)
252
445
{
253
445
  pdf_annot *annot;
254
255
445
  annot = fz_malloc_struct(ctx, pdf_annot);
256
445
  annot->refs = 1;
257
445
  annot->page = page; /* only borrowed, as the page owns the annot */
258
445
  annot->obj = pdf_keep_obj(ctx, obj);
259
260
445
  return annot;
261
445
}
262
263
void
264
pdf_nuke_annots(fz_context *ctx, pdf_page *page)
265
0
{
266
0
  pdf_annot *annot;
267
0
  pdf_annot *next;
268
269
0
  for (annot = page->annots; annot; annot = next)
270
0
  {
271
0
    next = annot->next;
272
0
    pdf_drop_obj(ctx, annot->obj);
273
0
    annot->obj = NULL;
274
0
    pdf_drop_annot(ctx, annot);
275
0
  }
276
0
  for (annot = page->widgets; annot; annot = next)
277
0
  {
278
0
    next = annot->next;
279
0
    pdf_drop_obj(ctx, annot->obj);
280
0
    annot->obj = NULL;
281
0
    pdf_drop_annot(ctx, annot);
282
0
  }
283
284
0
  page->annots = NULL;
285
0
  page->widgets = NULL;
286
0
  page->annot_tailp = &page->annots;
287
0
  page->widget_tailp = &page->widgets;
288
0
}
289
290
static pdf_annot *find_and_unlink_annot_from_list(fz_context *ctx, pdf_annot **prev, pdf_obj *obj)
291
445
{
292
445
  int num = pdf_to_num(ctx, obj);
293
445
  pdf_annot *node;
294
295
445
  node = *prev;
296
445
  while (node)
297
0
  {
298
0
    if (pdf_to_num(ctx, node->obj) == num)
299
0
    {
300
0
      *prev = node->next;
301
0
      node->next = NULL;
302
0
      return node;
303
0
    }
304
0
    else
305
0
    {
306
0
      prev = &node->next;
307
0
      node = node->next;
308
0
    }
309
0
  }
310
311
445
  return NULL;
312
445
}
313
314
void
315
pdf_sync_annots(fz_context *ctx, pdf_page *page)
316
33
{
317
33
  pdf_annot *old_annots;
318
33
  pdf_annot *old_widgets;
319
33
  pdf_annot *annot, *next;
320
33
  pdf_obj *annots;
321
33
  pdf_obj *subtype;
322
33
  int i, n;
323
324
  // Save list of annots loaded last time (if any).
325
33
  old_annots = page->annots;
326
33
  old_widgets = page->widgets;
327
33
  page->annots = NULL;
328
33
  page->widgets = NULL;
329
33
  page->annot_tailp = &page->annots;
330
33
  page->widget_tailp = &page->widgets;
331
332
  // Create new list of annots (reusing old annots when possible)
333
33
  annots = pdf_dict_get(ctx, page->obj, PDF_NAME(Annots));
334
33
  n = pdf_array_len(ctx, annots);
335
582
  for (i = 0; i < n; ++i)
336
549
  {
337
549
    pdf_obj *obj = pdf_array_get(ctx, annots, i);
338
549
    if (pdf_is_dict(ctx, obj))
339
513
    {
340
513
      subtype = pdf_dict_get(ctx, obj, PDF_NAME(Subtype));
341
513
      if (pdf_name_eq(ctx, subtype, PDF_NAME(Link)))
342
0
        continue;
343
513
      if (pdf_name_eq(ctx, subtype, PDF_NAME(Popup)))
344
68
        continue;
345
346
445
      if (pdf_name_eq(ctx, subtype, PDF_NAME(Widget)))
347
360
      {
348
360
        annot = find_and_unlink_annot_from_list(ctx, &old_widgets, obj);
349
360
        if (!annot)
350
360
          annot = pdf_new_annot(ctx, page, obj);
351
360
        *page->widget_tailp = annot;
352
360
        page->widget_tailp = &annot->next;
353
360
      }
354
85
      else
355
85
      {
356
85
        annot = find_and_unlink_annot_from_list(ctx, &old_annots, obj);
357
85
        if (!annot)
358
85
          annot = pdf_new_annot(ctx, page, obj);
359
85
        *page->annot_tailp = annot;
360
85
        page->annot_tailp = &annot->next;
361
85
      }
362
445
    }
363
549
  }
364
365
  // Nuke the annot structs that are no longer used on the page
366
33
  for (annot = old_annots; annot; annot = next)
367
0
  {
368
0
    next = annot->next;
369
0
    pdf_drop_obj(ctx, annot->obj);
370
0
    annot->obj = NULL;
371
0
    pdf_drop_annot(ctx, annot);
372
0
  }
373
33
  for (annot = old_widgets; annot; annot = next)
374
0
  {
375
0
    next = annot->next;
376
0
    pdf_drop_obj(ctx, annot->obj);
377
0
    annot->obj = NULL;
378
0
    pdf_drop_annot(ctx, annot);
379
0
  }
380
33
}
381
382
static int pdf_need_appearances(fz_context *ctx, pdf_document *doc)
383
31
{
384
31
  return pdf_to_bool(ctx,
385
31
    pdf_dict_getl(ctx,
386
31
      pdf_trailer(ctx, doc),
387
31
      PDF_NAME(Root),
388
31
      PDF_NAME(AcroForm),
389
31
      PDF_NAME(NeedAppearances),
390
31
      NULL
391
31
    )
392
31
  );
393
31
}
394
395
void
396
pdf_load_annots(fz_context *ctx, pdf_page *page)
397
33
{
398
33
  pdf_sync_annots(ctx, page);
399
400
  /* We need to run a resynth pass on the annotations on this
401
   * page. That means rerunning it on the complete document. */
402
33
  if (page->annots || page->widgets)
403
31
  {
404
31
    page->doc->resynth_required = 1;
405
406
31
    if (pdf_need_appearances(ctx, page->doc))
407
0
    {
408
0
      pdf_annot *widget;
409
0
      for (widget = page->widgets; widget; widget = widget->next)
410
0
      {
411
        // request local only resynthesis
412
0
        widget->needs_new_local_ap = 1;
413
0
      }
414
0
    }
415
31
  }
416
417
  /* And actually update the page so that any annotations required
418
   * get synthesised. */
419
33
  pdf_update_page(ctx, page);
420
33
}
421
422
pdf_annot *
423
pdf_first_annot(fz_context *ctx, pdf_page *page)
424
62
{
425
62
  return page ? page->annots : NULL;
426
62
}
427
428
pdf_annot *
429
pdf_next_annot(fz_context *ctx, pdf_annot *annot)
430
170
{
431
170
  return annot ? annot->next : NULL;
432
170
}
433
434
pdf_obj *pdf_annot_obj(fz_context *ctx, pdf_annot *annot)
435
0
{
436
0
  return annot ? annot->obj : NULL;
437
0
}
438
439
pdf_page *pdf_annot_page(fz_context *ctx, pdf_annot *annot)
440
0
{
441
0
  return annot ? annot->page : NULL;
442
0
}
443
444
fz_rect
445
pdf_bound_annot(fz_context *ctx, pdf_annot *annot)
446
0
{
447
0
  fz_matrix page_ctm;
448
0
  fz_rect rect;
449
0
  pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
450
0
  rect = pdf_annot_display_rect(ctx, annot);
451
0
  return fz_transform_rect(rect, page_ctm);
452
0
}
453
454
void
455
pdf_annot_request_resynthesis(fz_context *ctx, pdf_annot *annot)
456
0
{
457
0
  if (annot == NULL)
458
0
    return;
459
460
0
  annot->needs_new_local_ap = 0;
461
0
  annot->needs_new_ap = 1;
462
0
  annot->page->doc->resynth_required = 1;
463
0
}
464
465
void
466
pdf_annot_request_synthesis(fz_context *ctx, pdf_annot *annot)
467
0
{
468
0
  if (annot == NULL)
469
0
    return;
470
0
  if (!pdf_annot_ap(ctx, annot))
471
0
    pdf_annot_request_resynthesis(ctx, annot);
472
0
}
473
474
int
475
pdf_annot_needs_resynthesis(fz_context *ctx, pdf_annot *annot)
476
890
{
477
890
  if (annot == NULL)
478
0
    return 0;
479
890
  if (annot->needs_new_local_ap)
480
0
    return -1;
481
890
  return annot->needs_new_ap;
482
890
}
483
484
void pdf_set_annot_resynthesised(fz_context *ctx, pdf_annot *annot)
485
170
{
486
170
  if (annot == NULL)
487
0
    return;
488
489
170
  if (annot->page->doc->local_xref_nesting == 0)
490
0
    annot->needs_new_local_ap = 0;
491
170
  annot->needs_new_ap = 0;
492
170
  pdf_set_annot_has_changed(ctx, annot);
493
170
}
494
495
void pdf_set_annot_has_changed(fz_context *ctx, pdf_annot *annot)
496
170
{
497
170
  if (annot == NULL)
498
0
    return;
499
500
170
  annot->has_new_ap = 1;
501
170
}
502
503
void
504
pdf_dirty_annot(fz_context *ctx, pdf_annot *annot)
505
0
{
506
0
  pdf_annot_request_resynthesis(ctx, annot);
507
0
}
508
509
const char *
510
pdf_string_from_annot_type(fz_context *ctx, enum pdf_annot_type type)
511
0
{
512
0
  switch (type)
513
0
  {
514
0
  case PDF_ANNOT_TEXT: return "Text";
515
0
  case PDF_ANNOT_LINK: return "Link";
516
0
  case PDF_ANNOT_FREE_TEXT: return "FreeText";
517
0
  case PDF_ANNOT_LINE: return "Line";
518
0
  case PDF_ANNOT_SQUARE: return "Square";
519
0
  case PDF_ANNOT_CIRCLE: return "Circle";
520
0
  case PDF_ANNOT_POLYGON: return "Polygon";
521
0
  case PDF_ANNOT_POLY_LINE: return "PolyLine";
522
0
  case PDF_ANNOT_HIGHLIGHT: return "Highlight";
523
0
  case PDF_ANNOT_UNDERLINE: return "Underline";
524
0
  case PDF_ANNOT_SQUIGGLY: return "Squiggly";
525
0
  case PDF_ANNOT_STRIKE_OUT: return "StrikeOut";
526
0
  case PDF_ANNOT_REDACT: return "Redact";
527
0
  case PDF_ANNOT_STAMP: return "Stamp";
528
0
  case PDF_ANNOT_CARET: return "Caret";
529
0
  case PDF_ANNOT_INK: return "Ink";
530
0
  case PDF_ANNOT_POPUP: return "Popup";
531
0
  case PDF_ANNOT_FILE_ATTACHMENT: return "FileAttachment";
532
0
  case PDF_ANNOT_SOUND: return "Sound";
533
0
  case PDF_ANNOT_MOVIE: return "Movie";
534
0
  case PDF_ANNOT_RICH_MEDIA: return "RichMedia";
535
0
  case PDF_ANNOT_WIDGET: return "Widget";
536
0
  case PDF_ANNOT_SCREEN: return "Screen";
537
0
  case PDF_ANNOT_PRINTER_MARK: return "PrinterMark";
538
0
  case PDF_ANNOT_TRAP_NET: return "TrapNet";
539
0
  case PDF_ANNOT_WATERMARK: return "Watermark";
540
0
  case PDF_ANNOT_3D: return "3D";
541
0
  case PDF_ANNOT_PROJECTION: return "Projection";
542
0
  default: return "UNKNOWN";
543
0
  }
544
0
}
545
546
enum pdf_annot_type
547
pdf_annot_type_from_string(fz_context *ctx, const char *subtype)
548
601
{
549
601
  if (!strcmp("Text", subtype)) return PDF_ANNOT_TEXT;
550
601
  if (!strcmp("Link", subtype)) return PDF_ANNOT_LINK;
551
601
  if (!strcmp("FreeText", subtype)) return PDF_ANNOT_FREE_TEXT;
552
599
  if (!strcmp("Line", subtype)) return PDF_ANNOT_LINE;
553
599
  if (!strcmp("Square", subtype)) return PDF_ANNOT_SQUARE;
554
599
  if (!strcmp("Circle", subtype)) return PDF_ANNOT_CIRCLE;
555
599
  if (!strcmp("Polygon", subtype)) return PDF_ANNOT_POLYGON;
556
599
  if (!strcmp("PolyLine", subtype)) return PDF_ANNOT_POLY_LINE;
557
599
  if (!strcmp("Highlight", subtype)) return PDF_ANNOT_HIGHLIGHT;
558
541
  if (!strcmp("Underline", subtype)) return PDF_ANNOT_UNDERLINE;
559
541
  if (!strcmp("Squiggly", subtype)) return PDF_ANNOT_SQUIGGLY;
560
541
  if (!strcmp("StrikeOut", subtype)) return PDF_ANNOT_STRIKE_OUT;
561
541
  if (!strcmp("Redact", subtype)) return PDF_ANNOT_REDACT;
562
541
  if (!strcmp("Stamp", subtype)) return PDF_ANNOT_STAMP;
563
536
  if (!strcmp("Caret", subtype)) return PDF_ANNOT_CARET;
564
536
  if (!strcmp("Ink", subtype)) return PDF_ANNOT_INK;
565
512
  if (!strcmp("Popup", subtype)) return PDF_ANNOT_POPUP;
566
512
  if (!strcmp("FileAttachment", subtype)) return PDF_ANNOT_FILE_ATTACHMENT;
567
512
  if (!strcmp("Sound", subtype)) return PDF_ANNOT_SOUND;
568
512
  if (!strcmp("Movie", subtype)) return PDF_ANNOT_MOVIE;
569
512
  if (!strcmp("RichMedia", subtype)) return PDF_ANNOT_RICH_MEDIA;
570
512
  if (!strcmp("Widget", subtype)) return PDF_ANNOT_WIDGET;
571
0
  if (!strcmp("Screen", subtype)) return PDF_ANNOT_SCREEN;
572
0
  if (!strcmp("PrinterMark", subtype)) return PDF_ANNOT_PRINTER_MARK;
573
0
  if (!strcmp("TrapNet", subtype)) return PDF_ANNOT_TRAP_NET;
574
0
  if (!strcmp("Watermark", subtype)) return PDF_ANNOT_WATERMARK;
575
0
  if (!strcmp("3D", subtype)) return PDF_ANNOT_3D;
576
0
  if (!strcmp("Projection", subtype)) return PDF_ANNOT_PROJECTION;
577
0
  return PDF_ANNOT_UNKNOWN;
578
0
}
579
580
static void
581
begin_annot_op(fz_context *ctx, pdf_annot *annot, const char *op)
582
156
{
583
156
  if (!annot->page)
584
0
    fz_throw(ctx, FZ_ERROR_ARGUMENT, "annotation not bound to any page");
585
586
156
  pdf_begin_operation(ctx, annot->page->doc, op);
587
156
}
588
589
static void
590
end_annot_op(fz_context *ctx, pdf_annot *annot)
591
156
{
592
156
  pdf_end_operation(ctx, annot->page->doc);
593
156
}
594
595
static void
596
abandon_annot_op(fz_context *ctx, pdf_annot *annot)
597
0
{
598
0
  pdf_abandon_operation(ctx, annot->page->doc);
599
0
}
600
601
static int is_allowed_subtype(fz_context *ctx, pdf_annot *annot, pdf_obj *property, pdf_obj **allowed)
602
486
{
603
486
  pdf_obj *subtype;
604
605
486
  subtype = pdf_dict_get(ctx, annot->obj, PDF_NAME(Subtype));
606
2.05k
  while (*allowed) {
607
2.05k
    if (pdf_name_eq(ctx, subtype, *allowed))
608
486
      return 1;
609
1.56k
    allowed++;
610
1.56k
  }
611
612
0
  return 0;
613
486
}
614
615
static int is_allowed_subtype_wrap(fz_context *ctx, pdf_annot *annot, pdf_obj *property, pdf_obj **allowed)
616
0
{
617
0
  int ret;
618
619
0
  pdf_annot_push_local_xref(ctx, annot);
620
621
0
  fz_try(ctx)
622
0
    ret = is_allowed_subtype(ctx, annot, property, allowed);
623
0
  fz_always(ctx)
624
0
    pdf_annot_pop_local_xref(ctx, annot);
625
0
  fz_catch(ctx)
626
0
    fz_rethrow(ctx);
627
628
0
  return ret;
629
0
}
630
631
static void check_allowed_subtypes(fz_context *ctx, pdf_annot *annot, pdf_obj *property, pdf_obj **allowed)
632
486
{
633
486
  pdf_obj *subtype;
634
635
486
  subtype = pdf_dict_get(ctx, annot->obj, PDF_NAME(Subtype));
636
486
  if (!is_allowed_subtype(ctx, annot, property, allowed))
637
0
    fz_throw(ctx, FZ_ERROR_ARGUMENT, "%s annotations have no %s property", pdf_to_name(ctx, subtype), pdf_to_name(ctx, property));
638
486
}
639
640
pdf_annot *
641
pdf_create_annot_raw(fz_context *ctx, pdf_page *page, enum pdf_annot_type type)
642
0
{
643
0
  pdf_annot *annot = NULL;
644
0
  pdf_document *doc = page->doc;
645
0
  pdf_obj *annot_obj = pdf_new_dict(ctx, doc, 0);
646
0
  pdf_obj *ind_obj = NULL;
647
0
  pdf_obj *free_arr = NULL;
648
649
0
  fz_var(annot);
650
0
  fz_var(ind_obj);
651
0
  fz_var(free_arr);
652
0
  fz_try(ctx)
653
0
  {
654
0
    int ind_obj_num;
655
0
    const char *type_str;
656
0
    pdf_obj *annot_arr;
657
658
0
    type_str = pdf_string_from_annot_type(ctx, type);
659
0
    if (type == PDF_ANNOT_UNKNOWN)
660
0
      fz_throw(ctx, FZ_ERROR_ARGUMENT, "cannot create unknown annotation");
661
662
0
    annot_arr = pdf_dict_get(ctx, page->obj, PDF_NAME(Annots));
663
0
    if (!pdf_is_array(ctx, annot_arr))
664
0
      annot_arr = pdf_dict_put_array(ctx, page->obj, PDF_NAME(Annots), 0);
665
0
    else if (pdf_is_indirect(ctx, annot_arr))
666
0
    {
667
0
      free_arr = annot_arr = pdf_copy_array(ctx, annot_arr);
668
0
      pdf_dict_put(ctx, page->obj, PDF_NAME(Annots), annot_arr);
669
0
    }
670
671
0
    pdf_dict_put(ctx, annot_obj, PDF_NAME(Type), PDF_NAME(Annot));
672
0
    pdf_dict_put_name(ctx, annot_obj, PDF_NAME(Subtype), type_str);
673
674
    /*
675
      Both annotation object and annotation structure are now created.
676
      Insert the object in the hierarchy and the structure in the
677
      page's array.
678
    */
679
0
    ind_obj_num = pdf_create_object(ctx, doc);
680
0
    pdf_update_object(ctx, doc, ind_obj_num, annot_obj);
681
0
    ind_obj = pdf_new_indirect(ctx, doc, ind_obj_num, 0);
682
0
    pdf_array_push(ctx, annot_arr, ind_obj);
683
684
0
    annot = pdf_new_annot(ctx, page, ind_obj);
685
686
    /*
687
      Linking must be done after any call that might throw because
688
      pdf_drop_annots below actually frees a list. Put the new annot
689
      at the end of the list, so that it will be drawn last.
690
    */
691
0
    if (type == PDF_ANNOT_WIDGET)
692
0
    {
693
0
      *page->widget_tailp = annot;
694
0
      page->widget_tailp = &annot->next;
695
0
    }
696
0
    else
697
0
    {
698
0
      *page->annot_tailp = annot;
699
0
      page->annot_tailp = &annot->next;
700
0
    }
701
0
  }
702
0
  fz_always(ctx)
703
0
  {
704
0
    pdf_drop_obj(ctx, free_arr);
705
0
    pdf_drop_obj(ctx, annot_obj);
706
0
    pdf_drop_obj(ctx, ind_obj);
707
0
  }
708
0
  fz_catch(ctx)
709
0
  {
710
0
    pdf_drop_annots(ctx, annot);
711
0
    fz_rethrow(ctx);
712
0
  }
713
714
0
  return pdf_keep_annot(ctx, annot);
715
0
}
716
717
fz_link *
718
pdf_create_link(fz_context *ctx, pdf_page *page, fz_rect bbox, const char *uri)
719
0
{
720
0
  fz_link *link = NULL;
721
0
  pdf_document *doc = page->doc;
722
0
  pdf_obj *annot_obj = pdf_new_dict(ctx, doc, 0);
723
0
  pdf_obj *ind_obj = NULL;
724
0
  pdf_obj *bs = NULL;
725
0
  pdf_obj *a = NULL;
726
0
  fz_link **linkp;
727
0
  fz_rect page_mediabox;
728
0
  fz_matrix page_ctm;
729
0
  fz_rect rect;
730
0
  pdf_obj *free_arr = NULL;
731
732
0
  fz_var(link);
733
0
  fz_var(ind_obj);
734
0
  fz_var(bs);
735
0
  fz_var(a);
736
0
  fz_var(free_arr);
737
738
0
  pdf_begin_operation(ctx, page->doc, "Create Link");
739
740
0
  fz_try(ctx)
741
0
  {
742
0
    int ind_obj_num;
743
0
    pdf_obj *annot_arr;
744
745
0
    pdf_page_transform(ctx, page, &page_mediabox, &page_ctm);
746
0
    page_ctm = fz_invert_matrix(page_ctm);
747
0
    rect = fz_transform_rect(bbox, page_ctm);
748
749
0
    annot_arr = pdf_dict_get(ctx, page->obj, PDF_NAME(Annots));
750
0
    if (!pdf_is_array(ctx, annot_arr))
751
0
      annot_arr = pdf_dict_put_array(ctx, page->obj, PDF_NAME(Annots), 0);
752
0
    else if (pdf_is_indirect(ctx, annot_arr))
753
0
    {
754
0
      free_arr = annot_arr = pdf_copy_array(ctx, annot_arr);
755
0
      pdf_dict_put(ctx, page->obj, PDF_NAME(Annots), annot_arr);
756
0
    }
757
758
0
    pdf_dict_put(ctx, annot_obj, PDF_NAME(Type), PDF_NAME(Annot));
759
0
    pdf_dict_put(ctx, annot_obj, PDF_NAME(Subtype), PDF_NAME(Link));
760
0
    pdf_dict_put_rect(ctx, annot_obj, PDF_NAME(Rect), rect);
761
762
0
    bs = pdf_dict_put_dict(ctx, annot_obj, PDF_NAME(BS), 4);
763
0
    pdf_dict_put(ctx, bs, PDF_NAME(S), PDF_NAME(S));
764
0
    pdf_dict_put(ctx, bs, PDF_NAME(Type), PDF_NAME(Border));
765
0
    pdf_dict_put_int(ctx, bs, PDF_NAME(W), 0);
766
767
0
    pdf_dict_put_drop(ctx, annot_obj, PDF_NAME(A),
768
0
      pdf_new_action_from_link(ctx, doc, uri));
769
770
    /*
771
      Both annotation object and annotation structure are now created.
772
      Insert the object in the hierarchy and the structure in the
773
      page's array.
774
    */
775
0
    ind_obj_num = pdf_create_object(ctx, doc);
776
0
    pdf_update_object(ctx, doc, ind_obj_num, annot_obj);
777
0
    ind_obj = pdf_new_indirect(ctx, doc, ind_obj_num, 0);
778
0
    pdf_array_push(ctx, annot_arr, ind_obj);
779
780
0
    link = (fz_link *) pdf_new_link(ctx, page, bbox, uri, annot_obj);
781
782
0
    linkp = &page->links;
783
784
0
    while (*linkp != NULL)
785
0
      linkp = &(*linkp)->next;
786
787
0
    *linkp = link;
788
0
    pdf_end_operation(ctx, page->doc);
789
0
  }
790
0
  fz_always(ctx)
791
0
  {
792
0
    pdf_drop_obj(ctx, free_arr);
793
0
    pdf_drop_obj(ctx, annot_obj);
794
0
    pdf_drop_obj(ctx, ind_obj);
795
0
  }
796
0
  fz_catch(ctx)
797
0
  {
798
0
    pdf_abandon_operation(ctx, page->doc);
799
0
    fz_rethrow(ctx);
800
0
  }
801
802
0
  return fz_keep_link(ctx, link);
803
0
}
804
805
void pdf_delete_link(fz_context *ctx, pdf_page *page, fz_link *link)
806
0
{
807
0
  fz_link **linkptr;
808
0
  pdf_obj *annots;
809
0
  int i;
810
811
0
  if (link == NULL || page == NULL || page != ((pdf_link *) link)->page)
812
0
    return;
813
814
0
  for (linkptr = &page->links; *linkptr; linkptr = &((*linkptr)->next))
815
0
  {
816
0
    if (*linkptr == link)
817
0
      break;
818
0
  }
819
820
0
  if (*linkptr == NULL)
821
0
    return;
822
823
  /* Link may no longer borrow page pointer, since they are separated */
824
0
  ((pdf_link *) link)->page = NULL;
825
826
0
  pdf_begin_operation(ctx, page->doc, "Delete Link");
827
828
0
  fz_try(ctx)
829
0
  {
830
831
0
    annots = pdf_dict_get(ctx, page->obj, PDF_NAME(Annots));
832
0
    i = pdf_array_find(ctx, annots, ((pdf_link *) link)->obj);
833
0
    if (i >= 0)
834
0
      pdf_array_delete(ctx, annots, i);
835
0
    *linkptr = link->next;
836
0
    link->next = NULL;
837
0
    fz_drop_link(ctx, link);
838
0
    pdf_end_operation(ctx, page->doc);
839
0
  }
840
0
  fz_catch(ctx)
841
0
  {
842
0
    pdf_abandon_operation(ctx, page->doc);
843
0
    fz_rethrow(ctx);
844
0
  }
845
0
}
846
847
static pdf_obj *
848
pdf_add_popup_annot(fz_context *ctx, pdf_annot *annot)
849
0
{
850
0
  pdf_obj *annots, *popup;
851
852
0
  popup = pdf_dict_get(ctx, annot->obj, PDF_NAME(Popup));
853
0
  if (popup)
854
0
    return popup;
855
856
0
  annots = pdf_dict_get(ctx, annot->page->obj, PDF_NAME(Annots));
857
0
  if (!annots)
858
0
    return NULL;
859
860
0
  popup = pdf_add_new_dict(ctx, annot->page->doc, 4);
861
0
  pdf_array_push_drop(ctx, annots, popup);
862
863
0
  pdf_dict_put(ctx, popup, PDF_NAME(Type), PDF_NAME(Annot));
864
0
  pdf_dict_put(ctx, popup, PDF_NAME(Subtype), PDF_NAME(Popup));
865
0
  pdf_dict_put(ctx, popup, PDF_NAME(Parent), annot->obj);
866
0
  pdf_dict_put_rect(ctx, popup, PDF_NAME(Rect), fz_make_rect(0,0,0,0));
867
868
0
  pdf_dict_put(ctx, annot->obj, PDF_NAME(Popup), popup);
869
870
0
  return popup;
871
0
}
872
873
static pdf_obj *popup_subtypes[] = {
874
  PDF_NAME(Text),
875
  PDF_NAME(FreeText),
876
  PDF_NAME(Line),
877
  PDF_NAME(Square),
878
  PDF_NAME(Circle),
879
  PDF_NAME(Polygon),
880
  PDF_NAME(PolyLine),
881
  PDF_NAME(Highlight),
882
  PDF_NAME(Underline),
883
  PDF_NAME(Squiggly),
884
  PDF_NAME(StrikeOut),
885
  PDF_NAME(Stamp),
886
  PDF_NAME(Caret),
887
  PDF_NAME(Ink),
888
  PDF_NAME(FileAttachment),
889
  PDF_NAME(Sound),
890
  PDF_NAME(Redact),
891
  NULL,
892
};
893
894
int
895
pdf_annot_has_popup(fz_context *ctx, pdf_annot *annot)
896
0
{
897
0
  return is_allowed_subtype_wrap(ctx, annot, PDF_NAME(Popup), popup_subtypes);
898
0
}
899
900
void pdf_set_annot_popup(fz_context *ctx, pdf_annot *annot, fz_rect rect)
901
0
{
902
0
  fz_matrix page_ctm, inv_page_ctm;
903
0
  pdf_obj *popup;
904
905
0
  begin_annot_op(ctx, annot, "Set popup");
906
907
0
  fz_try(ctx)
908
0
  {
909
0
    check_allowed_subtypes(ctx, annot, PDF_NAME(Popup), popup_subtypes);
910
0
    pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
911
0
    inv_page_ctm = fz_invert_matrix(page_ctm);
912
0
    rect = fz_transform_rect(rect, inv_page_ctm);
913
0
    popup = pdf_add_popup_annot(ctx, annot);
914
0
    pdf_dict_put_rect(ctx, popup, PDF_NAME(Rect), rect);
915
0
    end_annot_op(ctx, annot);
916
0
  }
917
0
  fz_catch(ctx)
918
0
  {
919
0
    abandon_annot_op(ctx, annot);
920
0
    fz_rethrow(ctx);
921
0
  }
922
923
0
  pdf_dirty_annot(ctx, annot);
924
0
}
925
926
fz_rect pdf_annot_popup(fz_context *ctx, pdf_annot *annot)
927
0
{
928
0
  fz_matrix page_ctm;
929
0
  fz_rect rect;
930
0
  pdf_obj *popup;
931
932
0
  pdf_annot_push_local_xref(ctx, annot);
933
934
0
  fz_try(ctx)
935
0
  {
936
0
    check_allowed_subtypes(ctx, annot, PDF_NAME(Popup), popup_subtypes);
937
0
    pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
938
0
    popup = pdf_dict_get(ctx, annot->obj, PDF_NAME(Popup));
939
0
    rect = pdf_dict_get_rect(ctx, popup, PDF_NAME(Rect));
940
0
    rect = fz_transform_rect(rect, page_ctm);
941
0
  }
942
0
  fz_always(ctx)
943
0
    pdf_annot_pop_local_xref(ctx, annot);
944
0
  fz_catch(ctx)
945
0
    fz_rethrow(ctx);
946
947
0
  return rect;
948
0
}
949
950
pdf_annot *
951
pdf_create_annot(fz_context *ctx, pdf_page *page, enum pdf_annot_type type)
952
0
{
953
0
  static const float black[3] = { 0, 0, 0 };
954
0
  static const float red[3] = { 1, 0, 0 };
955
0
  static const float green[3] = { 0, 1, 0 };
956
0
  static const float blue[3] = { 0, 0, 1 };
957
0
  static const float yellow[3] = { 1, 1, 0 };
958
0
  static const float magenta[3] = { 1, 0, 1 };
959
960
0
  int flags = PDF_ANNOT_IS_PRINT; /* Make printable as default */
961
962
0
  pdf_annot *annot = NULL;
963
964
0
  fz_var(annot);
965
966
0
  pdf_begin_operation(ctx, page->doc, "Create Annotation");
967
968
0
  fz_try(ctx)
969
0
  {
970
0
    annot = pdf_create_annot_raw(ctx, page, type);
971
972
0
    switch (type)
973
0
    {
974
0
    default:
975
0
      break;
976
977
0
    case PDF_ANNOT_TEXT:
978
0
    case PDF_ANNOT_FILE_ATTACHMENT:
979
0
    case PDF_ANNOT_SOUND:
980
0
      {
981
0
        fz_rect icon_rect = { 12, 12, 12+16, 12+16 };
982
0
        flags = PDF_ANNOT_IS_PRINT | PDF_ANNOT_IS_NO_ZOOM | PDF_ANNOT_IS_NO_ROTATE;
983
0
        pdf_set_annot_rect(ctx, annot, icon_rect);
984
0
        pdf_set_annot_color(ctx, annot, 3, yellow);
985
0
        pdf_set_annot_popup(ctx, annot, fz_make_rect(32, 12, 32+200, 12+100));
986
0
      }
987
0
      break;
988
989
0
    case PDF_ANNOT_FREE_TEXT:
990
0
      {
991
0
        fz_rect text_rect = { 12, 12, 12+200, 12+100 };
992
993
        /* Use undocumented Adobe property to match page rotation. */
994
0
        int rot = pdf_dict_get_inheritable_int(ctx, page->obj, PDF_NAME(Rotate));
995
0
        if (rot != 0)
996
0
          pdf_dict_put_int(ctx, annot->obj, PDF_NAME(Rotate), rot);
997
998
0
        pdf_set_annot_rect(ctx, annot, text_rect);
999
0
        pdf_set_annot_border_width(ctx, annot, 0);
1000
0
        pdf_set_annot_default_appearance(ctx, annot, "Helv", 12, nelem(black), black);
1001
0
      }
1002
0
      break;
1003
1004
0
    case PDF_ANNOT_STAMP:
1005
0
      {
1006
0
        fz_rect stamp_rect = { 12, 12, 12+190, 12+50 };
1007
0
        pdf_set_annot_rect(ctx, annot, stamp_rect);
1008
0
        pdf_set_annot_color(ctx, annot, 3, red);
1009
0
        pdf_set_annot_icon_name(ctx, annot, "Draft");
1010
0
      }
1011
0
      break;
1012
1013
0
    case PDF_ANNOT_CARET:
1014
0
      {
1015
0
        fz_rect caret_rect = { 12, 12, 12+18, 12+15 };
1016
0
        pdf_set_annot_rect(ctx, annot, caret_rect);
1017
0
        pdf_set_annot_color(ctx, annot, 3, blue);
1018
0
      }
1019
0
      break;
1020
1021
0
    case PDF_ANNOT_LINE:
1022
0
      {
1023
0
        fz_point a = { 12, 12 }, b = { 12 + 100, 12 + 50 };
1024
0
        pdf_set_annot_line(ctx, annot, a, b);
1025
0
        pdf_set_annot_border_width(ctx, annot, 1);
1026
0
        pdf_set_annot_color(ctx, annot, 3, red);
1027
0
      }
1028
0
      break;
1029
1030
0
    case PDF_ANNOT_SQUARE:
1031
0
    case PDF_ANNOT_CIRCLE:
1032
0
      {
1033
0
        fz_rect shape_rect = { 12, 12, 12+100, 12+50 };
1034
0
        fz_rect rd = { 0.5, 0.5, 0.5, 0.5 };
1035
0
        pdf_set_annot_rect(ctx, annot, shape_rect);
1036
0
        pdf_dict_put_rect(ctx, annot->obj, PDF_NAME(RD), rd);
1037
0
        pdf_set_annot_border_width(ctx, annot, 1);
1038
0
        pdf_set_annot_color(ctx, annot, 3, red);
1039
0
      }
1040
0
      break;
1041
1042
0
    case PDF_ANNOT_POLYGON:
1043
0
    case PDF_ANNOT_POLY_LINE:
1044
0
    case PDF_ANNOT_INK:
1045
0
      {
1046
0
        fz_rect rd = { 0.5, 0.5, 0.5, 0.5 };
1047
0
        pdf_set_annot_border_width(ctx, annot, 1);
1048
0
        pdf_dict_put_rect(ctx, annot->obj, PDF_NAME(RD), rd);
1049
0
        pdf_set_annot_color(ctx, annot, 3, red);
1050
0
      }
1051
0
      break;
1052
1053
0
    case PDF_ANNOT_HIGHLIGHT:
1054
0
      pdf_set_annot_color(ctx, annot, 3, yellow);
1055
0
      break;
1056
0
    case PDF_ANNOT_UNDERLINE:
1057
0
      pdf_set_annot_color(ctx, annot, 3, green);
1058
0
      break;
1059
0
    case PDF_ANNOT_STRIKE_OUT:
1060
0
      pdf_set_annot_color(ctx, annot, 3, red);
1061
0
      break;
1062
0
    case PDF_ANNOT_SQUIGGLY:
1063
0
      pdf_set_annot_color(ctx, annot, 3, magenta);
1064
0
      break;
1065
0
    }
1066
1067
0
    pdf_dict_put(ctx, annot->obj, PDF_NAME(P), page->obj);
1068
0
    pdf_dict_put_int(ctx, annot->obj, PDF_NAME(F), flags);
1069
0
    pdf_end_operation(ctx, page->doc);
1070
0
  }
1071
0
  fz_catch(ctx)
1072
0
  {
1073
0
    pdf_drop_annot(ctx, annot);
1074
0
    pdf_abandon_operation(ctx, page->doc);
1075
0
    fz_rethrow(ctx);
1076
0
  }
1077
1078
0
  return annot;
1079
0
}
1080
1081
static int
1082
remove_from_tree(fz_context *ctx, pdf_obj *arr, pdf_obj *item, pdf_cycle_list *cycle_up)
1083
0
{
1084
0
  pdf_cycle_list cycle;
1085
0
  int i, n, res = 0;
1086
1087
0
  if (arr == NULL || pdf_cycle(ctx, &cycle, cycle_up, arr))
1088
0
    return 0;
1089
1090
0
  n = pdf_array_len(ctx, arr);
1091
0
  for (i = 0; i < n; ++i)
1092
0
  {
1093
0
    pdf_obj *obj = pdf_array_get(ctx, arr, i);
1094
0
    if (!pdf_objcmp(ctx, obj, item))
1095
0
    {
1096
0
      pdf_array_delete(ctx, arr, i);
1097
0
      res = 1;
1098
0
      break;
1099
0
    }
1100
1101
0
    if (remove_from_tree(ctx, pdf_dict_get(ctx, obj, PDF_NAME(Kids)), item, &cycle))
1102
0
    {
1103
0
      res = 1;
1104
0
      break;
1105
0
    }
1106
0
  }
1107
1108
0
  return res;
1109
0
}
1110
1111
void
1112
pdf_delete_annot(fz_context *ctx, pdf_page *page, pdf_annot *annot)
1113
0
{
1114
0
  pdf_document *doc;
1115
0
  pdf_annot **annotptr;
1116
0
  pdf_obj *annot_arr, *popup;
1117
0
  int i;
1118
0
  int is_widget = 0;
1119
1120
0
  if (annot == NULL || page == NULL || page != annot->page)
1121
0
    return;
1122
1123
0
  doc = page->doc;
1124
1125
  /* Look for the annot in the page's list */
1126
0
  for (annotptr = &page->annots; *annotptr; annotptr = &(*annotptr)->next)
1127
0
  {
1128
0
    if (*annotptr == annot)
1129
0
      break;
1130
0
  }
1131
1132
0
  if (*annotptr == NULL)
1133
0
  {
1134
0
    is_widget = 1;
1135
1136
    /* Look also in the widget list*/
1137
0
    for (annotptr = &page->widgets; *annotptr; annotptr = &(*annotptr)->next)
1138
0
    {
1139
0
      if (*annotptr == annot)
1140
0
        break;
1141
0
    }
1142
0
  }
1143
1144
  /* Check the passed annotation was of this page */
1145
0
  if (*annotptr == NULL)
1146
0
    return;
1147
1148
0
  pdf_begin_operation(ctx, page->doc, "Delete Annotation");
1149
1150
  /* Remove annot from page's list */
1151
0
  *annotptr = annot->next;
1152
1153
  /* Annot may no longer borrow page pointer, since they are separated */
1154
0
  annot->page = NULL;
1155
1156
  /* If the removed annotation was the last in the list adjust the end pointer */
1157
0
  if (*annotptr == NULL)
1158
0
  {
1159
0
    if (is_widget)
1160
0
      page->widget_tailp = annotptr;
1161
0
    else
1162
0
      page->annot_tailp = annotptr;
1163
0
  }
1164
1165
0
  fz_try(ctx)
1166
0
  {
1167
    /* Remove the annot from the "Annots" array. */
1168
0
    annot_arr = pdf_dict_get(ctx, page->obj, PDF_NAME(Annots));
1169
0
    i = pdf_array_find(ctx, annot_arr, annot->obj);
1170
0
    if (i >= 0)
1171
0
      pdf_array_delete(ctx, annot_arr, i);
1172
1173
    /* Remove the associated Popup annotation from the Annots array */
1174
0
    popup = pdf_dict_get(ctx, annot->obj, PDF_NAME(Popup));
1175
0
    if (popup)
1176
0
    {
1177
0
      i = pdf_array_find(ctx, annot_arr, popup);
1178
0
      if (i >= 0)
1179
0
        pdf_array_delete(ctx, annot_arr, i);
1180
0
    }
1181
1182
    /* For a widget, remove also from the AcroForm tree */
1183
0
    if (is_widget)
1184
0
    {
1185
0
      pdf_obj *root = pdf_dict_get(ctx, pdf_trailer(ctx, doc), PDF_NAME(Root));
1186
0
      pdf_obj *acroform = pdf_dict_get(ctx, root, PDF_NAME(AcroForm));
1187
0
      pdf_obj *fields = pdf_dict_get(ctx, acroform, PDF_NAME(Fields));
1188
0
      (void)remove_from_tree(ctx, fields, annot->obj, NULL);
1189
0
    }
1190
1191
    /* The garbage collection pass when saving will remove the annot object,
1192
     * removing it here may break files if multiple pages use the same annot. */
1193
0
    pdf_end_operation(ctx, page->doc);
1194
0
  }
1195
0
  fz_always(ctx)
1196
0
  {
1197
    /* Drop the reference to annot previously held by the page list. */
1198
0
    pdf_drop_annot(ctx, annot);
1199
0
  }
1200
0
  fz_catch(ctx)
1201
0
  {
1202
0
    pdf_abandon_operation(ctx, page->doc);
1203
0
    fz_rethrow(ctx);
1204
0
  }
1205
0
}
1206
1207
enum pdf_annot_type
1208
pdf_annot_type(fz_context *ctx, pdf_annot *annot)
1209
601
{
1210
601
  enum pdf_annot_type ret;
1211
1212
601
  pdf_annot_push_local_xref(ctx, annot);
1213
1214
1.20k
  fz_try(ctx)
1215
1.20k
  {
1216
601
    const char *subtype = pdf_dict_get_name(ctx, annot->obj, PDF_NAME(Subtype));
1217
601
    ret = pdf_annot_type_from_string(ctx, subtype);
1218
601
  }
1219
1.20k
  fz_always(ctx)
1220
601
    pdf_annot_pop_local_xref(ctx, annot);
1221
601
  fz_catch(ctx)
1222
0
    fz_rethrow(ctx);
1223
1224
601
  return ret;
1225
601
}
1226
1227
int
1228
pdf_annot_flags(fz_context *ctx, pdf_annot *annot)
1229
3.95k
{
1230
3.95k
  int flags;
1231
3.95k
  pdf_annot_push_local_xref(ctx, annot);
1232
1233
7.90k
  fz_try(ctx)
1234
7.90k
  {
1235
3.95k
    flags = pdf_dict_get_int(ctx, annot->obj, PDF_NAME(F));
1236
    // Text annotations always behave as if NoRotate and NoZoom is set!
1237
3.95k
    if (pdf_dict_get(ctx, annot->obj, PDF_NAME(Subtype)) == PDF_NAME(Text))
1238
0
      flags |= PDF_ANNOT_IS_NO_ROTATE | PDF_ANNOT_IS_NO_ZOOM;
1239
3.95k
  }
1240
7.90k
  fz_always(ctx)
1241
3.95k
    pdf_annot_pop_local_xref(ctx, annot);
1242
3.95k
  fz_catch(ctx)
1243
0
    fz_rethrow(ctx);
1244
1245
3.95k
  return flags;
1246
3.95k
}
1247
1248
void
1249
pdf_set_annot_flags(fz_context *ctx, pdf_annot *annot, int flags)
1250
0
{
1251
0
  begin_annot_op(ctx, annot, "Set flags");
1252
1253
0
  fz_try(ctx)
1254
0
  {
1255
0
    pdf_dict_put_int(ctx, annot->obj, PDF_NAME(F), flags);
1256
0
    end_annot_op(ctx, annot);
1257
0
  }
1258
0
  fz_catch(ctx)
1259
0
  {
1260
0
    abandon_annot_op(ctx, annot);
1261
0
    fz_rethrow(ctx);
1262
0
  }
1263
1264
0
  pdf_dirty_annot(ctx, annot);
1265
0
}
1266
1267
static pdf_obj *rect_subtypes[] = {
1268
  PDF_NAME(Text),
1269
  PDF_NAME(FreeText),
1270
  PDF_NAME(Square),
1271
  PDF_NAME(Circle),
1272
  PDF_NAME(Redact),
1273
  PDF_NAME(Stamp),
1274
  PDF_NAME(Caret),
1275
  PDF_NAME(Popup),
1276
  PDF_NAME(FileAttachment),
1277
  PDF_NAME(Sound),
1278
  PDF_NAME(Movie),
1279
  PDF_NAME(Widget),
1280
  PDF_NAME(3D),
1281
  PDF_NAME(RichMedia),
1282
  NULL,
1283
};
1284
1285
int
1286
pdf_annot_has_rect(fz_context *ctx, pdf_annot *annot)
1287
0
{
1288
  /* True for annotations where the user can manipulate the size or location
1289
   * of the annotation through the Rect.
1290
   * False for annotations where the Rect is computed from other
1291
   * annotation data such as InkList, QuadPoints, and Vertices.
1292
   */
1293
0
  return is_allowed_subtype_wrap(ctx, annot, PDF_NAME(Rect), rect_subtypes);
1294
0
}
1295
1296
fz_rect
1297
pdf_annot_rect(fz_context *ctx, pdf_annot *annot)
1298
0
{
1299
0
  fz_matrix page_ctm;
1300
0
  fz_rect rect;
1301
0
  fz_rect rd;
1302
1303
0
  pdf_annot_push_local_xref(ctx, annot);
1304
0
  fz_try(ctx)
1305
0
  {
1306
0
    check_allowed_subtypes(ctx, annot, PDF_NAME(Rect), rect_subtypes);
1307
0
    pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
1308
0
    rect = pdf_dict_get_rect(ctx, annot->obj, PDF_NAME(Rect));
1309
1310
    /* remove RD adjustment from bounding box to get design box */
1311
0
    rd = pdf_annot_rect_diff(ctx, annot);
1312
0
    rect.x0 += rd.x0;
1313
0
    rect.x1 -= rd.x1;
1314
0
    rect.y0 += rd.y0;
1315
0
    rect.y1 -= rd.y1;
1316
0
  }
1317
0
  fz_always(ctx)
1318
0
    pdf_annot_pop_local_xref(ctx, annot);
1319
0
  fz_catch(ctx)
1320
0
    fz_rethrow(ctx);
1321
1322
0
  return fz_transform_rect(rect, page_ctm);
1323
0
}
1324
1325
void
1326
pdf_set_annot_rect(fz_context *ctx, pdf_annot *annot, fz_rect rect)
1327
0
{
1328
0
  fz_matrix page_ctm, inv_page_ctm;
1329
0
  fz_rect rd;
1330
1331
0
  begin_annot_op(ctx, annot, "Set rectangle");
1332
1333
0
  fz_try(ctx)
1334
0
  {
1335
0
    check_allowed_subtypes(ctx, annot, PDF_NAME(Rect), rect_subtypes);
1336
1337
0
    pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
1338
0
    inv_page_ctm = fz_invert_matrix(page_ctm);
1339
0
    rect = fz_transform_rect(rect, inv_page_ctm);
1340
1341
    /* add RD adjustment to design box to get bounding box */
1342
0
    rd = pdf_annot_rect_diff(ctx, annot);
1343
0
    rect.x0 -= rd.x0;
1344
0
    rect.x1 += rd.x1;
1345
0
    rect.y0 -= rd.y0;
1346
0
    rect.y1 += rd.y1;
1347
1348
0
    pdf_dict_put_rect(ctx, annot->obj, PDF_NAME(Rect), rect);
1349
1350
    /* recalculate callout line as necessary */
1351
0
    if (pdf_annot_has_callout(ctx, annot))
1352
0
    {
1353
0
      pdf_set_annot_callout_point(ctx, annot, pdf_annot_callout_point(ctx, annot));
1354
0
    }
1355
1356
0
    end_annot_op(ctx, annot);
1357
0
  }
1358
0
  fz_catch(ctx)
1359
0
  {
1360
0
    abandon_annot_op(ctx, annot);
1361
0
    fz_rethrow(ctx);
1362
0
  }
1363
1364
0
  pdf_dirty_annot(ctx, annot);
1365
0
}
1366
1367
const char *
1368
pdf_annot_contents(fz_context *ctx, pdf_annot *annot)
1369
0
{
1370
0
  return pdf_dict_get_text_string(ctx, annot->obj, PDF_NAME(Contents));
1371
0
}
1372
1373
void
1374
pdf_set_annot_contents(fz_context *ctx, pdf_annot *annot, const char *text)
1375
0
{
1376
0
  begin_annot_op(ctx, annot, "Set contents");
1377
1378
0
  fz_try(ctx)
1379
0
  {
1380
0
    pdf_dict_put_text_string(ctx, annot->obj, PDF_NAME(Contents), text);
1381
0
    pdf_dict_del(ctx, annot->obj, PDF_NAME(RC)); /* not supported */
1382
0
    pdf_dirty_annot(ctx, annot);
1383
0
    end_annot_op(ctx, annot);
1384
0
  }
1385
0
  fz_catch(ctx)
1386
0
  {
1387
0
    abandon_annot_op(ctx, annot);
1388
0
    fz_rethrow(ctx);
1389
0
  }
1390
1391
0
  pdf_dirty_annot(ctx, annot);
1392
0
}
1393
1394
static pdf_obj *markup_subtypes[] = {
1395
  PDF_NAME(Text),
1396
  PDF_NAME(FreeText),
1397
  PDF_NAME(Line),
1398
  PDF_NAME(Square),
1399
  PDF_NAME(Circle),
1400
  PDF_NAME(Polygon),
1401
  PDF_NAME(PolyLine),
1402
  PDF_NAME(Highlight),
1403
  PDF_NAME(Underline),
1404
  PDF_NAME(Squiggly),
1405
  PDF_NAME(StrikeOut),
1406
  PDF_NAME(Redact),
1407
  PDF_NAME(Stamp),
1408
  PDF_NAME(Caret),
1409
  PDF_NAME(Ink),
1410
  PDF_NAME(FileAttachment),
1411
  PDF_NAME(Sound),
1412
  NULL,
1413
};
1414
1415
int
1416
pdf_annot_has_rich_contents(fz_context *ctx, pdf_annot *annot)
1417
0
{
1418
0
  return is_allowed_subtype_wrap(ctx, annot, PDF_NAME(RC), markup_subtypes);
1419
0
}
1420
1421
const char *
1422
pdf_annot_rich_contents(fz_context *ctx, pdf_annot *annot)
1423
0
{
1424
0
  check_allowed_subtypes(ctx, annot, PDF_NAME(RC), markup_subtypes);
1425
0
  return pdf_dict_get_text_string(ctx, annot->obj, PDF_NAME(RC));
1426
0
}
1427
1428
void
1429
pdf_set_annot_rich_contents(fz_context *ctx, pdf_annot *annot, const char *plain, const char *rich)
1430
0
{
1431
0
  begin_annot_op(ctx, annot, "Set rich contents");
1432
0
  fz_try(ctx)
1433
0
  {
1434
0
    check_allowed_subtypes(ctx, annot, PDF_NAME(RC), markup_subtypes);
1435
0
    pdf_dict_put_text_string(ctx, annot->obj, PDF_NAME(Contents), plain);
1436
0
    pdf_dict_put_text_string(ctx, annot->obj, PDF_NAME(RC), rich);
1437
0
    end_annot_op(ctx, annot);
1438
0
  }
1439
0
  fz_catch(ctx)
1440
0
  {
1441
0
    abandon_annot_op(ctx, annot);
1442
0
    fz_rethrow(ctx);
1443
0
  }
1444
1445
0
  pdf_dirty_annot(ctx, annot);
1446
0
}
1447
1448
static pdf_obj *default_style_subtypes[] = {
1449
  PDF_NAME(FreeText),
1450
  PDF_NAME(Widget),
1451
  NULL,
1452
};
1453
1454
int
1455
pdf_annot_has_rich_defaults(fz_context *ctx, pdf_annot *annot)
1456
0
{
1457
0
  return is_allowed_subtype_wrap(ctx, annot, PDF_NAME(DS), default_style_subtypes);
1458
0
}
1459
1460
const char *
1461
pdf_annot_rich_defaults(fz_context *ctx, pdf_annot *annot)
1462
0
{
1463
0
  check_allowed_subtypes(ctx, annot, PDF_NAME(DS), default_style_subtypes);
1464
0
  return pdf_dict_get_text_string(ctx, annot->obj, PDF_NAME(DS));
1465
0
}
1466
1467
void
1468
pdf_set_annot_rich_defaults(fz_context *ctx, pdf_annot *annot, const char *style)
1469
0
{
1470
0
  begin_annot_op(ctx, annot, "Set rich defaults");
1471
0
  fz_try(ctx)
1472
0
  {
1473
0
    check_allowed_subtypes(ctx, annot, PDF_NAME(DS), default_style_subtypes);
1474
0
    pdf_dict_put_text_string(ctx, annot->obj, PDF_NAME(DS), style);
1475
0
    end_annot_op(ctx, annot);
1476
0
  }
1477
0
  fz_catch(ctx)
1478
0
  {
1479
0
    abandon_annot_op(ctx, annot);
1480
0
    fz_rethrow(ctx);
1481
0
  }
1482
1483
0
  pdf_dirty_annot(ctx, annot);
1484
0
}
1485
1486
int
1487
pdf_annot_has_open(fz_context *ctx, pdf_annot *annot)
1488
0
{
1489
0
  int ret;
1490
1491
0
  pdf_annot_push_local_xref(ctx, annot);
1492
1493
0
  fz_try(ctx)
1494
0
  {
1495
0
    pdf_obj *subtype = pdf_dict_get(ctx, annot->obj, PDF_NAME(Subtype));
1496
0
    pdf_obj *popup = pdf_dict_get(ctx, annot->obj, PDF_NAME(Popup));
1497
0
    ret = (subtype == PDF_NAME(Text) || popup);
1498
0
  }
1499
0
  fz_always(ctx)
1500
0
    pdf_annot_pop_local_xref(ctx, annot);
1501
0
  fz_catch(ctx)
1502
0
    fz_rethrow(ctx);
1503
1504
0
  return ret;
1505
0
}
1506
1507
int
1508
pdf_annot_is_open(fz_context *ctx, pdf_annot *annot)
1509
0
{
1510
0
  int ret = 0;
1511
1512
0
  pdf_annot_push_local_xref(ctx, annot);
1513
1514
0
  fz_try(ctx)
1515
0
  {
1516
0
    pdf_obj *subtype = pdf_dict_get(ctx, annot->obj, PDF_NAME(Subtype));
1517
0
    pdf_obj *popup = pdf_dict_get(ctx, annot->obj, PDF_NAME(Popup));
1518
0
    if (popup)
1519
0
      ret = pdf_dict_get_bool(ctx, popup, PDF_NAME(Open));
1520
0
    else if (subtype == PDF_NAME(Text))
1521
0
      ret = pdf_dict_get_bool(ctx, annot->obj, PDF_NAME(Open));
1522
0
  }
1523
0
  fz_always(ctx)
1524
0
    pdf_annot_pop_local_xref(ctx, annot);
1525
0
  fz_catch(ctx)
1526
0
    fz_rethrow(ctx);
1527
1528
0
  return ret;
1529
0
}
1530
1531
void
1532
pdf_set_annot_is_open(fz_context *ctx, pdf_annot *annot, int is_open)
1533
0
{
1534
0
  begin_annot_op(ctx, annot, is_open ? "Open" : "Close");
1535
1536
0
  fz_try(ctx)
1537
0
  {
1538
0
    pdf_obj *subtype = pdf_dict_get(ctx, annot->obj, PDF_NAME(Subtype));
1539
0
    pdf_obj *popup = pdf_dict_get(ctx, annot->obj, PDF_NAME(Popup));
1540
0
    if (popup)
1541
0
    {
1542
0
      pdf_dict_put_bool(ctx, popup, PDF_NAME(Open), is_open);
1543
0
      pdf_dirty_annot(ctx, annot);
1544
0
    }
1545
0
    else if (subtype == PDF_NAME(Text))
1546
0
    {
1547
0
      pdf_dict_put_bool(ctx, annot->obj, PDF_NAME(Open), is_open);
1548
0
      pdf_dirty_annot(ctx, annot);
1549
0
    }
1550
0
    end_annot_op(ctx, annot);
1551
0
  }
1552
0
  fz_catch(ctx)
1553
0
  {
1554
0
    abandon_annot_op(ctx, annot);
1555
0
    fz_rethrow(ctx);
1556
0
  }
1557
0
}
1558
1559
static pdf_obj *icon_name_subtypes[] = {
1560
  PDF_NAME(FileAttachment),
1561
  PDF_NAME(Sound),
1562
  PDF_NAME(Stamp),
1563
  PDF_NAME(Text),
1564
  NULL,
1565
};
1566
1567
int
1568
pdf_annot_has_icon_name(fz_context *ctx, pdf_annot *annot)
1569
0
{
1570
0
  return is_allowed_subtype_wrap(ctx, annot, PDF_NAME(Name), icon_name_subtypes);
1571
0
}
1572
1573
const char *
1574
pdf_annot_icon_name(fz_context *ctx, pdf_annot *annot)
1575
0
{
1576
0
  const char *ret;
1577
0
  pdf_obj *name;
1578
1579
0
  pdf_annot_push_local_xref(ctx, annot);
1580
1581
0
  fz_try(ctx)
1582
0
  {
1583
0
    check_allowed_subtypes(ctx, annot, PDF_NAME(Name), icon_name_subtypes);
1584
0
    name = pdf_dict_get(ctx, annot->obj, PDF_NAME(Name));
1585
0
    if (!name)
1586
0
    {
1587
0
      pdf_obj *subtype = pdf_dict_get(ctx, annot->obj, PDF_NAME(Subtype));
1588
0
      if (pdf_name_eq(ctx, subtype, PDF_NAME(Text)))
1589
0
      {
1590
0
        ret = "Note";
1591
0
        break;
1592
0
      }
1593
0
      if (pdf_name_eq(ctx, subtype, PDF_NAME(Stamp)))
1594
0
      {
1595
0
        ret = ""; // Should be "Draft" according to spec
1596
0
        break;
1597
0
      }
1598
0
      if (pdf_name_eq(ctx, subtype, PDF_NAME(FileAttachment)))
1599
0
      {
1600
0
        ret = "PushPin";
1601
0
        break;
1602
0
      }
1603
0
      if (pdf_name_eq(ctx, subtype, PDF_NAME(Sound)))
1604
0
      {
1605
0
        ret = "Speaker";
1606
0
        break;
1607
0
      }
1608
0
    }
1609
0
    ret = pdf_to_name(ctx, name);
1610
0
  }
1611
0
  fz_always(ctx)
1612
0
    pdf_annot_pop_local_xref(ctx, annot);
1613
0
  fz_catch(ctx)
1614
0
    fz_rethrow(ctx);
1615
1616
0
  return ret;
1617
0
}
1618
1619
void
1620
pdf_set_annot_icon_name(fz_context *ctx, pdf_annot *annot, const char *name)
1621
0
{
1622
0
  begin_annot_op(ctx, annot, "Set icon name");
1623
1624
0
  fz_try(ctx)
1625
0
  {
1626
0
    check_allowed_subtypes(ctx, annot, PDF_NAME(Name), icon_name_subtypes);
1627
0
    if (name)
1628
0
      pdf_dict_put_name(ctx, annot->obj, PDF_NAME(Name), name);
1629
0
    else
1630
0
      pdf_dict_del(ctx, annot->obj, PDF_NAME(Name));
1631
0
    end_annot_op(ctx, annot);
1632
0
  }
1633
0
  fz_catch(ctx)
1634
0
  {
1635
0
    abandon_annot_op(ctx, annot);
1636
0
    fz_rethrow(ctx);
1637
0
  }
1638
1639
0
  pdf_dirty_annot(ctx, annot);
1640
0
}
1641
1642
int
1643
pdf_annot_is_standard_stamp(fz_context *ctx, pdf_annot *annot)
1644
0
{
1645
0
  pdf_obj *name = pdf_dict_get(ctx, annot->obj, PDF_NAME(Name));
1646
0
  if (pdf_name_eq(ctx, name, PDF_NAME(Approved))) return 1;
1647
0
  else if (pdf_name_eq(ctx, name, PDF_NAME(AsIs))) return 1;
1648
0
  else if (pdf_name_eq(ctx, name, PDF_NAME(Confidential))) return 1;
1649
0
  else if (pdf_name_eq(ctx, name, PDF_NAME(Departmental))) return 1;
1650
0
  else if (pdf_name_eq(ctx, name, PDF_NAME(Draft))) return 1;
1651
0
  else if (pdf_name_eq(ctx, name, PDF_NAME(Experimental))) return 1;
1652
0
  else if (pdf_name_eq(ctx, name, PDF_NAME(Expired))) return 1;
1653
0
  else if (pdf_name_eq(ctx, name, PDF_NAME(Final))) return 1;
1654
0
  else if (pdf_name_eq(ctx, name, PDF_NAME(ForComment))) return 1;
1655
0
  else if (pdf_name_eq(ctx, name, PDF_NAME(ForPublicRelease))) return 1;
1656
0
  else if (pdf_name_eq(ctx, name, PDF_NAME(NotApproved))) return 1;
1657
0
  else if (pdf_name_eq(ctx, name, PDF_NAME(NotForPublicRelease))) return 1;
1658
0
  else if (pdf_name_eq(ctx, name, PDF_NAME(Sold))) return 1;
1659
0
  else if (pdf_name_eq(ctx, name, PDF_NAME(TopSecret))) return 1;
1660
0
  else if (pdf_annot_stamp_image_obj(ctx, annot) != NULL) return 1;
1661
0
  else return 0;
1662
0
}
1663
1664
enum pdf_line_ending pdf_line_ending_from_name(fz_context *ctx, pdf_obj *end)
1665
0
{
1666
0
  if (pdf_name_eq(ctx, end, PDF_NAME(None))) return PDF_ANNOT_LE_NONE;
1667
0
  else if (pdf_name_eq(ctx, end, PDF_NAME(Square))) return PDF_ANNOT_LE_SQUARE;
1668
0
  else if (pdf_name_eq(ctx, end, PDF_NAME(Circle))) return PDF_ANNOT_LE_CIRCLE;
1669
0
  else if (pdf_name_eq(ctx, end, PDF_NAME(Diamond))) return PDF_ANNOT_LE_DIAMOND;
1670
0
  else if (pdf_name_eq(ctx, end, PDF_NAME(OpenArrow))) return PDF_ANNOT_LE_OPEN_ARROW;
1671
0
  else if (pdf_name_eq(ctx, end, PDF_NAME(ClosedArrow))) return PDF_ANNOT_LE_CLOSED_ARROW;
1672
0
  else if (pdf_name_eq(ctx, end, PDF_NAME(Butt))) return PDF_ANNOT_LE_BUTT;
1673
0
  else if (pdf_name_eq(ctx, end, PDF_NAME(ROpenArrow))) return PDF_ANNOT_LE_R_OPEN_ARROW;
1674
0
  else if (pdf_name_eq(ctx, end, PDF_NAME(RClosedArrow))) return PDF_ANNOT_LE_R_CLOSED_ARROW;
1675
0
  else if (pdf_name_eq(ctx, end, PDF_NAME(Slash))) return PDF_ANNOT_LE_SLASH;
1676
0
  else return PDF_ANNOT_LE_NONE;
1677
0
}
1678
1679
enum pdf_line_ending pdf_line_ending_from_string(fz_context *ctx, const char *end)
1680
0
{
1681
0
  if (!strcmp(end, "None")) return PDF_ANNOT_LE_NONE;
1682
0
  else if (!strcmp(end, "Square")) return PDF_ANNOT_LE_SQUARE;
1683
0
  else if (!strcmp(end, "Circle")) return PDF_ANNOT_LE_CIRCLE;
1684
0
  else if (!strcmp(end, "Diamond")) return PDF_ANNOT_LE_DIAMOND;
1685
0
  else if (!strcmp(end, "OpenArrow")) return PDF_ANNOT_LE_OPEN_ARROW;
1686
0
  else if (!strcmp(end, "ClosedArrow")) return PDF_ANNOT_LE_CLOSED_ARROW;
1687
0
  else if (!strcmp(end, "Butt")) return PDF_ANNOT_LE_BUTT;
1688
0
  else if (!strcmp(end, "ROpenArrow")) return PDF_ANNOT_LE_R_OPEN_ARROW;
1689
0
  else if (!strcmp(end, "RClosedArrow")) return PDF_ANNOT_LE_R_CLOSED_ARROW;
1690
0
  else if (!strcmp(end, "Slash")) return PDF_ANNOT_LE_SLASH;
1691
0
  else return PDF_ANNOT_LE_NONE;
1692
0
}
1693
1694
pdf_obj *pdf_name_from_line_ending(fz_context *ctx, enum pdf_line_ending end)
1695
0
{
1696
0
  switch (end)
1697
0
  {
1698
0
  default:
1699
0
  case PDF_ANNOT_LE_NONE: return PDF_NAME(None);
1700
0
  case PDF_ANNOT_LE_SQUARE: return PDF_NAME(Square);
1701
0
  case PDF_ANNOT_LE_CIRCLE: return PDF_NAME(Circle);
1702
0
  case PDF_ANNOT_LE_DIAMOND: return PDF_NAME(Diamond);
1703
0
  case PDF_ANNOT_LE_OPEN_ARROW: return PDF_NAME(OpenArrow);
1704
0
  case PDF_ANNOT_LE_CLOSED_ARROW: return PDF_NAME(ClosedArrow);
1705
0
  case PDF_ANNOT_LE_BUTT: return PDF_NAME(Butt);
1706
0
  case PDF_ANNOT_LE_R_OPEN_ARROW: return PDF_NAME(ROpenArrow);
1707
0
  case PDF_ANNOT_LE_R_CLOSED_ARROW: return PDF_NAME(RClosedArrow);
1708
0
  case PDF_ANNOT_LE_SLASH: return PDF_NAME(Slash);
1709
0
  }
1710
0
}
1711
1712
const char *pdf_string_from_line_ending(fz_context *ctx, enum pdf_line_ending end)
1713
0
{
1714
0
  switch (end)
1715
0
  {
1716
0
  default:
1717
0
  case PDF_ANNOT_LE_NONE: return "None";
1718
0
  case PDF_ANNOT_LE_SQUARE: return "Square";
1719
0
  case PDF_ANNOT_LE_CIRCLE: return "Circle";
1720
0
  case PDF_ANNOT_LE_DIAMOND: return "Diamond";
1721
0
  case PDF_ANNOT_LE_OPEN_ARROW: return "OpenArrow";
1722
0
  case PDF_ANNOT_LE_CLOSED_ARROW: return "ClosedArrow";
1723
0
  case PDF_ANNOT_LE_BUTT: return "Butt";
1724
0
  case PDF_ANNOT_LE_R_OPEN_ARROW: return "ROpenArrow";
1725
0
  case PDF_ANNOT_LE_R_CLOSED_ARROW: return "RClosedArrow";
1726
0
  case PDF_ANNOT_LE_SLASH: return "Slash";
1727
0
  }
1728
0
}
1729
1730
static pdf_obj *line_ending_subtypes[] = {
1731
  PDF_NAME(FreeText),
1732
  PDF_NAME(Line),
1733
  PDF_NAME(PolyLine),
1734
  PDF_NAME(Polygon),
1735
  NULL,
1736
};
1737
1738
int
1739
pdf_annot_has_line_ending_styles(fz_context *ctx, pdf_annot *annot)
1740
0
{
1741
0
  return is_allowed_subtype_wrap(ctx, annot, PDF_NAME(LE), line_ending_subtypes);
1742
0
}
1743
1744
void
1745
pdf_annot_line_ending_styles(fz_context *ctx, pdf_annot *annot,
1746
    enum pdf_line_ending *start_style,
1747
    enum pdf_line_ending *end_style)
1748
0
{
1749
0
  pdf_obj *style;
1750
1751
0
  pdf_annot_push_local_xref(ctx, annot);
1752
1753
0
  fz_try(ctx)
1754
0
  {
1755
0
    check_allowed_subtypes(ctx, annot, PDF_NAME(LE), line_ending_subtypes);
1756
1757
0
    style = pdf_dict_get(ctx, annot->obj, PDF_NAME(LE));
1758
0
    *start_style = pdf_line_ending_from_name(ctx, pdf_array_get(ctx, style, 0));
1759
0
    *end_style = pdf_line_ending_from_name(ctx, pdf_array_get(ctx, style, 1));
1760
0
  }
1761
0
  fz_always(ctx)
1762
0
    pdf_annot_pop_local_xref(ctx, annot);
1763
0
  fz_catch(ctx)
1764
0
    fz_rethrow(ctx);
1765
0
}
1766
1767
enum pdf_line_ending
1768
pdf_annot_line_start_style(fz_context *ctx, pdf_annot *annot)
1769
0
{
1770
0
  pdf_obj *le = pdf_dict_get(ctx, annot->obj, PDF_NAME(LE));
1771
0
  return pdf_line_ending_from_name(ctx, pdf_array_get(ctx, le, 0));
1772
0
}
1773
1774
enum pdf_line_ending
1775
pdf_annot_line_end_style(fz_context *ctx, pdf_annot *annot)
1776
0
{
1777
0
  pdf_obj *le = pdf_dict_get(ctx, annot->obj, PDF_NAME(LE));
1778
0
  return pdf_line_ending_from_name(ctx, pdf_array_get(ctx, le, 1));
1779
0
}
1780
1781
void
1782
pdf_set_annot_line_ending_styles(fz_context *ctx, pdf_annot *annot,
1783
    enum pdf_line_ending start_style,
1784
    enum pdf_line_ending end_style)
1785
0
{
1786
0
  pdf_obj *style;
1787
1788
0
  begin_annot_op(ctx, annot, "Set line endings");
1789
1790
0
  fz_try(ctx)
1791
0
  {
1792
0
    check_allowed_subtypes(ctx, annot, PDF_NAME(LE), line_ending_subtypes);
1793
0
    style = pdf_dict_put_array(ctx, annot->obj, PDF_NAME(LE), 2);
1794
0
    pdf_array_put_drop(ctx, style, 0, pdf_name_from_line_ending(ctx, start_style));
1795
0
    pdf_array_put_drop(ctx, style, 1, pdf_name_from_line_ending(ctx, end_style));
1796
0
    end_annot_op(ctx, annot);
1797
0
  }
1798
0
  fz_catch(ctx)
1799
0
  {
1800
0
    abandon_annot_op(ctx, annot);
1801
0
    fz_rethrow(ctx);
1802
0
  }
1803
1804
0
  pdf_dirty_annot(ctx, annot);
1805
0
}
1806
1807
void
1808
pdf_set_annot_line_start_style(fz_context *ctx, pdf_annot *annot, enum pdf_line_ending s)
1809
0
{
1810
0
  enum pdf_line_ending e = pdf_annot_line_end_style(ctx, annot);
1811
0
  pdf_set_annot_line_ending_styles(ctx, annot, s, e);
1812
0
}
1813
1814
void
1815
pdf_set_annot_line_end_style(fz_context *ctx, pdf_annot *annot, enum pdf_line_ending e)
1816
0
{
1817
0
  enum pdf_line_ending s = pdf_annot_line_start_style(ctx, annot);
1818
0
  pdf_set_annot_line_ending_styles(ctx, annot, s, e);
1819
0
}
1820
1821
static pdf_obj *border_style_subtypes[] = {
1822
  PDF_NAME(Circle),
1823
  PDF_NAME(FreeText),
1824
  PDF_NAME(Ink),
1825
  PDF_NAME(Line),
1826
  PDF_NAME(Polygon),
1827
  PDF_NAME(PolyLine),
1828
  PDF_NAME(Square),
1829
  PDF_NAME(Widget),
1830
  NULL,
1831
};
1832
1833
static pdf_obj *border_effect_subtypes[] = {
1834
  PDF_NAME(Circle),
1835
  PDF_NAME(FreeText),
1836
  PDF_NAME(Polygon),
1837
  PDF_NAME(Square),
1838
  NULL,
1839
};
1840
1841
int
1842
pdf_annot_has_border(fz_context *ctx, pdf_annot *annot)
1843
0
{
1844
0
  return is_allowed_subtype_wrap(ctx, annot, PDF_NAME(BS), border_style_subtypes);
1845
0
}
1846
1847
int
1848
pdf_annot_has_border_effect(fz_context *ctx, pdf_annot *annot)
1849
0
{
1850
0
  return is_allowed_subtype_wrap(ctx, annot, PDF_NAME(BE), border_effect_subtypes);
1851
0
}
1852
1853
enum pdf_border_style
1854
pdf_annot_border_style(fz_context *ctx, pdf_annot *annot)
1855
0
{
1856
0
  pdf_obj *bs, *s;
1857
0
  enum pdf_border_style style;
1858
1859
0
  pdf_annot_push_local_xref(ctx, annot);
1860
1861
0
  fz_try(ctx)
1862
0
  {
1863
0
    check_allowed_subtypes(ctx, annot, PDF_NAME(BS), border_style_subtypes);
1864
0
    bs = pdf_dict_get(ctx, annot->obj, PDF_NAME(BS));
1865
0
    s = pdf_dict_get(ctx, bs, PDF_NAME(S));
1866
1867
0
    if (s == PDF_NAME(D))
1868
0
      style = PDF_BORDER_STYLE_DASHED;
1869
0
    else if (s == PDF_NAME(B))
1870
0
      style = PDF_BORDER_STYLE_BEVELED;
1871
0
    else if (s == PDF_NAME(I))
1872
0
      style = PDF_BORDER_STYLE_INSET;
1873
0
    else if (s == PDF_NAME(U))
1874
0
      style = PDF_BORDER_STYLE_UNDERLINE;
1875
0
    else
1876
0
      style = PDF_BORDER_STYLE_SOLID;
1877
0
  }
1878
0
  fz_always(ctx)
1879
0
    pdf_annot_pop_local_xref(ctx, annot);
1880
0
  fz_catch(ctx)
1881
0
    fz_rethrow(ctx);
1882
1883
0
  return style;
1884
0
}
1885
1886
float
1887
pdf_annot_border_width(fz_context *ctx, pdf_annot *annot)
1888
181
{
1889
181
  pdf_obj *bs, *bs_w, *border;
1890
181
  float ret = 1;
1891
1892
181
  pdf_annot_push_local_xref(ctx, annot);
1893
1894
362
  fz_try(ctx)
1895
362
  {
1896
181
    check_allowed_subtypes(ctx, annot, PDF_NAME(BS), border_style_subtypes);
1897
1898
181
    bs = pdf_dict_get(ctx, annot->obj, PDF_NAME(BS));
1899
181
    bs_w = pdf_dict_get(ctx, bs, PDF_NAME(W));
1900
181
    if (pdf_is_number(ctx, bs_w))
1901
1
    {
1902
1
      ret = pdf_to_real(ctx, bs_w);
1903
1
      break;
1904
1
    }
1905
180
    border = pdf_dict_get(ctx, annot->obj, PDF_NAME(Border));
1906
180
    bs_w = pdf_array_get(ctx, border, 2);
1907
180
    if (pdf_is_number(ctx, bs_w))
1908
0
      ret = pdf_to_real(ctx, bs_w);
1909
180
  }
1910
362
  fz_always(ctx)
1911
181
    pdf_annot_pop_local_xref(ctx, annot);
1912
181
  fz_catch(ctx)
1913
0
    fz_rethrow(ctx);
1914
1915
181
  return ret;
1916
181
}
1917
1918
float
1919
pdf_annot_border(fz_context *ctx, pdf_annot *annot)
1920
0
{
1921
  /* DEPRECATED */
1922
0
  return pdf_annot_border_width(ctx, annot);
1923
0
}
1924
1925
int
1926
pdf_annot_border_dash_count(fz_context *ctx, pdf_annot *annot)
1927
1
{
1928
1
  pdf_obj *bs, *d, *border;
1929
1
  int count;
1930
1931
1
  pdf_annot_push_local_xref(ctx, annot);
1932
1933
2
  fz_try(ctx)
1934
2
  {
1935
1
    check_allowed_subtypes(ctx, annot, PDF_NAME(BS), border_style_subtypes);
1936
1
    bs = pdf_dict_get(ctx, annot->obj, PDF_NAME(BS));
1937
1
    d = pdf_dict_get(ctx, bs, PDF_NAME(D));
1938
    /* Query legacy dash pattern as a fallback */
1939
1
    border = pdf_dict_get(ctx, annot->obj, PDF_NAME(Border));
1940
1
    if (!pdf_is_array(ctx, d) && pdf_is_array(ctx, border))
1941
0
      d = pdf_array_get(ctx, border, 3);
1942
1
    count = pdf_array_len(ctx, d);
1943
1
  }
1944
2
  fz_always(ctx)
1945
1
    pdf_annot_pop_local_xref(ctx, annot);
1946
1
  fz_catch(ctx)
1947
0
    fz_rethrow(ctx);
1948
1949
1
  return count;
1950
1
}
1951
1952
float
1953
pdf_annot_border_dash_item(fz_context *ctx, pdf_annot *annot, int i)
1954
0
{
1955
0
  pdf_obj *bs, *d, *border;
1956
0
  float length;
1957
1958
0
  pdf_annot_push_local_xref(ctx, annot);
1959
1960
0
  fz_try(ctx)
1961
0
  {
1962
0
    check_allowed_subtypes(ctx, annot, PDF_NAME(BS), border_style_subtypes);
1963
0
    bs = pdf_dict_get(ctx, annot->obj, PDF_NAME(BS));
1964
0
    d = pdf_dict_get(ctx, bs, PDF_NAME(D));
1965
    /* Query legacy dash pattern as a fallback */
1966
0
    border = pdf_dict_get(ctx, annot->obj, PDF_NAME(Border));
1967
0
    if (!pdf_is_array(ctx, d) && pdf_is_array(ctx, border))
1968
0
      d = pdf_array_get(ctx, border, 3);
1969
0
    length = pdf_array_get_real(ctx, d, i);
1970
0
  }
1971
0
  fz_always(ctx)
1972
0
    pdf_annot_pop_local_xref(ctx, annot);
1973
0
  fz_catch(ctx)
1974
0
    fz_rethrow(ctx);
1975
1976
0
  return length;
1977
0
}
1978
1979
enum pdf_border_effect
1980
pdf_annot_border_effect(fz_context *ctx, pdf_annot *annot)
1981
0
{
1982
0
  pdf_obj *be;
1983
0
  enum pdf_border_effect effect;
1984
1985
0
  pdf_annot_push_local_xref(ctx, annot);
1986
1987
0
  fz_try(ctx)
1988
0
  {
1989
0
    check_allowed_subtypes(ctx, annot, PDF_NAME(BE), border_effect_subtypes);
1990
0
    be = pdf_dict_get(ctx, annot->obj, PDF_NAME(BE));
1991
0
    if (pdf_dict_get(ctx, be, PDF_NAME(S)) == PDF_NAME(C))
1992
0
      effect = PDF_BORDER_EFFECT_CLOUDY;
1993
0
    else
1994
0
      effect = PDF_BORDER_EFFECT_NONE;
1995
0
  }
1996
0
  fz_always(ctx)
1997
0
    pdf_annot_pop_local_xref(ctx, annot);
1998
0
  fz_catch(ctx)
1999
0
    fz_rethrow(ctx);
2000
2001
0
  return effect;
2002
0
}
2003
2004
float
2005
pdf_annot_border_effect_intensity(fz_context *ctx, pdf_annot *annot)
2006
0
{
2007
0
  pdf_obj *be;
2008
0
  float intensity;
2009
2010
0
  pdf_annot_push_local_xref(ctx, annot);
2011
2012
0
  fz_try(ctx)
2013
0
  {
2014
0
    check_allowed_subtypes(ctx, annot, PDF_NAME(BE), border_effect_subtypes);
2015
0
    be = pdf_dict_get(ctx, annot->obj, PDF_NAME(BE));
2016
0
    intensity = pdf_dict_get_real(ctx, be, PDF_NAME(I));
2017
0
  }
2018
0
  fz_always(ctx)
2019
0
    pdf_annot_pop_local_xref(ctx, annot);
2020
0
  fz_catch(ctx)
2021
0
    fz_rethrow(ctx);
2022
2023
0
  return intensity;
2024
0
}
2025
2026
void
2027
pdf_set_annot_border_width(fz_context *ctx, pdf_annot *annot, float width)
2028
0
{
2029
0
  pdf_obj *bs;
2030
0
  pdf_obj *type;
2031
0
  float old_width, adj;
2032
0
  pdf_obj *rectobj;
2033
2034
0
  begin_annot_op(ctx, annot, "Set border width");
2035
2036
0
  fz_try(ctx)
2037
0
  {
2038
0
    check_allowed_subtypes(ctx, annot, PDF_NAME(BS), border_style_subtypes);
2039
0
    bs = pdf_dict_get(ctx, annot->obj, PDF_NAME(BS));
2040
0
    if (!pdf_is_dict(ctx, bs))
2041
0
      bs = pdf_dict_put_dict(ctx, annot->obj, PDF_NAME(BS), 1);
2042
0
    pdf_dict_put(ctx, bs, PDF_NAME(Type), PDF_NAME(Border));
2043
0
    old_width = pdf_dict_get_real(ctx, bs, PDF_NAME(W));
2044
0
    pdf_dict_put_real(ctx, bs, PDF_NAME(W), width);
2045
0
    rectobj = pdf_dict_get(ctx, annot->obj, PDF_NAME(Rect));
2046
0
    if (pdf_is_array(ctx, rectobj)) {
2047
0
      fz_rect rect = pdf_to_rect(ctx, rectobj);
2048
0
      adj = (width - old_width)/2;
2049
0
      rect.x0 -= adj;
2050
0
      rect.x1 += adj;
2051
0
      rect.y0 -= adj;
2052
0
      rect.y1 += adj;
2053
0
      pdf_dict_put_rect(ctx, annot->obj, PDF_NAME(Rect), rect);
2054
      /* For any of these types, we want to adjust the Rect and RD
2055
       * together so that Rect+RD doesn't change, but the border stroke
2056
       * stays centred on the same point. */
2057
0
      type = pdf_dict_get(ctx, annot->obj, PDF_NAME(Subtype));
2058
0
      if (pdf_name_eq(ctx, type, PDF_NAME(Square)) ||
2059
0
        pdf_name_eq(ctx, type, PDF_NAME(Circle)))
2060
0
      {
2061
0
        fz_rect rd = pdf_annot_rect_diff(ctx, annot);
2062
0
        rd.x0 += adj;
2063
0
        rd.x1 += adj;
2064
0
        rd.y0 += adj;
2065
0
        rd.y1 += adj;
2066
0
        pdf_dict_put_rect(ctx, annot->obj, PDF_NAME(RD), rd);
2067
0
      }
2068
0
    }
2069
0
    pdf_dict_del(ctx, annot->obj, PDF_NAME(Border)); /* deprecated */
2070
0
    end_annot_op(ctx, annot);
2071
0
  }
2072
0
  fz_catch(ctx)
2073
0
  {
2074
0
    abandon_annot_op(ctx, annot);
2075
0
    fz_rethrow(ctx);
2076
0
  }
2077
2078
0
  pdf_dirty_annot(ctx, annot);
2079
0
}
2080
2081
void
2082
pdf_set_annot_border(fz_context *ctx, pdf_annot *annot, float w)
2083
0
{
2084
  /* DEPRECATED */
2085
0
  pdf_set_annot_border_width(ctx, annot, w);
2086
0
}
2087
2088
void
2089
pdf_set_annot_border_style(fz_context *ctx, pdf_annot *annot, enum pdf_border_style style)
2090
0
{
2091
0
  pdf_obj *bs, *s;
2092
2093
0
  begin_annot_op(ctx, annot, "Set border style");
2094
2095
0
  fz_try(ctx)
2096
0
  {
2097
0
    check_allowed_subtypes(ctx, annot, PDF_NAME(BS), border_style_subtypes);
2098
0
    bs = pdf_dict_get(ctx, annot->obj, PDF_NAME(BS));
2099
0
    if (!pdf_is_dict(ctx, bs))
2100
0
      bs = pdf_dict_put_dict(ctx, annot->obj, PDF_NAME(BS), 1);
2101
0
    pdf_dict_put(ctx, bs, PDF_NAME(Type), PDF_NAME(Border));
2102
0
    switch (style)
2103
0
    {
2104
0
    default:
2105
0
    case PDF_BORDER_STYLE_SOLID: s = PDF_NAME(S); break;
2106
0
    case PDF_BORDER_STYLE_DASHED: s = PDF_NAME(D); break;
2107
0
    case PDF_BORDER_STYLE_BEVELED: s = PDF_NAME(B); break;
2108
0
    case PDF_BORDER_STYLE_INSET: s = PDF_NAME(I); break;
2109
0
    case PDF_BORDER_STYLE_UNDERLINE: s = PDF_NAME(U); break;
2110
0
    }
2111
0
    pdf_dict_put(ctx, bs, PDF_NAME(S), s);
2112
0
    end_annot_op(ctx, annot);
2113
0
  }
2114
0
  fz_catch(ctx)
2115
0
  {
2116
0
    abandon_annot_op(ctx, annot);
2117
0
    fz_rethrow(ctx);
2118
0
  }
2119
2120
0
  pdf_dirty_annot(ctx, annot);
2121
0
}
2122
2123
void
2124
pdf_clear_annot_border_dash(fz_context *ctx, pdf_annot *annot)
2125
0
{
2126
0
  pdf_obj *bs, *border;
2127
2128
0
  begin_annot_op(ctx, annot, "Clear border dash pattern");
2129
2130
0
  fz_try(ctx)
2131
0
  {
2132
0
    check_allowed_subtypes(ctx, annot, PDF_NAME(BS), border_style_subtypes);
2133
0
    bs = pdf_dict_get(ctx, annot->obj, PDF_NAME(BS));
2134
0
    if (!pdf_is_dict(ctx, bs))
2135
0
      bs = pdf_dict_put_dict(ctx, annot->obj, PDF_NAME(BS), 1);
2136
0
    pdf_dict_del(ctx, bs, PDF_NAME(D));
2137
    /* Remove legacy dash pattern */
2138
0
    border = pdf_dict_get(ctx, annot->obj, PDF_NAME(Border));
2139
0
    if (pdf_is_array(ctx, border))
2140
0
      pdf_array_delete(ctx, border, 3);
2141
0
    end_annot_op(ctx, annot);
2142
0
  }
2143
0
  fz_catch(ctx)
2144
0
  {
2145
0
    abandon_annot_op(ctx, annot);
2146
0
    fz_rethrow(ctx);
2147
0
  }
2148
2149
0
  pdf_dirty_annot(ctx, annot);
2150
0
}
2151
2152
void
2153
pdf_add_annot_border_dash_item(fz_context *ctx, pdf_annot *annot, float length)
2154
0
{
2155
0
  pdf_obj *bs, *d, *border;
2156
2157
0
  begin_annot_op(ctx, annot, "Add border dash pattern item");
2158
2159
0
  fz_try(ctx)
2160
0
  {
2161
0
    check_allowed_subtypes(ctx, annot, PDF_NAME(BS), border_style_subtypes);
2162
0
    bs = pdf_dict_get(ctx, annot->obj, PDF_NAME(BS));
2163
0
    if (!pdf_is_dict(ctx, bs))
2164
0
      bs = pdf_dict_put_dict(ctx, annot->obj, PDF_NAME(BS), 1);
2165
0
    d = pdf_dict_get(ctx, bs, PDF_NAME(D));
2166
0
    if (!pdf_is_array(ctx, d))
2167
0
      d = pdf_dict_put_array(ctx, bs, PDF_NAME(D), 1);
2168
0
    pdf_array_push_real(ctx, d, length);
2169
    /* Remove legacy dash pattern */
2170
0
    border = pdf_dict_get(ctx, annot->obj, PDF_NAME(Border));
2171
0
    if (pdf_is_array(ctx, border))
2172
0
      pdf_array_delete(ctx, border, 3);
2173
0
    end_annot_op(ctx, annot);
2174
0
  }
2175
0
  fz_catch(ctx)
2176
0
  {
2177
0
    abandon_annot_op(ctx, annot);
2178
0
    fz_rethrow(ctx);
2179
0
  }
2180
2181
0
  pdf_dirty_annot(ctx, annot);
2182
0
}
2183
2184
void
2185
pdf_set_annot_border_effect(fz_context *ctx, pdf_annot *annot, enum pdf_border_effect effect)
2186
0
{
2187
0
  pdf_obj *be, *s;
2188
2189
0
  begin_annot_op(ctx, annot, "Set border effect");
2190
2191
0
  fz_try(ctx)
2192
0
  {
2193
0
    check_allowed_subtypes(ctx, annot, PDF_NAME(BE), border_effect_subtypes);
2194
0
    be = pdf_dict_get(ctx, annot->obj, PDF_NAME(BE));
2195
0
    if (!pdf_is_dict(ctx, be))
2196
0
      be = pdf_dict_put_dict(ctx, annot->obj, PDF_NAME(BE), 1);
2197
0
    switch (effect)
2198
0
    {
2199
0
    default:
2200
0
    case PDF_BORDER_EFFECT_NONE: s = PDF_NAME(S); break;
2201
0
    case PDF_BORDER_EFFECT_CLOUDY: s = PDF_NAME(C); break;
2202
0
    }
2203
0
    pdf_dict_put(ctx, be, PDF_NAME(S), s);
2204
0
    end_annot_op(ctx, annot);
2205
0
  }
2206
0
  fz_catch(ctx)
2207
0
  {
2208
0
    abandon_annot_op(ctx, annot);
2209
0
    fz_rethrow(ctx);
2210
0
  }
2211
2212
0
  pdf_dirty_annot(ctx, annot);
2213
0
}
2214
2215
void
2216
pdf_set_annot_border_effect_intensity(fz_context *ctx, pdf_annot *annot, float intensity)
2217
0
{
2218
0
  pdf_obj *be;
2219
2220
0
  begin_annot_op(ctx, annot, "Set border effect intensity");
2221
2222
0
  fz_try(ctx)
2223
0
  {
2224
0
    check_allowed_subtypes(ctx, annot, PDF_NAME(BE), border_effect_subtypes);
2225
0
    be = pdf_dict_get(ctx, annot->obj, PDF_NAME(BE));
2226
0
    if (!pdf_is_dict(ctx, be))
2227
0
      be = pdf_dict_put_dict(ctx, annot->obj, PDF_NAME(BE), 1);
2228
0
    pdf_dict_put_real(ctx, be, PDF_NAME(I), intensity);
2229
0
    end_annot_op(ctx, annot);
2230
0
  }
2231
0
  fz_catch(ctx)
2232
0
  {
2233
0
    abandon_annot_op(ctx, annot);
2234
0
    fz_rethrow(ctx);
2235
0
  }
2236
2237
0
  pdf_dirty_annot(ctx, annot);
2238
0
}
2239
2240
fz_text_language
2241
pdf_document_language(fz_context *ctx, pdf_document *doc)
2242
152
{
2243
152
  pdf_obj *trailer = pdf_trailer(ctx, doc);
2244
152
  pdf_obj *root = pdf_dict_get(ctx, trailer, PDF_NAME(Root));
2245
152
  pdf_obj *lang = pdf_dict_get(ctx, root, PDF_NAME(Lang));
2246
152
  return fz_text_language_from_string(pdf_to_text_string(ctx, lang));
2247
152
}
2248
2249
void pdf_set_document_language(fz_context *ctx, pdf_document *doc, fz_text_language lang)
2250
0
{
2251
0
  pdf_obj *trailer = pdf_trailer(ctx, doc);
2252
0
  pdf_obj *root = pdf_dict_get(ctx, trailer, PDF_NAME(Root));
2253
0
  char buf[8];
2254
0
  if (lang == FZ_LANG_UNSET)
2255
0
    pdf_dict_del(ctx, root, PDF_NAME(Lang));
2256
0
  else
2257
0
    pdf_dict_put_text_string(ctx, root, PDF_NAME(Lang), fz_string_from_text_language(buf, lang));
2258
0
}
2259
2260
fz_text_language
2261
pdf_guess_text_language_from_font_name(fz_context *ctx, const char *name)
2262
152
{
2263
152
  if (strstr(name, "SimFang")) return FZ_LANG_zh_Hans;
2264
152
  if (strstr(name, "SimHei")) return FZ_LANG_zh_Hans;
2265
152
  if (strstr(name, "SimKai")) return FZ_LANG_zh_Hans;
2266
152
  if (strstr(name, "SimLi")) return FZ_LANG_zh_Hans;
2267
152
  if (strstr(name, "SimSun")) return FZ_LANG_zh_Hans;
2268
152
  if (strstr(name, "Song")) return FZ_LANG_zh_Hans;
2269
2270
152
  if (strstr(name, "MingLiU")) return FZ_LANG_zh_Hant;
2271
2272
152
  if (strstr(name, "Gothic")) return FZ_LANG_ja;
2273
152
  if (strstr(name, "Mincho")) return FZ_LANG_ja;
2274
2275
152
  if (strstr(name, "Batang")) return FZ_LANG_ko;
2276
152
  if (strstr(name, "Gulim")) return FZ_LANG_ko;
2277
152
  if (strstr(name, "Dotum")) return FZ_LANG_ko;
2278
2279
152
  return FZ_LANG_UNSET;
2280
152
}
2281
2282
fz_text_language
2283
pdf_annot_language(fz_context *ctx, pdf_annot *annot)
2284
152
{
2285
152
  fz_text_language ret;
2286
2287
152
  pdf_annot_push_local_xref(ctx, annot);
2288
2289
304
  fz_try(ctx)
2290
304
  {
2291
152
    pdf_obj *lang = pdf_dict_get_inheritable(ctx, annot->obj, PDF_NAME(Lang));
2292
152
    if (lang)
2293
0
      ret = fz_text_language_from_string(pdf_to_str_buf(ctx, lang));
2294
152
    else
2295
152
      ret = pdf_document_language(ctx, annot->page->doc);
2296
152
  }
2297
304
  fz_always(ctx)
2298
152
    pdf_annot_pop_local_xref(ctx, annot);
2299
152
  fz_catch(ctx)
2300
0
    fz_rethrow(ctx);
2301
2302
  // If Lang is not explicitly set, try to guess from the DA font:
2303
152
  if (ret == FZ_LANG_UNSET)
2304
152
  {
2305
152
    char font_name[80];
2306
152
    float size, color[4];
2307
152
    int n;
2308
152
    pdf_annot_default_appearance_unmapped(ctx, annot, font_name, sizeof font_name, &size, &n, color);
2309
152
    ret = pdf_guess_text_language_from_font_name(ctx, font_name);
2310
152
  }
2311
2312
152
  return ret;
2313
152
}
2314
2315
void
2316
pdf_set_annot_language(fz_context *ctx, pdf_annot *annot, fz_text_language lang)
2317
0
{
2318
0
  char buf[8];
2319
2320
0
  begin_annot_op(ctx, annot, "Set language");
2321
2322
0
  fz_try(ctx)
2323
0
  {
2324
0
    if (lang == FZ_LANG_UNSET)
2325
0
      pdf_dict_del(ctx, annot->obj, PDF_NAME(Lang));
2326
0
    else
2327
0
      pdf_dict_put_text_string(ctx, annot->obj, PDF_NAME(Lang), fz_string_from_text_language(buf, lang));
2328
0
    end_annot_op(ctx, annot);
2329
0
  }
2330
0
  fz_catch(ctx)
2331
0
  {
2332
0
    abandon_annot_op(ctx, annot);
2333
0
    fz_rethrow(ctx);
2334
0
  }
2335
2336
0
  pdf_dirty_annot(ctx, annot);
2337
0
}
2338
2339
static pdf_obj *quadding_subtypes[] = {
2340
  PDF_NAME(FreeText),
2341
  PDF_NAME(Widget),
2342
  NULL,
2343
};
2344
2345
int
2346
pdf_annot_has_quadding(fz_context *ctx, pdf_annot *annot)
2347
0
{
2348
0
  return is_allowed_subtype_wrap(ctx, annot, PDF_NAME(Q), quadding_subtypes);
2349
0
}
2350
2351
int
2352
pdf_annot_quadding(fz_context *ctx, pdf_annot *annot)
2353
152
{
2354
152
  int q;
2355
2356
152
  pdf_annot_push_local_xref(ctx, annot);
2357
2358
304
  fz_try(ctx)
2359
304
  {
2360
152
    check_allowed_subtypes(ctx, annot, PDF_NAME(Q), quadding_subtypes);
2361
152
    q = pdf_dict_get_int(ctx, annot->obj, PDF_NAME(Q));
2362
152
    q = (q < 0 || q > 2) ? 0 : q;
2363
152
  }
2364
304
  fz_always(ctx)
2365
152
    pdf_annot_pop_local_xref(ctx, annot);
2366
152
  fz_catch(ctx)
2367
0
    fz_rethrow(ctx);
2368
2369
152
  return q;
2370
152
}
2371
2372
void
2373
pdf_set_annot_quadding(fz_context *ctx, pdf_annot *annot, int q)
2374
0
{
2375
0
  q = (q < 0 || q > 2) ? 0 : q;
2376
2377
0
  begin_annot_op(ctx, annot, "Set quadding");
2378
2379
0
  fz_try(ctx)
2380
0
  {
2381
0
    check_allowed_subtypes(ctx, annot, PDF_NAME(Q), quadding_subtypes);
2382
0
    pdf_dict_put_int(ctx, annot->obj, PDF_NAME(Q), q);
2383
0
    end_annot_op(ctx, annot);
2384
0
  }
2385
0
  fz_catch(ctx)
2386
0
  {
2387
0
    abandon_annot_op(ctx, annot);
2388
0
    fz_rethrow(ctx);
2389
0
  }
2390
2391
0
  pdf_dirty_annot(ctx, annot);
2392
0
}
2393
2394
float pdf_annot_opacity(fz_context *ctx, pdf_annot *annot)
2395
4
{
2396
4
  float ret;
2397
2398
4
  pdf_annot_push_local_xref(ctx, annot);
2399
2400
8
  fz_try(ctx)
2401
8
    ret = pdf_dict_get_real_default(ctx, annot->obj, PDF_NAME(CA), 1);
2402
8
  fz_always(ctx)
2403
4
    pdf_annot_pop_local_xref(ctx, annot);
2404
4
  fz_catch(ctx)
2405
0
    fz_rethrow(ctx);
2406
2407
4
  return ret;
2408
4
}
2409
2410
void pdf_set_annot_opacity(fz_context *ctx, pdf_annot *annot, float opacity)
2411
0
{
2412
0
  begin_annot_op(ctx, annot, "Set opacity");
2413
2414
0
  fz_try(ctx)
2415
0
  {
2416
0
    if (opacity != 1)
2417
0
      pdf_dict_put_real(ctx, annot->obj, PDF_NAME(CA), opacity);
2418
0
    else
2419
0
      pdf_dict_del(ctx, annot->obj, PDF_NAME(CA));
2420
0
    end_annot_op(ctx, annot);
2421
0
  }
2422
0
  fz_catch(ctx)
2423
0
  {
2424
0
    abandon_annot_op(ctx, annot);
2425
0
    fz_rethrow(ctx);
2426
0
  }
2427
2428
0
  pdf_dirty_annot(ctx, annot);
2429
0
}
2430
2431
static void pdf_annot_color_imp(fz_context *ctx, pdf_obj *arr, int *n, float color[4])
2432
364
{
2433
364
  switch (pdf_array_len(ctx, arr))
2434
364
  {
2435
360
  case 0:
2436
360
    if (n)
2437
360
      *n = 0;
2438
360
    break;
2439
0
  case 1:
2440
0
  case 2:
2441
0
    if (n)
2442
0
      *n = 1;
2443
0
    if (color)
2444
0
      color[0] = pdf_array_get_real(ctx, arr, 0);
2445
0
    break;
2446
4
  case 3:
2447
4
    if (n)
2448
4
      *n = 3;
2449
4
    if (color)
2450
4
    {
2451
4
      color[0] = pdf_array_get_real(ctx, arr, 0);
2452
4
      color[1] = pdf_array_get_real(ctx, arr, 1);
2453
4
      color[2] = pdf_array_get_real(ctx, arr, 2);
2454
4
    }
2455
4
    break;
2456
0
  case 4:
2457
0
  default:
2458
0
    if (n)
2459
0
      *n = 4;
2460
0
    if (color)
2461
0
    {
2462
0
      color[0] = pdf_array_get_real(ctx, arr, 0);
2463
0
      color[1] = pdf_array_get_real(ctx, arr, 1);
2464
0
      color[2] = pdf_array_get_real(ctx, arr, 2);
2465
0
      color[3] = pdf_array_get_real(ctx, arr, 3);
2466
0
    }
2467
0
    break;
2468
364
  }
2469
364
}
2470
2471
static int pdf_annot_color_rgb(fz_context *ctx, pdf_obj *arr, float rgb[3])
2472
0
{
2473
0
  float color[4];
2474
0
  int n;
2475
0
  pdf_annot_color_imp(ctx, arr, &n, color);
2476
0
  if (n == 0)
2477
0
  {
2478
0
    return 0;
2479
0
  }
2480
0
  else if (n == 1)
2481
0
  {
2482
0
    rgb[0] = rgb[1] = rgb[2] = color[0];
2483
0
  }
2484
0
  else if (n == 3)
2485
0
  {
2486
0
    rgb[0] = color[0];
2487
0
    rgb[1] = color[1];
2488
0
    rgb[2] = color[2];
2489
0
  }
2490
0
  else if (n == 4)
2491
0
  {
2492
0
    rgb[0] = 1 - fz_min(1, color[0] + color[3]);
2493
0
    rgb[1] = 1 - fz_min(1, color[1] + color[3]);
2494
0
    rgb[2] = 1 - fz_min(1, color[2] + color[3]);
2495
0
  }
2496
0
  return 1;
2497
0
}
2498
2499
static void pdf_set_annot_color_imp(fz_context *ctx, pdf_annot *annot, pdf_obj *key, int n, const float *color, pdf_obj **allowed)
2500
0
{
2501
0
  pdf_obj *arr;
2502
2503
0
  if (allowed)
2504
0
    check_allowed_subtypes(ctx, annot, key, allowed);
2505
0
  if (n != 0 && n != 1 && n != 3 && n != 4)
2506
0
    fz_throw(ctx, FZ_ERROR_ARGUMENT, "color must be 0, 1, 3 or 4 components");
2507
0
  if (!color)
2508
0
    fz_throw(ctx, FZ_ERROR_ARGUMENT, "no color given");
2509
2510
0
  arr = pdf_dict_put_array(ctx, annot->obj, key, n);
2511
0
  fz_try(ctx)
2512
0
  {
2513
0
    switch (n)
2514
0
    {
2515
0
    case 1:
2516
0
      pdf_array_push_real(ctx, arr, color[0]);
2517
0
      break;
2518
0
    case 3:
2519
0
      pdf_array_push_real(ctx, arr, color[0]);
2520
0
      pdf_array_push_real(ctx, arr, color[1]);
2521
0
      pdf_array_push_real(ctx, arr, color[2]);
2522
0
      break;
2523
0
    case 4:
2524
0
      pdf_array_push_real(ctx, arr, color[0]);
2525
0
      pdf_array_push_real(ctx, arr, color[1]);
2526
0
      pdf_array_push_real(ctx, arr, color[2]);
2527
0
      pdf_array_push_real(ctx, arr, color[3]);
2528
0
      break;
2529
0
    }
2530
0
  }
2531
0
  fz_catch(ctx)
2532
0
    fz_rethrow(ctx);
2533
2534
0
  pdf_dirty_annot(ctx, annot);
2535
0
}
2536
2537
static void
2538
do_pdf_annot_color(fz_context *ctx, pdf_annot *annot, int *n, float color[4], pdf_obj *name)
2539
4
{
2540
4
  pdf_annot_push_local_xref(ctx, annot);
2541
2542
8
  fz_try(ctx)
2543
8
  {
2544
4
    pdf_obj *c = pdf_dict_get(ctx, annot->obj, name);
2545
4
    pdf_annot_color_imp(ctx, c, n, color);
2546
4
  }
2547
8
  fz_always(ctx)
2548
4
    pdf_annot_pop_local_xref(ctx, annot);
2549
4
  fz_catch(ctx)
2550
0
    fz_rethrow(ctx);
2551
4
}
2552
2553
void
2554
pdf_annot_color(fz_context *ctx, pdf_annot *annot, int *n, float color[4])
2555
4
{
2556
4
  do_pdf_annot_color(ctx, annot, n, color, PDF_NAME(C));
2557
4
}
2558
2559
void
2560
pdf_annot_MK_BG(fz_context *ctx, pdf_annot *annot, int *n, float color[4])
2561
180
{
2562
180
  pdf_annot_push_local_xref(ctx, annot);
2563
2564
360
  fz_try(ctx)
2565
360
  {
2566
180
    pdf_obj *mk_bg = pdf_dict_get(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME(MK)), PDF_NAME(BG));
2567
180
    pdf_annot_color_imp(ctx, mk_bg, n, color);
2568
180
  }
2569
360
  fz_always(ctx)
2570
180
    pdf_annot_pop_local_xref(ctx, annot);
2571
180
  fz_catch(ctx)
2572
0
    fz_rethrow(ctx);
2573
180
}
2574
2575
int
2576
pdf_annot_MK_BG_rgb(fz_context *ctx, pdf_annot *annot, float rgb[3])
2577
0
{
2578
0
  int ret;
2579
2580
0
  pdf_annot_push_local_xref(ctx, annot);
2581
2582
0
  fz_try(ctx)
2583
0
  {
2584
0
    pdf_obj *mk_bg = pdf_dict_get(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME(MK)), PDF_NAME(BG));
2585
0
    ret = pdf_annot_color_rgb(ctx, mk_bg, rgb);
2586
0
  }
2587
0
  fz_always(ctx)
2588
0
    pdf_annot_pop_local_xref(ctx, annot);
2589
0
  fz_catch(ctx)
2590
0
    fz_rethrow(ctx);
2591
2592
0
  return ret;
2593
0
}
2594
2595
void
2596
pdf_annot_MK_BC(fz_context *ctx, pdf_annot *annot, int *n, float color[4])
2597
180
{
2598
180
  pdf_annot_push_local_xref(ctx, annot);
2599
2600
360
  fz_try(ctx)
2601
360
  {
2602
180
    pdf_obj *mk_bc = pdf_dict_get(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME(MK)), PDF_NAME(BC));
2603
180
    pdf_annot_color_imp(ctx, mk_bc, n, color);
2604
180
  }
2605
360
  fz_always(ctx)
2606
180
    pdf_annot_pop_local_xref(ctx, annot);
2607
180
  fz_catch(ctx)
2608
0
    fz_rethrow(ctx);
2609
180
}
2610
2611
int
2612
pdf_annot_MK_BC_rgb(fz_context *ctx, pdf_annot *annot, float rgb[3])
2613
0
{
2614
0
  int ret;
2615
2616
0
  pdf_annot_push_local_xref(ctx, annot);
2617
2618
0
  fz_try(ctx)
2619
0
  {
2620
0
    pdf_obj *mk_bc = pdf_dict_get(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME(MK)), PDF_NAME(BC));
2621
0
    ret = pdf_annot_color_rgb(ctx, mk_bc, rgb);
2622
0
  }
2623
0
  fz_always(ctx)
2624
0
    pdf_annot_pop_local_xref(ctx, annot);
2625
0
  fz_catch(ctx)
2626
0
    fz_rethrow(ctx);
2627
2628
0
  return ret;
2629
0
}
2630
2631
void
2632
pdf_set_annot_color(fz_context *ctx, pdf_annot *annot, int n, const float *color)
2633
0
{
2634
0
  begin_annot_op(ctx, annot, "Set color");
2635
2636
0
  fz_try(ctx)
2637
0
  {
2638
0
    pdf_set_annot_color_imp(ctx, annot, PDF_NAME(C), n, color, NULL);
2639
0
    end_annot_op(ctx, annot);
2640
0
  }
2641
0
  fz_catch(ctx)
2642
0
  {
2643
0
    abandon_annot_op(ctx, annot);
2644
0
    fz_rethrow(ctx);
2645
0
  }
2646
2647
0
  pdf_dirty_annot(ctx, annot);
2648
0
}
2649
2650
static pdf_obj *interior_color_subtypes[] = {
2651
  PDF_NAME(Circle),
2652
  PDF_NAME(Line),
2653
  PDF_NAME(PolyLine),
2654
  PDF_NAME(Polygon),
2655
  PDF_NAME(Square),
2656
  NULL,
2657
};
2658
2659
int
2660
pdf_annot_has_interior_color(fz_context *ctx, pdf_annot *annot)
2661
0
{
2662
0
  return is_allowed_subtype_wrap(ctx, annot, PDF_NAME(IC), interior_color_subtypes);
2663
0
}
2664
2665
void
2666
pdf_annot_interior_color(fz_context *ctx, pdf_annot *annot, int *n, float color[4])
2667
0
{
2668
0
  check_allowed_subtypes(ctx, annot, PDF_NAME(IC), interior_color_subtypes);
2669
0
  do_pdf_annot_color(ctx, annot, n, color, PDF_NAME(IC));
2670
0
}
2671
2672
void
2673
pdf_set_annot_interior_color(fz_context *ctx, pdf_annot *annot, int n, const float *color)
2674
0
{
2675
0
  begin_annot_op(ctx, annot, "Set interior color");
2676
2677
0
  fz_try(ctx)
2678
0
  {
2679
0
    check_allowed_subtypes(ctx, annot, PDF_NAME(IC), interior_color_subtypes);
2680
0
    pdf_set_annot_color_imp(ctx, annot, PDF_NAME(IC), n, color, interior_color_subtypes);
2681
0
    end_annot_op(ctx, annot);
2682
0
  }
2683
0
  fz_catch(ctx)
2684
0
  {
2685
0
    abandon_annot_op(ctx, annot);
2686
0
    fz_rethrow(ctx);
2687
0
  }
2688
2689
0
  pdf_dirty_annot(ctx, annot);
2690
0
}
2691
2692
static pdf_obj *line_subtypes[] = {
2693
  PDF_NAME(Line),
2694
  NULL,
2695
};
2696
2697
int
2698
pdf_annot_has_line(fz_context *ctx, pdf_annot *annot)
2699
0
{
2700
0
  return is_allowed_subtype_wrap(ctx, annot, PDF_NAME(L), line_subtypes);
2701
0
}
2702
2703
void
2704
pdf_annot_line(fz_context *ctx, pdf_annot *annot, fz_point *a, fz_point *b)
2705
0
{
2706
0
  fz_matrix page_ctm;
2707
0
  pdf_obj *line;
2708
2709
0
  pdf_annot_push_local_xref(ctx, annot);
2710
2711
0
  fz_try(ctx)
2712
0
  {
2713
0
    check_allowed_subtypes(ctx, annot, PDF_NAME(L), line_subtypes);
2714
2715
0
    pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
2716
2717
0
    line = pdf_dict_get(ctx, annot->obj, PDF_NAME(L));
2718
0
    a->x = pdf_array_get_real(ctx, line, 0);
2719
0
    a->y = pdf_array_get_real(ctx, line, 1);
2720
0
    b->x = pdf_array_get_real(ctx, line, 2);
2721
0
    b->y = pdf_array_get_real(ctx, line, 3);
2722
0
    *a = fz_transform_point(*a, page_ctm);
2723
0
    *b = fz_transform_point(*b, page_ctm);
2724
0
  }
2725
0
  fz_always(ctx)
2726
0
    pdf_annot_pop_local_xref(ctx, annot);
2727
0
  fz_catch(ctx)
2728
0
    fz_rethrow(ctx);
2729
0
}
2730
2731
void
2732
pdf_set_annot_line(fz_context *ctx, pdf_annot *annot, fz_point a, fz_point b)
2733
0
{
2734
0
  fz_matrix page_ctm, inv_page_ctm;
2735
0
  pdf_obj *line;
2736
2737
0
  begin_annot_op(ctx, annot, "Set line");
2738
2739
0
  fz_try(ctx)
2740
0
  {
2741
0
    check_allowed_subtypes(ctx, annot, PDF_NAME(L), line_subtypes);
2742
2743
0
    pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
2744
0
    inv_page_ctm = fz_invert_matrix(page_ctm);
2745
2746
0
    a = fz_transform_point(a, inv_page_ctm);
2747
0
    b = fz_transform_point(b, inv_page_ctm);
2748
2749
0
    line = pdf_dict_put_array(ctx, annot->obj, PDF_NAME(L), 4);
2750
0
    pdf_array_push_real(ctx, line, a.x);
2751
0
    pdf_array_push_real(ctx, line, a.y);
2752
0
    pdf_array_push_real(ctx, line, b.x);
2753
0
    pdf_array_push_real(ctx, line, b.y);
2754
0
    end_annot_op(ctx, annot);
2755
0
  }
2756
0
  fz_catch(ctx)
2757
0
  {
2758
0
    abandon_annot_op(ctx, annot);
2759
0
    fz_rethrow(ctx);
2760
0
  }
2761
2762
0
  pdf_dirty_annot(ctx, annot);
2763
0
}
2764
2765
float
2766
pdf_annot_line_leader(fz_context *ctx, pdf_annot *annot)
2767
0
{
2768
0
  float value;
2769
0
  pdf_annot_push_local_xref(ctx, annot);
2770
0
  fz_try(ctx)
2771
0
  {
2772
0
    check_allowed_subtypes(ctx, annot, PDF_NAME(LL), line_subtypes);
2773
0
    value = pdf_dict_get_real(ctx, annot->obj, PDF_NAME(LL));
2774
0
  }
2775
0
  fz_always(ctx)
2776
0
    pdf_annot_pop_local_xref(ctx, annot);
2777
0
  fz_catch(ctx)
2778
0
    fz_rethrow(ctx);
2779
0
  return value;
2780
0
}
2781
2782
float
2783
pdf_annot_line_leader_extension(fz_context *ctx, pdf_annot *annot)
2784
0
{
2785
0
  float value;
2786
0
  pdf_annot_push_local_xref(ctx, annot);
2787
0
  fz_try(ctx)
2788
0
  {
2789
0
    check_allowed_subtypes(ctx, annot, PDF_NAME(LLE), line_subtypes);
2790
0
    value = pdf_dict_get_real(ctx, annot->obj, PDF_NAME(LLE));
2791
0
  }
2792
0
  fz_always(ctx)
2793
0
    pdf_annot_pop_local_xref(ctx, annot);
2794
0
  fz_catch(ctx)
2795
0
    fz_rethrow(ctx);
2796
0
  return value;
2797
0
}
2798
2799
float
2800
pdf_annot_line_leader_offset(fz_context *ctx, pdf_annot *annot)
2801
0
{
2802
0
  float value;
2803
0
  pdf_annot_push_local_xref(ctx, annot);
2804
0
  fz_try(ctx)
2805
0
  {
2806
0
    check_allowed_subtypes(ctx, annot, PDF_NAME(LLO), line_subtypes);
2807
0
    value = pdf_dict_get_real(ctx, annot->obj, PDF_NAME(LLO));
2808
0
  }
2809
0
  fz_always(ctx)
2810
0
    pdf_annot_pop_local_xref(ctx, annot);
2811
0
  fz_catch(ctx)
2812
0
    fz_rethrow(ctx);
2813
0
  return value;
2814
0
}
2815
2816
void
2817
pdf_set_annot_line_leader(fz_context *ctx, pdf_annot *annot, float value)
2818
0
{
2819
0
  begin_annot_op(ctx, annot, "Set line leader");
2820
0
  fz_try(ctx)
2821
0
  {
2822
0
    check_allowed_subtypes(ctx, annot, PDF_NAME(LL), line_subtypes);
2823
0
    if (value)
2824
0
      pdf_dict_put_real(ctx, annot->obj, PDF_NAME(LL), value);
2825
0
    else
2826
0
      pdf_dict_del(ctx, annot->obj, PDF_NAME(LL));
2827
0
    end_annot_op(ctx, annot);
2828
0
  }
2829
0
  fz_catch(ctx)
2830
0
  {
2831
0
    abandon_annot_op(ctx, annot);
2832
0
    fz_rethrow(ctx);
2833
0
  }
2834
0
  pdf_dirty_annot(ctx, annot);
2835
0
}
2836
2837
void
2838
pdf_set_annot_line_leader_extension(fz_context *ctx, pdf_annot *annot, float value)
2839
0
{
2840
0
  begin_annot_op(ctx, annot, "Set line leader_extension");
2841
0
  fz_try(ctx)
2842
0
  {
2843
0
    check_allowed_subtypes(ctx, annot, PDF_NAME(LLE), line_subtypes);
2844
0
    if (value)
2845
0
      pdf_dict_put_real(ctx, annot->obj, PDF_NAME(LLE), value);
2846
0
    else
2847
0
      pdf_dict_del(ctx, annot->obj, PDF_NAME(LLE));
2848
0
    end_annot_op(ctx, annot);
2849
0
  }
2850
0
  fz_catch(ctx)
2851
0
  {
2852
0
    abandon_annot_op(ctx, annot);
2853
0
    fz_rethrow(ctx);
2854
0
  }
2855
0
  pdf_dirty_annot(ctx, annot);
2856
0
}
2857
2858
void
2859
pdf_set_annot_line_leader_offset(fz_context *ctx, pdf_annot *annot, float value)
2860
0
{
2861
0
  begin_annot_op(ctx, annot, "Set line leader offset");
2862
0
  fz_try(ctx)
2863
0
  {
2864
0
    check_allowed_subtypes(ctx, annot, PDF_NAME(LLO), line_subtypes);
2865
0
    if (value)
2866
0
      pdf_dict_put_real(ctx, annot->obj, PDF_NAME(LLO), value);
2867
0
    else
2868
0
      pdf_dict_del(ctx, annot->obj, PDF_NAME(LLO));
2869
0
    end_annot_op(ctx, annot);
2870
0
  }
2871
0
  fz_catch(ctx)
2872
0
  {
2873
0
    abandon_annot_op(ctx, annot);
2874
0
    fz_rethrow(ctx);
2875
0
  }
2876
0
  pdf_dirty_annot(ctx, annot);
2877
0
}
2878
2879
int
2880
pdf_annot_line_caption(fz_context *ctx, pdf_annot *annot)
2881
0
{
2882
0
  int cap = 0;
2883
2884
0
  pdf_annot_push_local_xref(ctx, annot);
2885
2886
0
  fz_try(ctx)
2887
0
  {
2888
0
    check_allowed_subtypes(ctx, annot, PDF_NAME(Cap), line_subtypes);
2889
2890
0
    cap = pdf_dict_get_bool(ctx, annot->obj, PDF_NAME(Cap));
2891
0
  }
2892
0
  fz_always(ctx)
2893
0
    pdf_annot_pop_local_xref(ctx, annot);
2894
0
  fz_catch(ctx)
2895
0
    fz_rethrow(ctx);
2896
2897
0
  return cap;
2898
0
}
2899
2900
void
2901
pdf_set_annot_line_caption(fz_context *ctx, pdf_annot *annot, int cap)
2902
0
{
2903
0
  begin_annot_op(ctx, annot, "Set line caption");
2904
2905
0
  fz_try(ctx)
2906
0
  {
2907
0
    check_allowed_subtypes(ctx, annot, PDF_NAME(Cap), line_subtypes);
2908
2909
0
    pdf_dict_put_bool(ctx, annot->obj, PDF_NAME(Cap), cap);
2910
2911
0
    end_annot_op(ctx, annot);
2912
0
  }
2913
0
  fz_catch(ctx)
2914
0
  {
2915
0
    abandon_annot_op(ctx, annot);
2916
0
    fz_rethrow(ctx);
2917
0
  }
2918
2919
0
  pdf_dirty_annot(ctx, annot);
2920
0
}
2921
2922
fz_point
2923
pdf_annot_line_caption_offset(fz_context *ctx, pdf_annot *annot)
2924
0
{
2925
0
  fz_point offset = fz_make_point(0, 0);
2926
2927
0
  pdf_annot_push_local_xref(ctx, annot);
2928
2929
0
  fz_try(ctx)
2930
0
  {
2931
0
    check_allowed_subtypes(ctx, annot, PDF_NAME(CO), line_subtypes);
2932
2933
0
    offset = pdf_dict_get_point(ctx, annot->obj, PDF_NAME(CO));
2934
0
  }
2935
0
  fz_always(ctx)
2936
0
    pdf_annot_pop_local_xref(ctx, annot);
2937
0
  fz_catch(ctx)
2938
0
    fz_rethrow(ctx);
2939
2940
0
  return offset;
2941
0
}
2942
2943
void
2944
pdf_set_annot_line_caption_offset(fz_context *ctx, pdf_annot *annot, fz_point offset)
2945
0
{
2946
0
  begin_annot_op(ctx, annot, "Set line caption");
2947
2948
0
  fz_try(ctx)
2949
0
  {
2950
0
    check_allowed_subtypes(ctx, annot, PDF_NAME(CO), line_subtypes);
2951
2952
0
    if (offset.x == 0 && offset.y == 0)
2953
0
      pdf_dict_del(ctx, annot->obj, PDF_NAME(CO));
2954
0
    else
2955
0
      pdf_dict_put_point(ctx, annot->obj, PDF_NAME(CO), offset);
2956
2957
0
    end_annot_op(ctx, annot);
2958
0
  }
2959
0
  fz_catch(ctx)
2960
0
  {
2961
0
    abandon_annot_op(ctx, annot);
2962
0
    fz_rethrow(ctx);
2963
0
  }
2964
2965
0
  pdf_dirty_annot(ctx, annot);
2966
0
}
2967
2968
static pdf_obj *vertices_subtypes[] = {
2969
  PDF_NAME(PolyLine),
2970
  PDF_NAME(Polygon),
2971
  NULL,
2972
};
2973
2974
int
2975
pdf_annot_has_vertices(fz_context *ctx, pdf_annot *annot)
2976
0
{
2977
0
  return is_allowed_subtype_wrap(ctx, annot, PDF_NAME(Vertices), vertices_subtypes);
2978
0
}
2979
2980
int
2981
pdf_annot_vertex_count(fz_context *ctx, pdf_annot *annot)
2982
0
{
2983
0
  pdf_obj *vertices;
2984
0
  int ret;
2985
2986
0
  pdf_annot_push_local_xref(ctx, annot);
2987
2988
0
  fz_try(ctx)
2989
0
  {
2990
0
    check_allowed_subtypes(ctx, annot, PDF_NAME(Vertices), vertices_subtypes);
2991
0
    vertices = pdf_dict_get(ctx, annot->obj, PDF_NAME(Vertices));
2992
0
    ret = pdf_array_len(ctx, vertices) / 2;
2993
0
  }
2994
0
  fz_always(ctx)
2995
0
    pdf_annot_pop_local_xref(ctx, annot);
2996
0
  fz_catch(ctx)
2997
0
    fz_rethrow(ctx);
2998
2999
0
  return ret;
3000
0
}
3001
3002
fz_point
3003
pdf_annot_vertex(fz_context *ctx, pdf_annot *annot, int i)
3004
0
{
3005
0
  pdf_obj *vertices;
3006
0
  fz_matrix page_ctm;
3007
0
  fz_point point;
3008
3009
0
  pdf_annot_push_local_xref(ctx, annot);
3010
3011
0
  fz_try(ctx)
3012
0
  {
3013
0
    check_allowed_subtypes(ctx, annot, PDF_NAME(Vertices), vertices_subtypes);
3014
3015
0
    vertices = pdf_dict_get(ctx, annot->obj, PDF_NAME(Vertices));
3016
3017
0
    pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
3018
3019
0
    point.x = pdf_array_get_real(ctx, vertices, i * 2);
3020
0
    point.y = pdf_array_get_real(ctx, vertices, i * 2 + 1);
3021
0
  }
3022
0
  fz_always(ctx)
3023
0
    pdf_annot_pop_local_xref(ctx, annot);
3024
0
  fz_catch(ctx)
3025
0
    fz_rethrow(ctx);
3026
3027
0
  return fz_transform_point(point, page_ctm);
3028
0
}
3029
3030
void
3031
pdf_set_annot_vertices(fz_context *ctx, pdf_annot *annot, int n, const fz_point *v)
3032
0
{
3033
0
  fz_matrix page_ctm, inv_page_ctm;
3034
0
  pdf_obj *vertices;
3035
0
  fz_point point;
3036
0
  int i;
3037
3038
0
  begin_annot_op(ctx, annot, "Set points");
3039
3040
0
  fz_try(ctx)
3041
0
  {
3042
0
    check_allowed_subtypes(ctx, annot, PDF_NAME(Vertices), vertices_subtypes);
3043
0
    if (n <= 0 || !v)
3044
0
      fz_throw(ctx, FZ_ERROR_ARGUMENT, "invalid number of vertices");
3045
3046
0
    pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
3047
0
    inv_page_ctm = fz_invert_matrix(page_ctm);
3048
3049
0
    vertices = pdf_dict_put_array(ctx, annot->obj, PDF_NAME(Vertices), n * 2);
3050
0
    for (i = 0; i < n; ++i)
3051
0
    {
3052
0
      point = fz_transform_point(v[i], inv_page_ctm);
3053
0
      pdf_array_push_real(ctx, vertices, point.x);
3054
0
      pdf_array_push_real(ctx, vertices, point.y);
3055
0
    }
3056
0
    end_annot_op(ctx, annot);
3057
0
  }
3058
0
  fz_catch(ctx)
3059
0
  {
3060
0
    abandon_annot_op(ctx, annot);
3061
0
    fz_rethrow(ctx);
3062
0
  }
3063
3064
0
  pdf_dirty_annot(ctx, annot);
3065
0
}
3066
3067
void pdf_clear_annot_vertices(fz_context *ctx, pdf_annot *annot)
3068
0
{
3069
0
  begin_annot_op(ctx, annot, "Clear vertices");
3070
3071
0
  fz_try(ctx)
3072
0
  {
3073
0
    check_allowed_subtypes(ctx, annot, PDF_NAME(Vertices), vertices_subtypes);
3074
0
    pdf_dict_del(ctx, annot->obj, PDF_NAME(Vertices));
3075
0
    end_annot_op(ctx, annot);
3076
0
  }
3077
0
  fz_catch(ctx)
3078
0
  {
3079
0
    abandon_annot_op(ctx, annot);
3080
0
    fz_rethrow(ctx);
3081
0
  }
3082
3083
0
  pdf_dirty_annot(ctx, annot);
3084
0
}
3085
3086
void pdf_add_annot_vertex(fz_context *ctx, pdf_annot *annot, fz_point p)
3087
0
{
3088
0
  fz_matrix page_ctm, inv_page_ctm;
3089
0
  pdf_obj *vertices;
3090
3091
0
  begin_annot_op(ctx, annot, "Add point");
3092
3093
0
  fz_try(ctx)
3094
0
  {
3095
0
    check_allowed_subtypes(ctx, annot, PDF_NAME(Vertices), vertices_subtypes);
3096
3097
0
    pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
3098
0
    inv_page_ctm = fz_invert_matrix(page_ctm);
3099
3100
0
    vertices = pdf_dict_get(ctx, annot->obj, PDF_NAME(Vertices));
3101
0
    if (!pdf_is_array(ctx, vertices))
3102
0
      vertices = pdf_dict_put_array(ctx, annot->obj, PDF_NAME(Vertices), 32);
3103
3104
0
    p = fz_transform_point(p, inv_page_ctm);
3105
0
    pdf_array_push_real(ctx, vertices, p.x);
3106
0
    pdf_array_push_real(ctx, vertices, p.y);
3107
0
    end_annot_op(ctx, annot);
3108
0
  }
3109
0
  fz_catch(ctx)
3110
0
  {
3111
0
    abandon_annot_op(ctx, annot);
3112
0
    fz_rethrow(ctx);
3113
0
  }
3114
3115
0
  pdf_dirty_annot(ctx, annot);
3116
0
}
3117
3118
void pdf_set_annot_vertex(fz_context *ctx, pdf_annot *annot, int i, fz_point p)
3119
0
{
3120
0
  fz_matrix page_ctm, inv_page_ctm;
3121
0
  pdf_obj *vertices;
3122
3123
0
  begin_annot_op(ctx, annot, "Set point");
3124
3125
0
  fz_try(ctx)
3126
0
  {
3127
0
    check_allowed_subtypes(ctx, annot, PDF_NAME(Vertices), vertices_subtypes);
3128
3129
0
    pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
3130
0
    inv_page_ctm = fz_invert_matrix(page_ctm);
3131
3132
0
    p = fz_transform_point(p, inv_page_ctm);
3133
3134
0
    vertices = pdf_dict_get(ctx, annot->obj, PDF_NAME(Vertices));
3135
0
    pdf_array_put_real(ctx, vertices, i * 2 + 0, p.x);
3136
0
    pdf_array_put_real(ctx, vertices, i * 2 + 1, p.x);
3137
0
    end_annot_op(ctx, annot);
3138
0
  }
3139
0
  fz_catch(ctx)
3140
0
  {
3141
0
    abandon_annot_op(ctx, annot);
3142
0
    fz_rethrow(ctx);
3143
0
  }
3144
3145
0
  pdf_dirty_annot(ctx, annot);
3146
0
}
3147
3148
static pdf_obj *quad_point_subtypes[] = {
3149
  PDF_NAME(Highlight),
3150
  PDF_NAME(Link),
3151
  PDF_NAME(Squiggly),
3152
  PDF_NAME(StrikeOut),
3153
  PDF_NAME(Underline),
3154
  PDF_NAME(Redact),
3155
  NULL,
3156
};
3157
3158
int
3159
pdf_annot_has_quad_points(fz_context *ctx, pdf_annot *annot)
3160
0
{
3161
0
  return is_allowed_subtype_wrap(ctx, annot, PDF_NAME(QuadPoints), quad_point_subtypes);
3162
0
}
3163
3164
int
3165
pdf_annot_quad_point_count(fz_context *ctx, pdf_annot *annot)
3166
0
{
3167
0
  pdf_obj *quad_points;
3168
0
  int ret;
3169
3170
0
  pdf_annot_push_local_xref(ctx, annot);
3171
3172
0
  fz_try(ctx)
3173
0
  {
3174
0
    check_allowed_subtypes(ctx, annot, PDF_NAME(QuadPoints), quad_point_subtypes);
3175
0
    quad_points = pdf_dict_get(ctx, annot->obj, PDF_NAME(QuadPoints));
3176
0
    ret = pdf_array_len(ctx, quad_points) / 8;
3177
0
  }
3178
0
  fz_always(ctx)
3179
0
    pdf_annot_pop_local_xref(ctx, annot);
3180
0
  fz_catch(ctx)
3181
0
    fz_rethrow(ctx);
3182
3183
0
  return ret;
3184
0
}
3185
3186
fz_quad
3187
pdf_annot_quad_point(fz_context *ctx, pdf_annot *annot, int idx)
3188
0
{
3189
0
  pdf_obj *quad_points;
3190
0
  fz_matrix page_ctm;
3191
0
  float v[8];
3192
0
  int i;
3193
3194
0
  pdf_annot_push_local_xref(ctx, annot);
3195
3196
0
  fz_try(ctx)
3197
0
  {
3198
0
    check_allowed_subtypes(ctx, annot, PDF_NAME(QuadPoints), quad_point_subtypes);
3199
0
    quad_points = pdf_dict_get(ctx, annot->obj, PDF_NAME(QuadPoints));
3200
0
    pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
3201
3202
0
    for (i = 0; i < 8; i += 2)
3203
0
    {
3204
0
      fz_point point;
3205
0
      point.x = pdf_array_get_real(ctx, quad_points, idx * 8 + i + 0);
3206
0
      point.y = pdf_array_get_real(ctx, quad_points, idx * 8 + i + 1);
3207
0
      point = fz_transform_point(point, page_ctm);
3208
0
      v[i+0] = point.x;
3209
0
      v[i+1] = point.y;
3210
0
    }
3211
0
  }
3212
0
  fz_always(ctx)
3213
0
    pdf_annot_pop_local_xref(ctx, annot);
3214
0
  fz_catch(ctx)
3215
0
    fz_rethrow(ctx);
3216
3217
0
  return fz_make_quad(v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7]);
3218
0
}
3219
3220
void
3221
pdf_set_annot_quad_points(fz_context *ctx, pdf_annot *annot, int n, const fz_quad *q)
3222
0
{
3223
0
  fz_matrix page_ctm, inv_page_ctm;
3224
0
  pdf_obj *quad_points;
3225
0
  fz_quad quad;
3226
0
  int i;
3227
3228
0
  begin_annot_op(ctx, annot, "Set quad points");
3229
3230
0
  fz_try(ctx)
3231
0
  {
3232
0
    check_allowed_subtypes(ctx, annot, PDF_NAME(QuadPoints), quad_point_subtypes);
3233
0
    if (n <= 0 || !q)
3234
0
      fz_throw(ctx, FZ_ERROR_ARGUMENT, "invalid number of quadrilaterals");
3235
3236
0
    pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
3237
0
    inv_page_ctm = fz_invert_matrix(page_ctm);
3238
3239
0
    quad_points = pdf_dict_put_array(ctx, annot->obj, PDF_NAME(QuadPoints), n);
3240
0
    for (i = 0; i < n; ++i)
3241
0
    {
3242
0
      quad = fz_transform_quad(q[i], inv_page_ctm);
3243
0
      pdf_array_push_real(ctx, quad_points, quad.ul.x);
3244
0
      pdf_array_push_real(ctx, quad_points, quad.ul.y);
3245
0
      pdf_array_push_real(ctx, quad_points, quad.ur.x);
3246
0
      pdf_array_push_real(ctx, quad_points, quad.ur.y);
3247
0
      pdf_array_push_real(ctx, quad_points, quad.ll.x);
3248
0
      pdf_array_push_real(ctx, quad_points, quad.ll.y);
3249
0
      pdf_array_push_real(ctx, quad_points, quad.lr.x);
3250
0
      pdf_array_push_real(ctx, quad_points, quad.lr.y);
3251
0
    }
3252
3253
0
    end_annot_op(ctx, annot);
3254
0
  }
3255
0
  fz_catch(ctx)
3256
0
  {
3257
0
    abandon_annot_op(ctx, annot);
3258
0
    fz_rethrow(ctx);
3259
0
  }
3260
3261
0
  pdf_dirty_annot(ctx, annot);
3262
0
}
3263
3264
void
3265
pdf_clear_annot_quad_points(fz_context *ctx, pdf_annot *annot)
3266
0
{
3267
0
  begin_annot_op(ctx, annot, "Clear quad points");
3268
3269
0
  fz_try(ctx)
3270
0
  {
3271
0
    check_allowed_subtypes(ctx, annot, PDF_NAME(QuadPoints), quad_point_subtypes);
3272
0
    pdf_dict_del(ctx, annot->obj, PDF_NAME(QuadPoints));
3273
0
    end_annot_op(ctx, annot);
3274
0
  }
3275
0
  fz_catch(ctx)
3276
0
  {
3277
0
    abandon_annot_op(ctx, annot);
3278
0
    fz_rethrow(ctx);
3279
0
  }
3280
3281
0
  pdf_dirty_annot(ctx, annot);
3282
0
}
3283
3284
void
3285
pdf_add_annot_quad_point(fz_context *ctx, pdf_annot *annot, fz_quad quad)
3286
0
{
3287
0
  fz_matrix page_ctm, inv_page_ctm;
3288
0
  pdf_obj *quad_points;
3289
3290
0
  begin_annot_op(ctx, annot, "Add quad point");
3291
3292
0
  fz_try(ctx)
3293
0
  {
3294
0
    check_allowed_subtypes(ctx, annot, PDF_NAME(QuadPoints), quad_point_subtypes);
3295
3296
0
    pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
3297
0
    inv_page_ctm = fz_invert_matrix(page_ctm);
3298
3299
0
    quad_points = pdf_dict_get(ctx, annot->obj, PDF_NAME(QuadPoints));
3300
0
    if (!pdf_is_array(ctx, quad_points))
3301
0
      quad_points = pdf_dict_put_array(ctx, annot->obj, PDF_NAME(QuadPoints), 8);
3302
3303
    /* Contrary to the specification, the points within a QuadPoint are NOT ordered
3304
     * in a counterclockwise fashion. Experiments with Adobe's implementation
3305
     * indicates a cross-wise ordering is intended: ul, ur, ll, lr.
3306
     */
3307
0
    quad = fz_transform_quad(quad, inv_page_ctm);
3308
0
    pdf_array_push_real(ctx, quad_points, quad.ul.x);
3309
0
    pdf_array_push_real(ctx, quad_points, quad.ul.y);
3310
0
    pdf_array_push_real(ctx, quad_points, quad.ur.x);
3311
0
    pdf_array_push_real(ctx, quad_points, quad.ur.y);
3312
0
    pdf_array_push_real(ctx, quad_points, quad.ll.x);
3313
0
    pdf_array_push_real(ctx, quad_points, quad.ll.y);
3314
0
    pdf_array_push_real(ctx, quad_points, quad.lr.x);
3315
0
    pdf_array_push_real(ctx, quad_points, quad.lr.y);
3316
0
    end_annot_op(ctx, annot);
3317
0
  }
3318
0
  fz_catch(ctx)
3319
0
  {
3320
0
    abandon_annot_op(ctx, annot);
3321
0
    fz_rethrow(ctx);
3322
0
  }
3323
3324
0
  pdf_dirty_annot(ctx, annot);
3325
0
}
3326
3327
static pdf_obj *ink_list_subtypes[] = {
3328
  PDF_NAME(Ink),
3329
  NULL,
3330
};
3331
3332
int
3333
pdf_annot_has_ink_list(fz_context *ctx, pdf_annot *annot)
3334
0
{
3335
0
  return is_allowed_subtype_wrap(ctx, annot, PDF_NAME(InkList), ink_list_subtypes);
3336
0
}
3337
3338
int
3339
pdf_annot_ink_list_count(fz_context *ctx, pdf_annot *annot)
3340
0
{
3341
0
  int ret;
3342
3343
0
  pdf_annot_push_local_xref(ctx, annot);
3344
3345
0
  fz_try(ctx)
3346
0
  {
3347
0
    pdf_obj *ink_list;
3348
0
    check_allowed_subtypes(ctx, annot, PDF_NAME(InkList), ink_list_subtypes);
3349
0
    ink_list = pdf_dict_get(ctx, annot->obj, PDF_NAME(InkList));
3350
0
    ret = pdf_array_len(ctx, ink_list);
3351
0
  }
3352
0
  fz_always(ctx)
3353
0
    pdf_annot_pop_local_xref(ctx, annot);
3354
0
  fz_catch(ctx)
3355
0
    fz_rethrow(ctx);
3356
3357
0
  return ret;
3358
0
}
3359
3360
int
3361
pdf_annot_ink_list_stroke_count(fz_context *ctx, pdf_annot *annot, int i)
3362
0
{
3363
0
  pdf_obj *ink_list;
3364
0
  pdf_obj *stroke;
3365
0
  int ret;
3366
3367
0
  pdf_annot_push_local_xref(ctx, annot);
3368
3369
0
  fz_try(ctx)
3370
0
  {
3371
0
    check_allowed_subtypes(ctx, annot, PDF_NAME(InkList), ink_list_subtypes);
3372
0
    ink_list = pdf_dict_get(ctx, annot->obj, PDF_NAME(InkList));
3373
0
    stroke = pdf_array_get(ctx, ink_list, i);
3374
0
    ret = pdf_array_len(ctx, stroke) / 2;
3375
0
  }
3376
0
  fz_always(ctx)
3377
0
    pdf_annot_pop_local_xref(ctx, annot);
3378
0
  fz_catch(ctx)
3379
0
    fz_rethrow(ctx);
3380
3381
0
  return ret;
3382
0
}
3383
3384
fz_point
3385
pdf_annot_ink_list_stroke_vertex(fz_context *ctx, pdf_annot *annot, int i, int k)
3386
0
{
3387
0
  pdf_obj *ink_list;
3388
0
  pdf_obj *stroke;
3389
0
  fz_matrix page_ctm;
3390
0
  fz_point point;
3391
3392
0
  pdf_annot_push_local_xref(ctx, annot);
3393
3394
0
  fz_try(ctx)
3395
0
  {
3396
0
    check_allowed_subtypes(ctx, annot, PDF_NAME(InkList), ink_list_subtypes);
3397
3398
0
    ink_list = pdf_dict_get(ctx, annot->obj, PDF_NAME(InkList));
3399
0
    stroke = pdf_array_get(ctx, ink_list, i);
3400
3401
0
    pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
3402
3403
0
    point.x = pdf_array_get_real(ctx, stroke, k * 2 + 0);
3404
0
    point.y = pdf_array_get_real(ctx, stroke, k * 2 + 1);
3405
0
  }
3406
0
  fz_always(ctx)
3407
0
    pdf_annot_pop_local_xref(ctx, annot);
3408
0
  fz_catch(ctx)
3409
0
    fz_rethrow(ctx);
3410
3411
0
  return fz_transform_point(point, page_ctm);
3412
0
}
3413
3414
/* FIXME: try/catch required for memory exhaustion */
3415
void
3416
pdf_set_annot_ink_list(fz_context *ctx, pdf_annot *annot, int n, const int *count, const fz_point *v)
3417
0
{
3418
0
  fz_matrix page_ctm, inv_page_ctm;
3419
0
  pdf_obj *ink_list = NULL, *stroke;
3420
0
  fz_point point;
3421
0
  int i, k;
3422
3423
0
  fz_var(ink_list);
3424
3425
0
  begin_annot_op(ctx, annot, "Set ink list");
3426
3427
0
  fz_try(ctx)
3428
0
  {
3429
0
    check_allowed_subtypes(ctx, annot, PDF_NAME(InkList), ink_list_subtypes);
3430
3431
0
    pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
3432
0
    inv_page_ctm = fz_invert_matrix(page_ctm);
3433
3434
0
    ink_list = pdf_dict_put_array(ctx, annot->obj, PDF_NAME(InkList), n);
3435
0
    for (i = 0; i < n; ++i)
3436
0
    {
3437
0
      stroke = pdf_array_push_array(ctx, ink_list, count[i] * 2);
3438
3439
      /* Although we have dropped our reference to stroke,
3440
       * it's still valid because we ink_list holds one, and
3441
       * we hold a reference to that. */
3442
0
      for (k = 0; k < count[i]; ++k)
3443
0
      {
3444
0
        point = fz_transform_point(*v++, inv_page_ctm);
3445
0
        pdf_array_push_real(ctx, stroke, point.x);
3446
0
        pdf_array_push_real(ctx, stroke, point.y);
3447
0
      }
3448
0
    }
3449
0
    end_annot_op(ctx, annot);
3450
0
  }
3451
0
  fz_catch(ctx)
3452
0
  {
3453
0
    abandon_annot_op(ctx, annot);
3454
0
    fz_rethrow(ctx);
3455
0
  }
3456
3457
0
  pdf_dirty_annot(ctx, annot);
3458
0
}
3459
3460
void
3461
pdf_clear_annot_ink_list(fz_context *ctx, pdf_annot *annot)
3462
0
{
3463
0
  begin_annot_op(ctx, annot, "Clear ink list");
3464
3465
0
  fz_try(ctx)
3466
0
  {
3467
0
    check_allowed_subtypes(ctx, annot, PDF_NAME(InkList), ink_list_subtypes);
3468
0
    pdf_dict_del(ctx, annot->obj, PDF_NAME(InkList));
3469
0
    end_annot_op(ctx, annot);
3470
0
  }
3471
0
  fz_catch(ctx)
3472
0
  {
3473
0
    abandon_annot_op(ctx, annot);
3474
0
    fz_rethrow(ctx);
3475
0
  }
3476
3477
0
  pdf_dirty_annot(ctx, annot);
3478
0
}
3479
3480
void pdf_add_annot_ink_list_stroke(fz_context *ctx, pdf_annot *annot)
3481
0
{
3482
0
  pdf_obj *ink_list;
3483
3484
0
  begin_annot_op(ctx, annot, "Add ink list stroke");
3485
3486
0
  fz_try(ctx)
3487
0
  {
3488
0
    check_allowed_subtypes(ctx, annot, PDF_NAME(InkList), ink_list_subtypes);
3489
0
    ink_list = pdf_dict_get(ctx, annot->obj, PDF_NAME(InkList));
3490
0
    if (!pdf_is_array(ctx, ink_list))
3491
0
      ink_list = pdf_dict_put_array(ctx, annot->obj, PDF_NAME(InkList), 10);
3492
3493
0
    pdf_array_push_array(ctx, ink_list, 16);
3494
0
    end_annot_op(ctx, annot);
3495
0
  }
3496
0
  fz_catch(ctx)
3497
0
  {
3498
0
    abandon_annot_op(ctx, annot);
3499
0
    fz_rethrow(ctx);
3500
0
  }
3501
3502
0
  pdf_dirty_annot(ctx, annot);
3503
0
}
3504
3505
void pdf_add_annot_ink_list_stroke_vertex(fz_context *ctx, pdf_annot *annot, fz_point p)
3506
0
{
3507
0
  fz_matrix page_ctm, inv_page_ctm;
3508
0
  pdf_obj *ink_list, *stroke;
3509
3510
0
  begin_annot_op(ctx, annot, "Add ink list stroke point");
3511
3512
0
  fz_try(ctx)
3513
0
  {
3514
0
    check_allowed_subtypes(ctx, annot, PDF_NAME(InkList), ink_list_subtypes);
3515
0
    pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
3516
0
    inv_page_ctm = fz_invert_matrix(page_ctm);
3517
3518
0
    ink_list = pdf_dict_get(ctx, annot->obj, PDF_NAME(InkList));
3519
0
    if (!pdf_is_array(ctx, ink_list))
3520
0
      ink_list = pdf_dict_put_array(ctx, annot->obj, PDF_NAME(InkList), 10);
3521
0
    stroke = pdf_array_get(ctx, ink_list, pdf_array_len(ctx, ink_list)-1);
3522
0
    if (!pdf_is_array(ctx, stroke))
3523
0
    {
3524
0
      int len = pdf_array_len(ctx, ink_list);
3525
0
      stroke = pdf_new_array(ctx, pdf_get_bound_document(ctx, ink_list), 16);
3526
0
      pdf_array_put_drop(ctx, ink_list, len ? len-1 : 0, stroke);
3527
0
    }
3528
3529
0
    p = fz_transform_point(p, inv_page_ctm);
3530
0
    pdf_array_push_real(ctx, stroke, p.x);
3531
0
    pdf_array_push_real(ctx, stroke, p.y);
3532
0
    end_annot_op(ctx, annot);
3533
0
  }
3534
0
  fz_catch(ctx)
3535
0
  {
3536
0
    abandon_annot_op(ctx, annot);
3537
0
    fz_rethrow(ctx);
3538
0
  }
3539
3540
0
  pdf_dirty_annot(ctx, annot);
3541
0
}
3542
3543
void
3544
pdf_add_annot_ink_list(fz_context *ctx, pdf_annot *annot, int n, fz_point p[])
3545
0
{
3546
0
  fz_matrix page_ctm, inv_page_ctm;
3547
0
  pdf_obj *ink_list, *stroke;
3548
0
  int i;
3549
3550
0
  begin_annot_op(ctx, annot, "Add ink list");
3551
3552
0
  fz_try(ctx)
3553
0
  {
3554
0
    check_allowed_subtypes(ctx, annot, PDF_NAME(InkList), ink_list_subtypes);
3555
3556
0
    pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
3557
0
    inv_page_ctm = fz_invert_matrix(page_ctm);
3558
3559
0
    ink_list = pdf_dict_get(ctx, annot->obj, PDF_NAME(InkList));
3560
0
    if (!pdf_is_array(ctx, ink_list))
3561
0
      ink_list = pdf_dict_put_array(ctx, annot->obj, PDF_NAME(InkList), 10);
3562
3563
0
    stroke = pdf_array_push_array(ctx, ink_list, n * 2);
3564
0
    for (i = 0; i < n; ++i)
3565
0
    {
3566
0
      fz_point tp = fz_transform_point(p[i], inv_page_ctm);
3567
0
      pdf_array_push_real(ctx, stroke, tp.x);
3568
0
      pdf_array_push_real(ctx, stroke, tp.y);
3569
0
    }
3570
0
    end_annot_op(ctx, annot);
3571
0
  }
3572
0
  fz_catch(ctx)
3573
0
  {
3574
0
    abandon_annot_op(ctx, annot);
3575
0
    fz_rethrow(ctx);
3576
0
  }
3577
3578
0
  pdf_dirty_annot(ctx, annot);
3579
0
}
3580
3581
/*
3582
  Get annotation's modification date in seconds since the epoch.
3583
*/
3584
int64_t
3585
pdf_annot_modification_date(fz_context *ctx, pdf_annot *annot)
3586
0
{
3587
0
  int64_t ret;
3588
3589
0
  pdf_annot_push_local_xref(ctx, annot);
3590
3591
0
  fz_try(ctx)
3592
0
  {
3593
0
    ret = pdf_dict_get_date(ctx, annot->obj, PDF_NAME(M));
3594
0
  }
3595
0
  fz_always(ctx)
3596
0
    pdf_annot_pop_local_xref(ctx, annot);
3597
0
  fz_catch(ctx)
3598
0
    fz_rethrow(ctx);
3599
3600
0
  return ret;
3601
0
}
3602
3603
/*
3604
  Get annotation's creation date in seconds since the epoch.
3605
*/
3606
int64_t
3607
pdf_annot_creation_date(fz_context *ctx, pdf_annot *annot)
3608
0
{
3609
0
  int64_t ret;
3610
3611
0
  pdf_annot_push_local_xref(ctx, annot);
3612
3613
0
  fz_try(ctx)
3614
0
    ret = pdf_dict_get_date(ctx, annot->obj, PDF_NAME(CreationDate));
3615
0
  fz_always(ctx)
3616
0
    pdf_annot_pop_local_xref(ctx, annot);
3617
0
  fz_catch(ctx)
3618
0
    fz_rethrow(ctx);
3619
3620
0
  return ret;
3621
0
}
3622
3623
/*
3624
  Set annotation's modification date in seconds since the epoch.
3625
*/
3626
void
3627
pdf_set_annot_modification_date(fz_context *ctx, pdf_annot *annot, int64_t secs)
3628
0
{
3629
0
  begin_annot_op(ctx, annot, "Set modification date");
3630
3631
0
  fz_try(ctx)
3632
0
  {
3633
0
    pdf_dict_put_date(ctx, annot->obj, PDF_NAME(M), secs);
3634
0
    end_annot_op(ctx, annot);
3635
0
  }
3636
0
  fz_catch(ctx)
3637
0
  {
3638
0
    abandon_annot_op(ctx, annot);
3639
0
    fz_rethrow(ctx);
3640
0
  }
3641
3642
0
  pdf_dirty_annot(ctx, annot);
3643
0
}
3644
3645
/*
3646
  Set annotation's creation date in seconds since the epoch.
3647
*/
3648
void
3649
pdf_set_annot_creation_date(fz_context *ctx, pdf_annot *annot, int64_t secs)
3650
0
{
3651
0
  begin_annot_op(ctx, annot, "Set creation date");
3652
3653
0
  fz_try(ctx)
3654
0
  {
3655
0
    check_allowed_subtypes(ctx, annot, PDF_NAME(CreationDate), markup_subtypes);
3656
0
    pdf_dict_put_date(ctx, annot->obj, PDF_NAME(CreationDate), secs);
3657
0
    end_annot_op(ctx, annot);
3658
0
  }
3659
0
  fz_catch(ctx)
3660
0
  {
3661
0
    abandon_annot_op(ctx, annot);
3662
0
    fz_rethrow(ctx);
3663
0
  }
3664
3665
0
  pdf_dirty_annot(ctx, annot);
3666
0
}
3667
3668
int
3669
pdf_annot_has_author(fz_context *ctx, pdf_annot *annot)
3670
0
{
3671
0
  return is_allowed_subtype_wrap(ctx, annot, PDF_NAME(T), markup_subtypes);
3672
0
}
3673
3674
int
3675
pdf_annot_has_subject(fz_context *ctx, pdf_annot *annot)
3676
0
{
3677
0
  return is_allowed_subtype_wrap(ctx, annot, PDF_NAME(Subj), markup_subtypes);
3678
0
}
3679
3680
int
3681
pdf_annot_has_in_reply_to(fz_context *ctx, pdf_annot *annot)
3682
0
{
3683
0
  return is_allowed_subtype_wrap(ctx, annot, PDF_NAME(IRT), markup_subtypes);
3684
0
}
3685
3686
const char *
3687
pdf_annot_name(fz_context *ctx, pdf_annot *annot)
3688
0
{
3689
0
  const char *ret;
3690
3691
0
  pdf_annot_push_local_xref(ctx, annot);
3692
3693
0
  fz_try(ctx)
3694
0
  {
3695
0
    ret = pdf_dict_get_text_string(ctx, annot->obj, PDF_NAME(NM));
3696
0
  }
3697
0
  fz_always(ctx)
3698
0
    pdf_annot_pop_local_xref(ctx, annot);
3699
0
  fz_catch(ctx)
3700
0
    fz_rethrow(ctx);
3701
3702
0
  return ret;
3703
0
}
3704
3705
void
3706
pdf_set_annot_name(fz_context *ctx, pdf_annot *annot, const char *name)
3707
0
{
3708
0
  begin_annot_op(ctx, annot, "Set name");
3709
3710
0
  fz_try(ctx)
3711
0
  {
3712
0
    pdf_dict_put_text_string(ctx, annot->obj, PDF_NAME(NM), name);
3713
0
    pdf_dirty_annot(ctx, annot);
3714
0
    end_annot_op(ctx, annot);
3715
0
  }
3716
0
  fz_catch(ctx)
3717
0
  {
3718
0
    abandon_annot_op(ctx, annot);
3719
0
    fz_rethrow(ctx);
3720
0
  }
3721
3722
0
  pdf_dirty_annot(ctx, annot);
3723
0
}
3724
3725
3726
const char *
3727
pdf_annot_author(fz_context *ctx, pdf_annot *annot)
3728
0
{
3729
0
  const char *ret;
3730
3731
0
  pdf_annot_push_local_xref(ctx, annot);
3732
3733
0
  fz_try(ctx)
3734
0
  {
3735
0
    check_allowed_subtypes(ctx, annot, PDF_NAME(T), markup_subtypes);
3736
0
    ret = pdf_dict_get_text_string(ctx, annot->obj, PDF_NAME(T));
3737
0
  }
3738
0
  fz_always(ctx)
3739
0
    pdf_annot_pop_local_xref(ctx, annot);
3740
0
  fz_catch(ctx)
3741
0
    fz_rethrow(ctx);
3742
3743
0
  return ret;
3744
0
}
3745
3746
void
3747
pdf_set_annot_author(fz_context *ctx, pdf_annot *annot, const char *author)
3748
0
{
3749
0
  begin_annot_op(ctx, annot, "Set author");
3750
3751
0
  fz_try(ctx)
3752
0
  {
3753
0
    check_allowed_subtypes(ctx, annot, PDF_NAME(T), markup_subtypes);
3754
0
    pdf_dict_put_text_string(ctx, annot->obj, PDF_NAME(T), author);
3755
0
    pdf_dirty_annot(ctx, annot);
3756
0
    end_annot_op(ctx, annot);
3757
0
  }
3758
0
  fz_catch(ctx)
3759
0
  {
3760
0
    abandon_annot_op(ctx, annot);
3761
0
    fz_rethrow(ctx);
3762
0
  }
3763
3764
0
  pdf_dirty_annot(ctx, annot);
3765
0
}
3766
3767
const char *
3768
pdf_annot_subject(fz_context *ctx, pdf_annot *annot)
3769
0
{
3770
0
  const char *ret;
3771
3772
0
  pdf_annot_push_local_xref(ctx, annot);
3773
3774
0
  fz_try(ctx)
3775
0
  {
3776
0
    check_allowed_subtypes(ctx, annot, PDF_NAME(Subj), markup_subtypes);
3777
0
    ret = pdf_dict_get_text_string(ctx, annot->obj, PDF_NAME(Subj));
3778
0
  }
3779
0
  fz_always(ctx)
3780
0
    pdf_annot_pop_local_xref(ctx, annot);
3781
0
  fz_catch(ctx)
3782
0
    fz_rethrow(ctx);
3783
3784
0
  return ret;
3785
0
}
3786
3787
void
3788
pdf_set_annot_subject(fz_context *ctx, pdf_annot *annot, const char *subject)
3789
0
{
3790
0
  begin_annot_op(ctx, annot, "Set subject");
3791
3792
0
  fz_try(ctx)
3793
0
  {
3794
0
    check_allowed_subtypes(ctx, annot, PDF_NAME(Subj), markup_subtypes);
3795
0
    pdf_dict_put_text_string(ctx, annot->obj, PDF_NAME(Subj), subject);
3796
0
    pdf_dirty_annot(ctx, annot);
3797
0
    end_annot_op(ctx, annot);
3798
0
  }
3799
0
  fz_catch(ctx)
3800
0
  {
3801
0
    abandon_annot_op(ctx, annot);
3802
0
    fz_rethrow(ctx);
3803
0
  }
3804
3805
0
  pdf_dirty_annot(ctx, annot);
3806
0
}
3807
3808
static pdf_obj *intent_subtypes[] = {
3809
  PDF_NAME(FreeText),
3810
  PDF_NAME(Line),
3811
  PDF_NAME(Polygon),
3812
  PDF_NAME(PolyLine),
3813
  PDF_NAME(Stamp),
3814
  NULL,
3815
};
3816
3817
enum pdf_intent pdf_intent_from_name(fz_context *ctx, pdf_obj *it)
3818
0
{
3819
0
  if (
3820
0
    it == PDF_NULL ||
3821
0
    it == PDF_NAME(FreeText) ||
3822
0
    it == PDF_NAME(Line) ||
3823
0
    it == PDF_NAME(PolyLine) ||
3824
0
    it == PDF_NAME(Polygon) ||
3825
0
    it == PDF_NAME(Stamp)
3826
0
  )
3827
0
    return PDF_ANNOT_IT_DEFAULT;
3828
0
  if (it == PDF_NAME(FreeTextCallout))
3829
0
    return PDF_ANNOT_IT_FREETEXT_CALLOUT;
3830
0
  if (it == PDF_NAME(FreeTextTypeWriter))
3831
0
    return PDF_ANNOT_IT_FREETEXT_TYPEWRITER;
3832
0
  if (it == PDF_NAME(LineArrow))
3833
0
    return PDF_ANNOT_IT_LINE_ARROW;
3834
0
  if (it == PDF_NAME(LineDimension))
3835
0
    return PDF_ANNOT_IT_LINE_DIMENSION;
3836
0
  if (it == PDF_NAME(PolyLineDimension))
3837
0
    return PDF_ANNOT_IT_POLYLINE_DIMENSION;
3838
0
  if (it == PDF_NAME(PolygonCloud))
3839
0
    return PDF_ANNOT_IT_POLYGON_CLOUD;
3840
0
  if (it == PDF_NAME(PolygonDimension))
3841
0
    return PDF_ANNOT_IT_POLYGON_DIMENSION;
3842
0
  if (it == PDF_NAME(StampImage))
3843
0
    return PDF_ANNOT_IT_STAMP_IMAGE;
3844
0
  if (it == PDF_NAME(StampSnapshot))
3845
0
    return PDF_ANNOT_IT_STAMP_SNAPSHOT;
3846
0
  return PDF_ANNOT_IT_UNKNOWN;
3847
0
}
3848
3849
enum pdf_intent pdf_intent_from_string(fz_context *ctx, const char *it)
3850
0
{
3851
0
  if (
3852
0
    it == NULL ||
3853
0
    !strcmp(it, "FreeText") ||
3854
0
    !strcmp(it, "Line") ||
3855
0
    !strcmp(it, "PolyLine") ||
3856
0
    !strcmp(it, "Polygon") ||
3857
0
    !strcmp(it, "Stamp")
3858
0
  )
3859
0
    return PDF_ANNOT_IT_DEFAULT;
3860
0
  if (!strcmp(it, "FreeTextCallout"))
3861
0
    return PDF_ANNOT_IT_FREETEXT_CALLOUT;
3862
0
  if (!strcmp(it, "FreeTextTypeWriter"))
3863
0
    return PDF_ANNOT_IT_FREETEXT_TYPEWRITER;
3864
0
  if (!strcmp(it, "LineArrow"))
3865
0
    return PDF_ANNOT_IT_LINE_ARROW;
3866
0
  if (!strcmp(it, "LineDimension"))
3867
0
    return PDF_ANNOT_IT_LINE_DIMENSION;
3868
0
  if (!strcmp(it, "PolyLineDimension"))
3869
0
    return PDF_ANNOT_IT_POLYLINE_DIMENSION;
3870
0
  if (!strcmp(it, "PolygonCloud"))
3871
0
    return PDF_ANNOT_IT_POLYGON_CLOUD;
3872
0
  if (!strcmp(it, "PolygonDimension"))
3873
0
    return PDF_ANNOT_IT_POLYGON_DIMENSION;
3874
0
  if (!strcmp(it, "StampImage"))
3875
0
    return PDF_ANNOT_IT_STAMP_IMAGE;
3876
0
  if (!strcmp(it, "StampSnapshot"))
3877
0
    return PDF_ANNOT_IT_STAMP_SNAPSHOT;
3878
0
  return PDF_ANNOT_IT_UNKNOWN;
3879
0
}
3880
3881
pdf_obj *pdf_name_from_intent(fz_context *ctx, enum pdf_intent it)
3882
0
{
3883
0
  switch (it)
3884
0
  {
3885
0
  default:
3886
0
  case PDF_ANNOT_IT_DEFAULT: return PDF_NULL;
3887
0
  case PDF_ANNOT_IT_FREETEXT_CALLOUT: return PDF_NAME(FreeTextCallout);
3888
0
  case PDF_ANNOT_IT_FREETEXT_TYPEWRITER: return PDF_NAME(FreeTextTypeWriter);
3889
0
  case PDF_ANNOT_IT_LINE_ARROW: return PDF_NAME(LineArrow);
3890
0
  case PDF_ANNOT_IT_LINE_DIMENSION: return PDF_NAME(LineDimension);
3891
0
  case PDF_ANNOT_IT_POLYLINE_DIMENSION: return PDF_NAME(PolyLineDimension);
3892
0
  case PDF_ANNOT_IT_POLYGON_CLOUD: return PDF_NAME(PolygonCloud);
3893
0
  case PDF_ANNOT_IT_POLYGON_DIMENSION: return PDF_NAME(PolygonDimension);
3894
0
  }
3895
0
}
3896
3897
const char *pdf_string_from_intent(fz_context *ctx, enum pdf_intent it)
3898
0
{
3899
0
  switch (it)
3900
0
  {
3901
0
  default:
3902
0
  case PDF_ANNOT_IT_DEFAULT: return "Default";
3903
0
  case PDF_ANNOT_IT_FREETEXT_CALLOUT: return "FreeTextCallout";
3904
0
  case PDF_ANNOT_IT_FREETEXT_TYPEWRITER: return "FreeTextTypeWriter";
3905
0
  case PDF_ANNOT_IT_LINE_ARROW: return "LineArrow";
3906
0
  case PDF_ANNOT_IT_LINE_DIMENSION: return "LineDimension";
3907
0
  case PDF_ANNOT_IT_POLYLINE_DIMENSION: return "PolyLineDimension";
3908
0
  case PDF_ANNOT_IT_POLYGON_CLOUD: return "PolygonCloud";
3909
0
  case PDF_ANNOT_IT_POLYGON_DIMENSION: return "PolygonDimension";
3910
0
  }
3911
0
}
3912
3913
int
3914
pdf_annot_has_intent(fz_context *ctx, pdf_annot *annot)
3915
0
{
3916
  /* Only a subset of intents are defined in the spec, so we limit this API to the defined ones.
3917
   * FreeText: Callout, TypeWriter
3918
   * Line: Arrow, Dimension
3919
   * Polygon: Cloud, Dimension
3920
   * PolyLine: Dimension
3921
   */
3922
0
  return is_allowed_subtype_wrap(ctx, annot, PDF_NAME(IT), intent_subtypes);
3923
0
}
3924
3925
enum pdf_intent
3926
pdf_annot_intent(fz_context *ctx, pdf_annot *annot)
3927
0
{
3928
0
  enum pdf_intent ret;
3929
3930
0
  pdf_annot_push_local_xref(ctx, annot);
3931
3932
0
  fz_try(ctx)
3933
0
  {
3934
0
    check_allowed_subtypes(ctx, annot, PDF_NAME(IT), intent_subtypes);
3935
0
    ret = pdf_intent_from_name(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME(IT)));
3936
0
  }
3937
0
  fz_always(ctx)
3938
0
    pdf_annot_pop_local_xref(ctx, annot);
3939
0
  fz_catch(ctx)
3940
0
    fz_rethrow(ctx);
3941
3942
0
  return ret;
3943
0
}
3944
3945
void
3946
pdf_set_annot_intent(fz_context *ctx, pdf_annot *annot, enum pdf_intent it)
3947
0
{
3948
0
  begin_annot_op(ctx, annot, "Set intent");
3949
3950
0
  fz_try(ctx)
3951
0
  {
3952
0
    check_allowed_subtypes(ctx, annot, PDF_NAME(IT), intent_subtypes);
3953
0
    pdf_dict_put(ctx, annot->obj, PDF_NAME(IT), pdf_name_from_intent(ctx, it));
3954
0
    pdf_dirty_annot(ctx, annot);
3955
0
    end_annot_op(ctx, annot);
3956
0
  }
3957
0
  fz_catch(ctx)
3958
0
  {
3959
0
    abandon_annot_op(ctx, annot);
3960
0
    fz_rethrow(ctx);
3961
0
  }
3962
3963
0
  pdf_dirty_annot(ctx, annot);
3964
0
}
3965
3966
static pdf_obj *callout_subtypes[] = {
3967
  PDF_NAME(FreeText),
3968
  NULL,
3969
};
3970
3971
int pdf_annot_has_callout(fz_context *ctx, pdf_annot *annot)
3972
0
{
3973
0
  return is_allowed_subtype_wrap(ctx, annot, PDF_NAME(CL), callout_subtypes);
3974
0
}
3975
3976
enum pdf_line_ending pdf_annot_callout_style(fz_context *ctx, pdf_annot *annot)
3977
0
{
3978
0
  enum pdf_line_ending style = PDF_ANNOT_LE_NONE;
3979
0
  pdf_obj *obj;
3980
0
  pdf_annot_push_local_xref(ctx, annot);
3981
0
  fz_try(ctx)
3982
0
  {
3983
0
    check_allowed_subtypes(ctx, annot, PDF_NAME(CL), callout_subtypes);
3984
0
    obj = pdf_dict_get(ctx, annot->obj, PDF_NAME(LE));
3985
0
    style = pdf_line_ending_from_name(ctx, obj);
3986
0
  }
3987
0
  fz_always(ctx)
3988
0
    pdf_annot_pop_local_xref(ctx, annot);
3989
0
  fz_catch(ctx)
3990
0
    fz_rethrow(ctx);
3991
0
  return style;
3992
0
}
3993
3994
void pdf_set_annot_callout_style(fz_context *ctx, pdf_annot *annot, enum pdf_line_ending style)
3995
0
{
3996
0
  begin_annot_op(ctx, annot, "Set callout style");
3997
3998
0
  fz_try(ctx)
3999
0
  {
4000
0
    check_allowed_subtypes(ctx, annot, PDF_NAME(CL), callout_subtypes);
4001
0
    pdf_dict_put(ctx, annot->obj, PDF_NAME(LE), pdf_name_from_line_ending(ctx, style));
4002
0
    end_annot_op(ctx, annot);
4003
0
  }
4004
0
  fz_catch(ctx)
4005
0
  {
4006
0
    abandon_annot_op(ctx, annot);
4007
0
    fz_rethrow(ctx);
4008
0
  }
4009
4010
0
  pdf_dirty_annot(ctx, annot);
4011
0
}
4012
4013
void pdf_annot_callout_line(fz_context *ctx, pdf_annot *annot, fz_point callout[3], int *np)
4014
0
{
4015
0
  pdf_annot_push_local_xref(ctx, annot);
4016
0
  fz_try(ctx)
4017
0
  {
4018
0
    fz_matrix page_ctm;
4019
0
    pdf_obj *obj;
4020
0
    int n;
4021
4022
0
    check_allowed_subtypes(ctx, annot, PDF_NAME(CL), callout_subtypes);
4023
4024
0
    pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
4025
4026
0
    obj = pdf_dict_get(ctx, annot->obj, PDF_NAME(CL));
4027
0
    n = pdf_array_len(ctx, obj);
4028
0
    if (n == 4 || n == 6)
4029
0
    {
4030
0
      callout[0] = fz_transform_point_xy(pdf_array_get_real(ctx, obj, 0), pdf_array_get_real(ctx, obj, 1), page_ctm);
4031
0
      callout[1] = fz_transform_point_xy(pdf_array_get_real(ctx, obj, 2), pdf_array_get_real(ctx, obj, 3), page_ctm);
4032
0
      if (n == 4)
4033
0
      {
4034
0
        *np = 2;
4035
0
        callout[2] = fz_make_point(0, 0);
4036
0
      }
4037
0
      else
4038
0
      {
4039
0
        *np = 3;
4040
0
        callout[2] = fz_transform_point_xy(pdf_array_get_real(ctx, obj, 4), pdf_array_get_real(ctx, obj, 5), page_ctm);
4041
0
      }
4042
0
    }
4043
0
    else
4044
0
    {
4045
0
      callout[0] = fz_make_point(0, 0);
4046
0
      callout[1] = fz_make_point(0, 0);
4047
0
      callout[2] = fz_make_point(0, 0);
4048
0
      *np = 0;
4049
0
    }
4050
0
  }
4051
0
  fz_always(ctx)
4052
0
    pdf_annot_pop_local_xref(ctx, annot);
4053
0
  fz_catch(ctx)
4054
0
    fz_rethrow(ctx);
4055
0
}
4056
4057
void pdf_set_annot_callout_line(fz_context *ctx, pdf_annot *annot, fz_point callout[3], int n)
4058
0
{
4059
0
  begin_annot_op(ctx, annot, "Set callout");
4060
4061
0
  fz_try(ctx)
4062
0
  {
4063
0
    fz_matrix page_ctm;
4064
0
    fz_point p;
4065
0
    int i;
4066
0
    pdf_obj *obj;
4067
4068
0
    check_allowed_subtypes(ctx, annot, PDF_NAME(CL), callout_subtypes);
4069
4070
0
    if (n == 2 || n == 3)
4071
0
    {
4072
0
      pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
4073
0
      obj = pdf_dict_put_array(ctx, annot->obj, PDF_NAME(CL), n * 2);
4074
0
      for (i = 0; i < n; ++i)
4075
0
      {
4076
0
        p = fz_transform_point(callout[i], page_ctm);
4077
0
        pdf_array_push_real(ctx, obj, p.x);
4078
0
        pdf_array_push_real(ctx, obj, p.y);
4079
0
      }
4080
0
    }
4081
0
    else
4082
0
    {
4083
0
      pdf_dict_del(ctx, annot->obj, PDF_NAME(CL));
4084
0
    }
4085
4086
0
    end_annot_op(ctx, annot);
4087
0
  }
4088
0
  fz_catch(ctx)
4089
0
  {
4090
0
    abandon_annot_op(ctx, annot);
4091
0
    fz_rethrow(ctx);
4092
0
  }
4093
4094
0
  pdf_dirty_annot(ctx, annot);
4095
0
}
4096
4097
fz_point pdf_annot_callout_point(fz_context *ctx, pdf_annot *annot)
4098
0
{
4099
0
  fz_point line[3];
4100
0
  int n;
4101
4102
0
  pdf_annot_callout_line(ctx, annot, line, &n);
4103
0
  if (n > 0)
4104
0
    return line[0];
4105
0
  return fz_make_point(0, 0);
4106
0
}
4107
4108
void pdf_set_annot_callout_point(fz_context *ctx, pdf_annot *annot, fz_point p)
4109
0
{
4110
0
  fz_rect rect;
4111
0
  fz_point a, b, line[3];
4112
0
  float m;
4113
4114
0
  rect = pdf_annot_rect(ctx, annot);
4115
4116
  // Make line from center of text box to designated end point.
4117
0
  a = fz_make_point((rect.x0 + rect.x1) / 2, (rect.y0 + rect.y1) / 2);
4118
0
  b = p;
4119
4120
  // No CalloutLine if end point is within the text box itself.
4121
0
  if (fz_is_point_inside_rect(p, rect))
4122
0
  {
4123
0
    line[0] = p;
4124
0
    line[1] = a;
4125
0
    pdf_set_annot_callout_line(ctx, annot, line, 2);
4126
0
    return;
4127
0
  }
4128
4129
  // Simplified Cohen-Sutherland algorithm to find intersection of line and text box.
4130
0
  m = (b.y - a.y) / (b.x - a.x);
4131
0
  for (;;)
4132
0
  {
4133
0
    if (b.y < rect.y0) {
4134
0
      b.x = a.x + (rect.y0 - a.y) / m;
4135
0
      b.y = rect.y0;
4136
0
    }
4137
0
    else
4138
0
    if (b.y > rect.y1) {
4139
0
      b.x = a.x + (rect.y1 - a.y) / m;
4140
0
      b.y = rect.y1;
4141
0
    }
4142
0
    else
4143
0
    if (b.x < rect.x0) {
4144
0
      b.y = a.y + (rect.x0 - a.x) * m;
4145
0
      b.x = rect.x0;
4146
0
    }
4147
0
    else
4148
0
    if (b.x > rect.x1) {
4149
0
      b.y = a.y + (rect.x1 - a.x) * m;
4150
0
      b.x = rect.x1;
4151
0
    }
4152
0
    else
4153
0
      break;
4154
0
  }
4155
4156
  // Draw line from intersection to end point.
4157
0
  line[0] = p;
4158
0
  line[1] = b;
4159
0
  pdf_set_annot_callout_line(ctx, annot, line, 2);
4160
0
}
4161
4162
void
4163
pdf_parse_default_appearance_unmapped(fz_context *ctx, const char *da, char *font_name, int font_name_size, float *size, int *n, float color[4])
4164
304
{
4165
304
  char buf[100], *p = buf, *tok, *end;
4166
304
  float stack[4] = { 0, 0, 0, 0 };
4167
304
  int top = 0;
4168
4169
304
  fz_strlcpy(font_name, "Helv", font_name_size);
4170
304
  *size = 12;
4171
304
  *n = 0;
4172
304
  color[0] = color[1] = color[2] = color[3] = 0;
4173
4174
304
  fz_strlcpy(buf, da, sizeof buf);
4175
1.82k
  while ((tok = fz_strsep(&p, " \n\r\t")) != NULL)
4176
1.52k
  {
4177
1.52k
    if (tok[0] == 0)
4178
0
      ;
4179
1.52k
    else if (tok[0] == '/')
4180
304
    {
4181
304
      fz_strlcpy(font_name, tok+1, font_name_size);
4182
304
    }
4183
1.21k
    else if (!strcmp(tok, "Tf"))
4184
304
    {
4185
304
      *size = stack[0];
4186
304
      top = 0;
4187
304
    }
4188
912
    else if (!strcmp(tok, "g"))
4189
304
    {
4190
304
      *n = 1;
4191
304
      color[0] = stack[0];
4192
304
      top = 0;
4193
304
    }
4194
608
    else if (!strcmp(tok, "rg"))
4195
0
    {
4196
0
      *n = 3;
4197
0
      color[0] = stack[0];
4198
0
      color[1] = stack[1];
4199
0
      color[2] = stack[2];
4200
0
      top=0;
4201
0
    }
4202
608
    else if (!strcmp(tok, "k"))
4203
0
    {
4204
0
      *n = 4;
4205
0
      color[0] = stack[0];
4206
0
      color[1] = stack[1];
4207
0
      color[2] = stack[2];
4208
0
      color[3] = stack[3];
4209
0
      top=0;
4210
0
    }
4211
608
    else
4212
608
    {
4213
608
      float v = fz_strtof(tok, &end);
4214
608
      if (top < 4)
4215
608
        stack[top] = v;
4216
608
      if (*end == 0)
4217
608
        ++top;
4218
0
      else
4219
0
        top = 0;
4220
608
    }
4221
1.52k
  }
4222
304
}
4223
4224
void
4225
pdf_parse_default_appearance(fz_context *ctx, const char *da, const char **font, float *size, int *n, float color[4])
4226
152
{
4227
152
  char font_name[100];
4228
4229
152
  pdf_parse_default_appearance_unmapped(ctx, da, font_name, sizeof font_name, size, n, color);
4230
4231
152
  if (!strcmp(font_name, "Cour")) *font = "Cour";
4232
0
  else if (!strcmp(font_name, "Helv")) *font = "Helv";
4233
0
  else if (!strcmp(font_name, "TiRo")) *font = "TiRo";
4234
0
  else if (!strcmp(font_name, "Symb")) *font = "Symb";
4235
0
  else if (!strcmp(font_name, "ZaDb")) *font = "ZaDb";
4236
0
  else *font = "Helv";
4237
152
}
4238
4239
void
4240
pdf_print_default_appearance(fz_context *ctx, char *buf, int nbuf, const char *font, float size, int n, const float *color)
4241
0
{
4242
0
  if (n == 4)
4243
0
    fz_snprintf(buf, nbuf, "/%s %g Tf %g %g %g %g k", font, size, color[0], color[1], color[2], color[3]);
4244
0
  else if (n == 3)
4245
0
    fz_snprintf(buf, nbuf, "/%s %g Tf %g %g %g rg", font, size, color[0], color[1], color[2]);
4246
0
  else if (n == 1)
4247
0
    fz_snprintf(buf, nbuf, "/%s %g Tf %g g", font, size, color[0]);
4248
0
  else
4249
0
    fz_snprintf(buf, nbuf, "/%s %g Tf", font, size);
4250
0
}
4251
4252
void
4253
pdf_annot_default_appearance_unmapped(fz_context *ctx, pdf_annot *annot, char *font_name, int font_name_len, float *size, int *n, float color[4])
4254
152
{
4255
152
  pdf_obj *da;
4256
152
  pdf_annot_push_local_xref(ctx, annot);
4257
4258
304
  fz_try(ctx)
4259
304
  {
4260
152
    da = pdf_dict_get_inheritable(ctx, annot->obj, PDF_NAME(DA));
4261
152
    if (!da)
4262
0
    {
4263
0
      pdf_obj *trailer = pdf_trailer(ctx, annot->page->doc);
4264
0
      da = pdf_dict_getl(ctx, trailer, PDF_NAME(Root), PDF_NAME(AcroForm), PDF_NAME(DA), NULL);
4265
0
    }
4266
152
    pdf_parse_default_appearance_unmapped(ctx, pdf_to_str_buf(ctx, da), font_name, font_name_len, size, n, color);
4267
152
  }
4268
304
  fz_always(ctx)
4269
152
    pdf_annot_pop_local_xref(ctx, annot);
4270
152
  fz_catch(ctx)
4271
0
    fz_rethrow(ctx);
4272
152
}
4273
4274
static pdf_obj *default_appearance_subtypes[] = {
4275
  PDF_NAME(FreeText),
4276
  PDF_NAME(Widget),
4277
  NULL,
4278
};
4279
4280
int
4281
pdf_annot_has_default_appearance(fz_context *ctx, pdf_annot *annot)
4282
0
{
4283
0
  return is_allowed_subtype_wrap(ctx, annot, PDF_NAME(DA), default_appearance_subtypes);
4284
0
}
4285
4286
void
4287
pdf_annot_default_appearance(fz_context *ctx, pdf_annot *annot, const char **font, float *size, int *n, float color[4])
4288
152
{
4289
152
  pdf_obj *da;
4290
152
  pdf_annot_push_local_xref(ctx, annot);
4291
4292
304
  fz_try(ctx)
4293
304
  {
4294
152
    check_allowed_subtypes(ctx, annot, PDF_NAME(DA), default_appearance_subtypes);
4295
152
    da = pdf_dict_get_inheritable(ctx, annot->obj, PDF_NAME(DA));
4296
152
    if (!da)
4297
0
    {
4298
0
      pdf_obj *trailer = pdf_trailer(ctx, annot->page->doc);
4299
0
      da = pdf_dict_getl(ctx, trailer, PDF_NAME(Root), PDF_NAME(AcroForm), PDF_NAME(DA), NULL);
4300
0
    }
4301
152
    pdf_parse_default_appearance(ctx, pdf_to_str_buf(ctx, da), font, size, n, color);
4302
152
  }
4303
304
  fz_always(ctx)
4304
152
    pdf_annot_pop_local_xref(ctx, annot);
4305
152
  fz_catch(ctx)
4306
0
    fz_rethrow(ctx);
4307
152
}
4308
4309
void
4310
pdf_set_annot_default_appearance(fz_context *ctx, pdf_annot *annot, const char *font, float size, int n, const float *color)
4311
0
{
4312
0
  char buf[100];
4313
4314
0
  begin_annot_op(ctx, annot, "Set default appearance");
4315
4316
0
  fz_try(ctx)
4317
0
  {
4318
0
    check_allowed_subtypes(ctx, annot, PDF_NAME(DA), default_appearance_subtypes);
4319
0
    pdf_print_default_appearance(ctx, buf, sizeof buf, font, size, n, color);
4320
4321
0
    pdf_dict_put_string(ctx, annot->obj, PDF_NAME(DA), buf, strlen(buf));
4322
4323
0
    pdf_dict_del(ctx, annot->obj, PDF_NAME(DS)); /* not supported */
4324
0
    pdf_dict_del(ctx, annot->obj, PDF_NAME(RC)); /* not supported */
4325
0
    end_annot_op(ctx, annot);
4326
0
  }
4327
0
  fz_catch(ctx)
4328
0
  {
4329
0
    abandon_annot_op(ctx, annot);
4330
0
    fz_rethrow(ctx);
4331
0
  }
4332
4333
0
  pdf_dirty_annot(ctx, annot);
4334
0
}
4335
4336
int pdf_annot_field_flags(fz_context *ctx, pdf_annot *annot)
4337
0
{
4338
0
  int ret;
4339
4340
0
  pdf_annot_push_local_xref(ctx, annot);
4341
4342
0
  fz_try(ctx)
4343
0
    ret = pdf_field_flags(ctx, annot->obj);
4344
0
  fz_always(ctx)
4345
0
    pdf_annot_pop_local_xref(ctx, annot);
4346
0
  fz_catch(ctx)
4347
0
    fz_rethrow(ctx);
4348
4349
0
  return ret;
4350
0
}
4351
4352
const char *pdf_annot_field_value(fz_context *ctx, pdf_annot *widget)
4353
0
{
4354
0
  const char *ret;
4355
4356
0
  pdf_annot_push_local_xref(ctx, widget);
4357
4358
0
  fz_try(ctx)
4359
0
    ret = pdf_field_value(ctx, widget->obj);
4360
0
  fz_always(ctx)
4361
0
    pdf_annot_pop_local_xref(ctx, widget);
4362
0
  fz_catch(ctx)
4363
0
    fz_rethrow(ctx);
4364
4365
0
  return ret;
4366
0
}
4367
4368
const char *pdf_annot_field_label(fz_context *ctx, pdf_annot *widget)
4369
0
{
4370
0
  const char *ret;
4371
4372
0
  pdf_annot_push_local_xref(ctx, widget);
4373
4374
0
  fz_try(ctx)
4375
0
    ret = pdf_field_label(ctx, widget->obj);
4376
0
  fz_always(ctx)
4377
0
    pdf_annot_pop_local_xref(ctx, widget);
4378
0
  fz_catch(ctx)
4379
0
    fz_rethrow(ctx);
4380
4381
0
  return ret;
4382
0
}
4383
4384
int pdf_set_annot_field_value(fz_context *ctx, pdf_document *doc, pdf_annot *annot, const char *text, int ignore_trigger_events)
4385
0
{
4386
0
  int ret;
4387
4388
0
  begin_annot_op(ctx, annot, "Set field value");
4389
4390
0
  fz_try(ctx)
4391
0
  {
4392
0
    ret = pdf_set_field_value(ctx, doc, annot->obj, text, ignore_trigger_events);
4393
0
    end_annot_op(ctx, annot);
4394
0
  }
4395
0
  fz_catch(ctx)
4396
0
  {
4397
0
    abandon_annot_op(ctx, annot);
4398
0
    fz_rethrow(ctx);
4399
0
  }
4400
4401
0
  pdf_dirty_annot(ctx, annot);
4402
4403
0
  return ret;
4404
0
}
4405
4406
void
4407
pdf_set_annot_appearance(fz_context *ctx, pdf_annot *annot, const char *appearance, const char *state, fz_matrix ctm, fz_rect bbox, pdf_obj *res, fz_buffer *contents)
4408
156
{
4409
156
  pdf_obj *form = NULL;
4410
156
  pdf_obj *ap, *app;
4411
156
  pdf_obj *app_name = NULL;
4412
4413
156
  begin_annot_op(ctx, annot, "Set appearance stream");
4414
4415
156
  if (!appearance)
4416
0
    appearance = "N";
4417
4418
156
  fz_var(form);
4419
156
  fz_var(app_name);
4420
4421
312
  fz_try(ctx)
4422
312
  {
4423
156
    ap = pdf_dict_get(ctx, annot->obj, PDF_NAME(AP));
4424
156
    if (!ap)
4425
155
      ap = pdf_dict_put_dict(ctx, annot->obj, PDF_NAME(AP), 1);
4426
4427
156
    if (!state)
4428
156
      form = pdf_keep_obj(ctx, pdf_dict_gets(ctx, ap, appearance));
4429
0
    else
4430
0
    {
4431
0
      if (strcmp(appearance, "N") && strcmp(appearance, "R") && strcmp(appearance, "D"))
4432
0
        fz_throw(ctx, FZ_ERROR_ARGUMENT, "Unknown annotation appearance");
4433
4434
0
      app_name = pdf_new_name(ctx, appearance);
4435
0
      app = pdf_dict_get(ctx, ap, app_name);
4436
0
      if (!app)
4437
0
        app = pdf_dict_put_dict(ctx, ap, app_name, 2);
4438
0
      form = pdf_keep_obj(ctx, pdf_dict_gets(ctx, ap, appearance));
4439
0
    }
4440
    /* Care required here. Some files have multiple annotations, which share
4441
     * appearance streams. As such, we must NOT reuse such appearance streams.
4442
     * On the other hand, we cannot afford to always recreate appearance
4443
     * streams, as this can lead to leakage of partial edits into the document.
4444
     * Any appearance we generate will be in the incremental section, and we
4445
     * will never generate shared appearances. As such, we can reuse an
4446
     * appearance object only if it is in the incremental section. */
4447
156
    if (!pdf_obj_is_incremental(ctx, form))
4448
156
    {
4449
156
      pdf_drop_obj(ctx, form);
4450
156
      form = NULL;
4451
156
    }
4452
156
    if (!pdf_is_dict(ctx, form))
4453
156
    {
4454
156
      pdf_drop_obj(ctx, form);
4455
156
      form = NULL;
4456
156
      form = pdf_new_xobject(ctx, annot->page->doc, bbox, ctm, res, contents);
4457
156
    }
4458
0
    else
4459
0
      pdf_update_xobject(ctx, annot->page->doc, form, bbox, ctm, res, contents);
4460
4461
156
    if (!state)
4462
156
      pdf_dict_puts(ctx, ap, appearance, form);
4463
0
    else
4464
0
      pdf_dict_puts(ctx, app, state, form);
4465
156
    end_annot_op(ctx, annot);
4466
156
  }
4467
312
  fz_always(ctx)
4468
156
  {
4469
156
    pdf_drop_obj(ctx, form);
4470
156
    pdf_drop_obj(ctx, app_name);
4471
156
  }
4472
156
  fz_catch(ctx)
4473
0
  {
4474
0
    abandon_annot_op(ctx, annot);
4475
0
    fz_rethrow(ctx);
4476
0
  }
4477
4478
156
  pdf_set_annot_resynthesised(ctx, annot);
4479
156
}
4480
4481
void
4482
pdf_set_annot_appearance_from_display_list(fz_context *ctx, pdf_annot *annot, const char *appearance, const char *state, fz_matrix ctm, fz_display_list *list)
4483
0
{
4484
0
  pdf_document *doc;
4485
0
  fz_device *dev = NULL;
4486
0
  pdf_obj *res = NULL;
4487
0
  fz_buffer *contents = NULL;
4488
4489
  /* Convert fitz-space mediabox to pdf-space bbox */
4490
0
  fz_rect mediabox = fz_bound_display_list(ctx, list);
4491
0
  fz_matrix transform = { 1, 0, 0, -1, -mediabox.x0, mediabox.y1 };
4492
0
  fz_rect bbox = fz_transform_rect(mediabox, transform);
4493
4494
0
  fz_var(dev);
4495
0
  fz_var(contents);
4496
0
  fz_var(res);
4497
4498
0
  begin_annot_op(ctx, annot, "Set appearance stream");
4499
0
  doc = annot->page->doc;
4500
4501
0
  fz_try(ctx)
4502
0
  {
4503
0
    res = pdf_new_dict(ctx, doc, 1);
4504
0
    contents = fz_new_buffer(ctx, 0);
4505
0
    dev = pdf_new_pdf_device(ctx, doc, transform, res, contents);
4506
0
    fz_run_display_list(ctx, list, dev, fz_identity, fz_infinite_rect, NULL);
4507
0
    fz_close_device(ctx, dev);
4508
0
    fz_drop_device(ctx, dev);
4509
0
    dev = NULL;
4510
4511
0
    pdf_set_annot_appearance(ctx, annot, appearance, state, ctm, bbox, res, contents);
4512
0
    end_annot_op(ctx, annot);
4513
0
  }
4514
0
  fz_always(ctx)
4515
0
  {
4516
0
    fz_drop_device(ctx, dev);
4517
0
    fz_drop_buffer(ctx, contents);
4518
0
    pdf_drop_obj(ctx, res);
4519
0
  }
4520
0
  fz_catch(ctx)
4521
0
  {
4522
0
    abandon_annot_op(ctx, annot);
4523
0
    fz_rethrow(ctx);
4524
0
  }
4525
0
}
4526
4527
static pdf_obj *stamp_subtypes[] = {
4528
  PDF_NAME(Stamp),
4529
  NULL,
4530
};
4531
4532
pdf_obj *
4533
pdf_annot_stamp_image_obj(fz_context *ctx, pdf_annot *annot)
4534
0
{
4535
0
  pdf_obj *obj, *imgobj = NULL;
4536
4537
0
  pdf_annot_push_local_xref(ctx, annot);
4538
4539
0
  fz_try(ctx)
4540
0
  {
4541
0
    obj = pdf_dict_getp(ctx, annot->obj, "AP/N/Resources/XObject");
4542
0
    if (pdf_dict_len(ctx, obj) == 1)
4543
0
    {
4544
0
      obj = pdf_dict_get_val(ctx, obj, 0);
4545
0
      if (pdf_is_image_stream(ctx, obj))
4546
0
        imgobj = obj;
4547
0
    }
4548
0
  }
4549
0
  fz_always(ctx)
4550
0
    pdf_annot_pop_local_xref(ctx, annot);
4551
0
  fz_catch(ctx)
4552
0
    fz_rethrow(ctx);
4553
4554
0
  return imgobj;
4555
0
}
4556
4557
void pdf_set_annot_stamp_image_obj(fz_context *ctx, pdf_annot *annot, pdf_obj *ref)
4558
0
{
4559
0
  begin_annot_op(ctx, annot, "Set stamp image");
4560
4561
0
  fz_try(ctx)
4562
0
  {
4563
0
    check_allowed_subtypes(ctx, annot, PDF_NAME(Stamp), stamp_subtypes);
4564
4565
0
    pdf_dict_del(ctx, annot->obj, PDF_NAME(AP));
4566
0
    pdf_dict_putp(ctx, annot->obj, "AP/N/Resources/XObject/I", ref);
4567
4568
0
    end_annot_op(ctx, annot);
4569
0
  }
4570
0
  fz_catch(ctx)
4571
0
  {
4572
0
    abandon_annot_op(ctx, annot);
4573
0
    fz_rethrow(ctx);
4574
0
  }
4575
4576
0
  pdf_dirty_annot(ctx, annot);
4577
0
}
4578
4579
void pdf_set_annot_stamp_image(fz_context *ctx, pdf_annot *annot, fz_image *img)
4580
0
{
4581
0
  pdf_obj *ref = pdf_add_image(ctx, annot->page->doc, img);
4582
0
  fz_try(ctx)
4583
0
    pdf_set_annot_stamp_image_obj(ctx, annot, ref);
4584
0
  fz_always(ctx)
4585
0
    pdf_drop_obj(ctx, ref);
4586
0
  fz_catch(ctx)
4587
0
    fz_rethrow(ctx);
4588
0
}
4589
4590
static pdf_obj *filespec_subtypes[] = {
4591
  PDF_NAME(FileAttachment),
4592
  NULL,
4593
};
4594
4595
int
4596
pdf_annot_has_filespec(fz_context *ctx, pdf_annot *annot)
4597
0
{
4598
0
  return is_allowed_subtype_wrap(ctx, annot, PDF_NAME(FS), filespec_subtypes);
4599
0
}
4600
4601
pdf_obj *
4602
pdf_annot_filespec(fz_context *ctx, pdf_annot *annot)
4603
0
{
4604
0
  pdf_obj *filespec;
4605
4606
0
  pdf_annot_push_local_xref(ctx, annot);
4607
4608
0
  fz_try(ctx)
4609
0
  {
4610
0
    check_allowed_subtypes(ctx, annot, PDF_NAME(FS), filespec_subtypes);
4611
0
    filespec = pdf_dict_get(ctx, annot->obj, PDF_NAME(FS));
4612
0
  }
4613
0
  fz_always(ctx)
4614
0
    pdf_annot_pop_local_xref(ctx, annot);
4615
0
  fz_catch(ctx)
4616
0
    fz_rethrow(ctx);
4617
4618
0
  return filespec;
4619
0
}
4620
4621
void
4622
pdf_set_annot_filespec(fz_context *ctx, pdf_annot *annot, pdf_obj *fs)
4623
0
{
4624
0
  if (fs != PDF_NULL && !pdf_is_embedded_file(ctx, fs))
4625
0
    fz_throw(ctx, FZ_ERROR_ARGUMENT, "cannot set non-filespec as annotation filespec");
4626
4627
0
  begin_annot_op(ctx, annot, "Set filespec");
4628
4629
0
  fz_try(ctx)
4630
0
  {
4631
0
    check_allowed_subtypes(ctx, annot, PDF_NAME(M), filespec_subtypes);
4632
0
    pdf_dict_put(ctx, pdf_annot_obj(ctx, annot), PDF_NAME(FS), fs);
4633
0
    end_annot_op(ctx, annot);
4634
0
  }
4635
0
  fz_catch(ctx)
4636
0
  {
4637
0
    abandon_annot_op(ctx, annot);
4638
0
    fz_rethrow(ctx);
4639
0
  }
4640
4641
0
  pdf_dirty_annot(ctx, annot);
4642
0
}
4643
4644
int
4645
pdf_annot_hidden_for_editing(fz_context *ctx, pdf_annot *annot)
4646
0
{
4647
0
  return annot->hidden_editing;
4648
0
}
4649
4650
void
4651
pdf_set_annot_hidden_for_editing(fz_context *ctx, pdf_annot *annot, int hidden)
4652
0
{
4653
0
  annot->hidden_editing = hidden;
4654
0
}