Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
7639 views
1
#include "mupdf/pdf.h"
2
3
static pdf_obj *
4
resolve_dest_rec(fz_context *ctx, pdf_document *doc, pdf_obj *dest, fz_link_kind kind, int depth)
5
{
6
if (depth > 10) /* Arbitrary to avoid infinite recursion */
7
return NULL;
8
9
if (pdf_is_name(ctx, dest) || pdf_is_string(ctx, dest))
10
{
11
if (kind == FZ_LINK_GOTO)
12
{
13
dest = pdf_lookup_dest(ctx, doc, dest);
14
dest = resolve_dest_rec(ctx, doc, dest, kind, depth+1);
15
}
16
17
return dest;
18
}
19
20
else if (pdf_is_array(ctx, dest))
21
{
22
return dest;
23
}
24
25
else if (pdf_is_dict(ctx, dest))
26
{
27
dest = pdf_dict_get(ctx, dest, PDF_NAME_D);
28
return resolve_dest_rec(ctx, doc, dest, kind, depth+1);
29
}
30
31
else if (pdf_is_indirect(ctx, dest))
32
return dest;
33
34
return NULL;
35
}
36
37
static pdf_obj *
38
resolve_dest(fz_context *ctx, pdf_document *doc, pdf_obj *dest, fz_link_kind kind)
39
{
40
return resolve_dest_rec(ctx, doc, dest, kind, 0);
41
}
42
43
fz_link_dest
44
pdf_parse_link_dest(fz_context *ctx, pdf_document *doc, fz_link_kind kind, pdf_obj *dest)
45
{
46
fz_link_dest ld;
47
pdf_obj *obj;
48
49
int l_from_2 = 0;
50
int b_from_3 = 0;
51
int r_from_4 = 0;
52
int t_from_5 = 0;
53
int t_from_3 = 0;
54
int t_from_2 = 0;
55
int z_from_4 = 0;
56
57
ld.kind = kind;
58
ld.ld.gotor.flags = 0;
59
ld.ld.gotor.lt.x = 0;
60
ld.ld.gotor.lt.y = 0;
61
ld.ld.gotor.rb.x = 0;
62
ld.ld.gotor.rb.y = 0;
63
ld.ld.gotor.page = -1;
64
ld.ld.gotor.dest = NULL;
65
66
dest = resolve_dest(ctx, doc, dest, kind);
67
if (dest == NULL)
68
{
69
fz_warn(ctx, "undefined link destination");
70
return ld;
71
}
72
73
if (pdf_is_name(ctx, dest))
74
{
75
ld.ld.gotor.dest = pdf_to_name(ctx, dest);
76
return ld;
77
}
78
else if (pdf_is_string(ctx, dest))
79
{
80
ld.ld.gotor.dest = pdf_to_str_buf(ctx, dest);
81
return ld;
82
}
83
84
obj = pdf_array_get(ctx, dest, 0);
85
if (pdf_is_int(ctx, obj))
86
ld.ld.gotor.page = pdf_to_int(ctx, obj);
87
else
88
{
89
fz_try(ctx)
90
{
91
ld.ld.gotor.page = pdf_lookup_page_number(ctx, doc, obj);
92
}
93
fz_catch(ctx)
94
{
95
ld.kind = FZ_LINK_NONE;
96
return ld;
97
}
98
}
99
100
obj = pdf_array_get(ctx, dest, 1);
101
if (!pdf_is_name(ctx, obj))
102
return ld;
103
104
if (pdf_name_eq(ctx, PDF_NAME_XYZ, obj))
105
{
106
l_from_2 = t_from_3 = z_from_4 = 1;
107
ld.ld.gotor.flags |= fz_link_flag_r_is_zoom;
108
}
109
else if ((pdf_name_eq(ctx, PDF_NAME_Fit, obj)) || (pdf_name_eq(ctx, PDF_NAME_FitB, obj)))
110
{
111
ld.ld.gotor.flags |= fz_link_flag_fit_h;
112
ld.ld.gotor.flags |= fz_link_flag_fit_v;
113
}
114
else if ((pdf_name_eq(ctx, PDF_NAME_FitH, obj)) || (pdf_name_eq(ctx, PDF_NAME_FitBH, obj)))
115
{
116
t_from_2 = 1;
117
ld.ld.gotor.flags |= fz_link_flag_fit_h;
118
}
119
else if ((pdf_name_eq(ctx, PDF_NAME_FitV, obj)) || (pdf_name_eq(ctx, PDF_NAME_FitBV, obj)))
120
{
121
l_from_2 = 1;
122
ld.ld.gotor.flags |= fz_link_flag_fit_v;
123
}
124
else if (pdf_name_eq(ctx, PDF_NAME_FitR, obj))
125
{
126
l_from_2 = b_from_3 = r_from_4 = t_from_5 = 1;
127
ld.ld.gotor.flags |= fz_link_flag_fit_h;
128
ld.ld.gotor.flags |= fz_link_flag_fit_v;
129
}
130
131
if (l_from_2)
132
{
133
obj = pdf_array_get(ctx, dest, 2);
134
if (pdf_is_int(ctx, obj))
135
{
136
ld.ld.gotor.flags |= fz_link_flag_l_valid;
137
ld.ld.gotor.lt.x = pdf_to_int(ctx, obj);
138
}
139
else if (pdf_is_real(ctx, obj))
140
{
141
ld.ld.gotor.flags |= fz_link_flag_l_valid;
142
ld.ld.gotor.lt.x = pdf_to_real(ctx, obj);
143
}
144
}
145
if (b_from_3)
146
{
147
obj = pdf_array_get(ctx, dest, 3);
148
if (pdf_is_int(ctx, obj))
149
{
150
ld.ld.gotor.flags |= fz_link_flag_b_valid;
151
ld.ld.gotor.rb.y = pdf_to_int(ctx, obj);
152
}
153
else if (pdf_is_real(ctx, obj))
154
{
155
ld.ld.gotor.flags |= fz_link_flag_b_valid;
156
ld.ld.gotor.rb.y = pdf_to_real(ctx, obj);
157
}
158
}
159
if (r_from_4)
160
{
161
obj = pdf_array_get(ctx, dest, 4);
162
if (pdf_is_int(ctx, obj))
163
{
164
ld.ld.gotor.flags |= fz_link_flag_r_valid;
165
ld.ld.gotor.rb.x = pdf_to_int(ctx, obj);
166
}
167
else if (pdf_is_real(ctx, obj))
168
{
169
ld.ld.gotor.flags |= fz_link_flag_r_valid;
170
ld.ld.gotor.rb.x = pdf_to_real(ctx, obj);
171
}
172
}
173
if (t_from_5 || t_from_3 || t_from_2)
174
{
175
if (t_from_5)
176
obj = pdf_array_get(ctx, dest, 5);
177
else if (t_from_3)
178
obj = pdf_array_get(ctx, dest, 3);
179
else
180
obj = pdf_array_get(ctx, dest, 2);
181
if (pdf_is_int(ctx, obj))
182
{
183
ld.ld.gotor.flags |= fz_link_flag_t_valid;
184
ld.ld.gotor.lt.y = pdf_to_int(ctx, obj);
185
}
186
else if (pdf_is_real(ctx, obj))
187
{
188
ld.ld.gotor.flags |= fz_link_flag_t_valid;
189
ld.ld.gotor.lt.y = pdf_to_real(ctx, obj);
190
}
191
}
192
if (z_from_4)
193
{
194
obj = pdf_array_get(ctx, dest, 4);
195
if (pdf_is_int(ctx, obj))
196
{
197
ld.ld.gotor.flags |= fz_link_flag_r_valid;
198
ld.ld.gotor.rb.x = pdf_to_int(ctx, obj);
199
}
200
else if (pdf_is_real(ctx, obj))
201
{
202
ld.ld.gotor.flags |= fz_link_flag_r_valid;
203
ld.ld.gotor.rb.x = pdf_to_real(ctx, obj);
204
}
205
}
206
207
/* Duplicate the values out for the sake of stupid clients */
208
if ((ld.ld.gotor.flags & (fz_link_flag_l_valid | fz_link_flag_r_valid)) == fz_link_flag_l_valid)
209
ld.ld.gotor.rb.x = ld.ld.gotor.lt.x;
210
if ((ld.ld.gotor.flags & (fz_link_flag_l_valid | fz_link_flag_r_valid | fz_link_flag_r_is_zoom)) == fz_link_flag_r_valid)
211
ld.ld.gotor.lt.x = ld.ld.gotor.rb.x;
212
if ((ld.ld.gotor.flags & (fz_link_flag_t_valid | fz_link_flag_b_valid)) == fz_link_flag_t_valid)
213
ld.ld.gotor.rb.y = ld.ld.gotor.lt.y;
214
if ((ld.ld.gotor.flags & (fz_link_flag_t_valid | fz_link_flag_b_valid)) == fz_link_flag_b_valid)
215
ld.ld.gotor.lt.y = ld.ld.gotor.rb.y;
216
217
return ld;
218
}
219
220
char *
221
pdf_parse_file_spec(fz_context *ctx, pdf_document *doc, pdf_obj *file_spec)
222
{
223
pdf_obj *filename=NULL;
224
char *path = NULL;
225
226
if (pdf_is_string(ctx, file_spec))
227
filename = file_spec;
228
229
if (pdf_is_dict(ctx, file_spec)) {
230
#if defined(_WIN32) || defined(_WIN64)
231
filename = pdf_dict_get(ctx, file_spec, PDF_NAME_DOS);
232
#else
233
filename = pdf_dict_get(ctx, file_spec, PDF_NAME_Unix);
234
#endif
235
if (!filename)
236
filename = pdf_dict_geta(ctx, file_spec, PDF_NAME_UF, PDF_NAME_F);
237
}
238
239
if (!pdf_is_string(ctx, filename))
240
{
241
fz_warn(ctx, "cannot parse file specification");
242
return NULL;
243
}
244
245
path = pdf_to_utf8(ctx, doc, filename);
246
#if defined(_WIN32) || defined(_WIN64)
247
if (strcmp(pdf_to_name(ctx, pdf_dict_gets(ctx, file_spec, "FS")), "URL") != 0)
248
{
249
/* move the file name into the expected place and use the expected path separator */
250
char *c;
251
if (path[0] == '/' && (('A' <= path[1] && path[1] <= 'Z') || ('a' <= path[1] && path[1] <= 'z')) && path[2] == '/')
252
{
253
path[0] = path[1];
254
path[1] = ':';
255
}
256
for (c = path; *c; c++)
257
{
258
if (*c == '/')
259
*c = '\\';
260
}
261
}
262
#endif
263
return path;
264
}
265
266
fz_link_dest
267
pdf_parse_action(fz_context *ctx, pdf_document *doc, pdf_obj *action)
268
{
269
fz_link_dest ld;
270
pdf_obj *obj, *dest, *file_spec;
271
272
ld.kind = FZ_LINK_NONE;
273
274
if (!action)
275
return ld;
276
277
obj = pdf_dict_get(ctx, action, PDF_NAME_S);
278
if (pdf_name_eq(ctx, PDF_NAME_GoTo, obj))
279
{
280
dest = pdf_dict_get(ctx, action, PDF_NAME_D);
281
ld = pdf_parse_link_dest(ctx, doc, FZ_LINK_GOTO, dest);
282
}
283
else if (pdf_name_eq(ctx, PDF_NAME_URI, obj))
284
{
285
ld.kind = FZ_LINK_URI;
286
ld.ld.uri.is_map = pdf_to_bool(ctx, pdf_dict_get(ctx, action, PDF_NAME_IsMap));
287
ld.ld.uri.uri = pdf_to_utf8(ctx, doc, pdf_dict_get(ctx, action, PDF_NAME_URI));
288
}
289
else if (pdf_name_eq(ctx, PDF_NAME_Launch, obj))
290
{
291
ld.kind = FZ_LINK_LAUNCH;
292
file_spec = pdf_dict_get(ctx, action, PDF_NAME_F);
293
ld.ld.launch.file_spec = pdf_parse_file_spec(ctx, doc, file_spec);
294
ld.ld.launch.new_window = pdf_to_int(ctx, pdf_dict_get(ctx, action, PDF_NAME_NewWindow));
295
ld.ld.launch.is_uri = pdf_name_eq(ctx, PDF_NAME_URL, pdf_dict_get(ctx, file_spec, PDF_NAME_FS));
296
}
297
else if (pdf_name_eq(ctx, PDF_NAME_Named, obj))
298
{
299
ld.kind = FZ_LINK_NAMED;
300
ld.ld.named.named = fz_strdup(ctx, pdf_to_name(ctx, pdf_dict_get(ctx, action, PDF_NAME_N)));
301
}
302
else if (pdf_name_eq(ctx, PDF_NAME_GoToR, obj))
303
{
304
dest = pdf_dict_get(ctx, action, PDF_NAME_D);
305
file_spec = pdf_dict_get(ctx, action, PDF_NAME_F);
306
ld = pdf_parse_link_dest(ctx, doc, FZ_LINK_GOTOR, dest);
307
ld.ld.gotor.file_spec = pdf_parse_file_spec(ctx, doc, file_spec);
308
ld.ld.gotor.new_window = pdf_to_int(ctx, pdf_dict_get(ctx, action, PDF_NAME_NewWindow));
309
}
310
return ld;
311
}
312
313
static fz_link *
314
pdf_load_link(fz_context *ctx, pdf_document *doc, pdf_obj *dict, const fz_matrix *page_ctm)
315
{
316
pdf_obj *action;
317
pdf_obj *obj;
318
fz_rect bbox;
319
fz_link_dest ld;
320
321
obj = pdf_dict_get(ctx, dict, PDF_NAME_Rect);
322
if (obj)
323
pdf_to_rect(ctx, obj, &bbox);
324
else
325
bbox = fz_empty_rect;
326
327
fz_transform_rect(&bbox, page_ctm);
328
329
obj = pdf_dict_get(ctx, dict, PDF_NAME_Dest);
330
if (obj)
331
ld = pdf_parse_link_dest(ctx, doc, FZ_LINK_GOTO, obj);
332
else
333
{
334
action = pdf_dict_get(ctx, dict, PDF_NAME_A);
335
/* fall back to additional action button's down/up action */
336
if (!action)
337
action = pdf_dict_geta(ctx, pdf_dict_get(ctx, dict, PDF_NAME_AA), PDF_NAME_U, PDF_NAME_D);
338
339
ld = pdf_parse_action(ctx, doc, action);
340
}
341
if (ld.kind == FZ_LINK_NONE)
342
return NULL;
343
return fz_new_link(ctx, &bbox, ld);
344
}
345
346
fz_link *
347
pdf_load_link_annots(fz_context *ctx, pdf_document *doc, pdf_obj *annots, const fz_matrix *page_ctm)
348
{
349
fz_link *link, *head, *tail;
350
pdf_obj *obj;
351
int i, n;
352
353
head = tail = NULL;
354
link = NULL;
355
356
n = pdf_array_len(ctx, annots);
357
for (i = 0; i < n; i++)
358
{
359
/* FIXME: Move the try/catch out of the loop for performance? */
360
fz_try(ctx)
361
{
362
obj = pdf_array_get(ctx, annots, i);
363
link = pdf_load_link(ctx, doc, obj, page_ctm);
364
}
365
fz_catch(ctx)
366
{
367
fz_rethrow_if(ctx, FZ_ERROR_TRYLATER);
368
link = NULL;
369
}
370
371
if (link)
372
{
373
if (!head)
374
head = tail = link;
375
else
376
{
377
tail->next = link;
378
tail = link;
379
}
380
}
381
}
382
383
return head;
384
}
385
386
void
387
pdf_drop_annot(fz_context *ctx, pdf_annot *annot)
388
{
389
pdf_annot *next;
390
391
while (annot)
392
{
393
next = annot->next;
394
if (annot->ap)
395
pdf_drop_xobject(ctx, annot->ap);
396
pdf_drop_obj(ctx, annot->obj);
397
fz_free(ctx, annot);
398
annot = next;
399
}
400
}
401
402
void
403
pdf_transform_annot(fz_context *ctx, pdf_annot *annot)
404
{
405
fz_rect bbox = annot->ap->bbox;
406
fz_rect rect = annot->rect;
407
float w, h, x, y;
408
409
fz_transform_rect(&bbox, &annot->ap->matrix);
410
if (bbox.x1 == bbox.x0)
411
w = 0;
412
else
413
w = (rect.x1 - rect.x0) / (bbox.x1 - bbox.x0);
414
if (bbox.y1 == bbox.y0)
415
h = 0;
416
else
417
h = (rect.y1 - rect.y0) / (bbox.y1 - bbox.y0);
418
x = rect.x0 - bbox.x0;
419
y = rect.y0 - bbox.y0;
420
421
fz_pre_scale(fz_translate(&annot->matrix, x, y), w, h);
422
}
423
424
fz_annot_type pdf_annot_obj_type(fz_context *ctx, pdf_obj *obj)
425
{
426
pdf_obj *subtype = pdf_dict_get(ctx, obj, PDF_NAME_Subtype);
427
if (pdf_name_eq(ctx, PDF_NAME_Text, subtype))
428
return FZ_ANNOT_TEXT;
429
else if (pdf_name_eq(ctx, PDF_NAME_Link, subtype))
430
return FZ_ANNOT_LINK;
431
else if (pdf_name_eq(ctx, PDF_NAME_FreeText, subtype))
432
return FZ_ANNOT_FREETEXT;
433
else if (pdf_name_eq(ctx, PDF_NAME_Line, subtype))
434
return FZ_ANNOT_LINE;
435
else if (pdf_name_eq(ctx, PDF_NAME_Square, subtype))
436
return FZ_ANNOT_SQUARE;
437
else if (pdf_name_eq(ctx, PDF_NAME_Circle, subtype))
438
return FZ_ANNOT_CIRCLE;
439
else if (pdf_name_eq(ctx, PDF_NAME_Polygon, subtype))
440
return FZ_ANNOT_POLYGON;
441
else if (pdf_name_eq(ctx, PDF_NAME_PolyLine, subtype))
442
return FZ_ANNOT_POLYLINE;
443
else if (pdf_name_eq(ctx, PDF_NAME_Highlight, subtype))
444
return FZ_ANNOT_HIGHLIGHT;
445
else if (pdf_name_eq(ctx, PDF_NAME_Underline, subtype))
446
return FZ_ANNOT_UNDERLINE;
447
else if (pdf_name_eq(ctx, PDF_NAME_Squiggly, subtype))
448
return FZ_ANNOT_SQUIGGLY;
449
else if (pdf_name_eq(ctx, PDF_NAME_StrikeOut, subtype))
450
return FZ_ANNOT_STRIKEOUT;
451
else if (pdf_name_eq(ctx, PDF_NAME_Stamp, subtype))
452
return FZ_ANNOT_STAMP;
453
else if (pdf_name_eq(ctx, PDF_NAME_Caret, subtype))
454
return FZ_ANNOT_CARET;
455
else if (pdf_name_eq(ctx, PDF_NAME_Ink, subtype))
456
return FZ_ANNOT_INK;
457
else if (pdf_name_eq(ctx, PDF_NAME_Popup, subtype))
458
return FZ_ANNOT_POPUP;
459
else if (pdf_name_eq(ctx, PDF_NAME_FileAttachment, subtype))
460
return FZ_ANNOT_FILEATTACHMENT;
461
else if (pdf_name_eq(ctx, PDF_NAME_Sound, subtype))
462
return FZ_ANNOT_SOUND;
463
else if (pdf_name_eq(ctx, PDF_NAME_Movie, subtype))
464
return FZ_ANNOT_MOVIE;
465
else if (pdf_name_eq(ctx, PDF_NAME_Widget, subtype))
466
return FZ_ANNOT_WIDGET;
467
else if (pdf_name_eq(ctx, PDF_NAME_Screen, subtype))
468
return FZ_ANNOT_SCREEN;
469
else if (pdf_name_eq(ctx, PDF_NAME_PrinterMark, subtype))
470
return FZ_ANNOT_PRINTERMARK;
471
else if (pdf_name_eq(ctx, PDF_NAME_TrapNet, subtype))
472
return FZ_ANNOT_TRAPNET;
473
else if (pdf_name_eq(ctx, PDF_NAME_Watermark, subtype))
474
return FZ_ANNOT_WATERMARK;
475
else if (pdf_name_eq(ctx, PDF_NAME_3D, subtype))
476
return FZ_ANNOT_3D;
477
else
478
return -1;
479
}
480
481
void
482
pdf_load_annots(fz_context *ctx, pdf_document *doc, pdf_page *page, pdf_obj *annots)
483
{
484
pdf_annot *annot, **itr;
485
pdf_obj *obj, *ap, *as, *n, *rect;
486
int i, len, keep_annot;
487
488
fz_var(annot);
489
fz_var(itr);
490
fz_var(keep_annot);
491
492
itr = &page->annots;
493
494
len = pdf_array_len(ctx, annots);
495
/*
496
Create an initial linked list of pdf_annot structures with only the obj field
497
filled in. We do this because update_appearance has the potential to change
498
the annot array, so we don't want to be iterating through the array while
499
that happens.
500
*/
501
fz_try(ctx)
502
{
503
for (i = 0; i < len; i++)
504
{
505
obj = pdf_array_get(ctx, annots, i);
506
annot = fz_malloc_struct(ctx, pdf_annot);
507
annot->obj = pdf_keep_obj(ctx, obj);
508
annot->page = page;
509
annot->next = NULL;
510
511
*itr = annot;
512
itr = &annot->next;
513
}
514
}
515
fz_catch(ctx)
516
{
517
pdf_drop_annot(ctx, page->annots);
518
page->annots = NULL;
519
fz_rethrow(ctx);
520
}
521
522
/*
523
Iterate through the newly created annot linked list, using a double pointer to
524
facilitate deleting broken annotations.
525
*/
526
itr = &page->annots;
527
while (*itr)
528
{
529
annot = *itr;
530
531
fz_try(ctx)
532
{
533
pdf_hotspot *hp = &doc->hotspot;
534
535
n = NULL;
536
537
if (doc->update_appearance)
538
doc->update_appearance(ctx, doc, annot);
539
540
obj = annot->obj;
541
rect = pdf_dict_get(ctx, obj, PDF_NAME_Rect);
542
ap = pdf_dict_get(ctx, obj, PDF_NAME_AP);
543
as = pdf_dict_get(ctx, obj, PDF_NAME_AS);
544
545
/* We only collect annotations with an appearance
546
* stream into this list, so remove any that don't
547
* (such as links) and continue. */
548
keep_annot = pdf_is_dict(ctx, ap);
549
if (!keep_annot)
550
break;
551
552
if (hp->num == pdf_to_num(ctx, obj)
553
&& hp->gen == pdf_to_gen(ctx, obj)
554
&& (hp->state & HOTSPOT_POINTER_DOWN))
555
{
556
n = pdf_dict_get(ctx, ap, PDF_NAME_D); /* down state */
557
}
558
559
if (n == NULL)
560
n = pdf_dict_get(ctx, ap, PDF_NAME_N); /* normal state */
561
562
/* lookup current state in sub-dictionary */
563
if (!pdf_is_stream(ctx, doc, pdf_to_num(ctx, n), pdf_to_gen(ctx, n)))
564
n = pdf_dict_get(ctx, n, as);
565
566
pdf_to_rect(ctx, rect, &annot->rect);
567
annot->pagerect = annot->rect;
568
fz_transform_rect(&annot->pagerect, &page->ctm);
569
annot->ap = NULL;
570
annot->annot_type = pdf_annot_obj_type(ctx, obj);
571
annot->widget_type = annot->annot_type == FZ_ANNOT_WIDGET ? pdf_field_type(ctx, doc, obj) : PDF_WIDGET_TYPE_NOT_WIDGET;
572
573
if (pdf_is_stream(ctx, doc, pdf_to_num(ctx, n), pdf_to_gen(ctx, n)))
574
{
575
annot->ap = pdf_load_xobject(ctx, doc, n);
576
pdf_transform_annot(ctx, annot);
577
annot->ap_iteration = annot->ap->iteration;
578
}
579
580
if (obj == doc->focus_obj)
581
doc->focus = annot;
582
583
/* Move to next item in the linked list */
584
itr = &annot->next;
585
}
586
fz_catch(ctx)
587
{
588
if (fz_caught(ctx) == FZ_ERROR_TRYLATER)
589
{
590
pdf_drop_annot(ctx, page->annots);
591
page->annots = NULL;
592
fz_rethrow(ctx);
593
}
594
keep_annot = 0;
595
fz_warn(ctx, "ignoring broken annotation");
596
}
597
if (!keep_annot)
598
{
599
/* Move to next item in the linked list, dropping this one */
600
*itr = annot->next;
601
annot->next = NULL; /* Required because pdf_drop_annot follows the "next" chain */
602
pdf_drop_annot(ctx, annot);
603
}
604
}
605
606
page->annot_tailp = itr;
607
}
608
609
pdf_annot *
610
pdf_first_annot(fz_context *ctx, pdf_page *page)
611
{
612
return page ? page->annots : NULL;
613
}
614
615
pdf_annot *
616
pdf_next_annot(fz_context *ctx, pdf_page *page, pdf_annot *annot)
617
{
618
return annot ? annot->next : NULL;
619
}
620
621
fz_rect *
622
pdf_bound_annot(fz_context *ctx, pdf_page *page, pdf_annot *annot, fz_rect *rect)
623
{
624
if (rect == NULL)
625
return NULL;
626
627
if (annot)
628
*rect = annot->pagerect;
629
else
630
*rect = fz_empty_rect;
631
return rect;
632
}
633
634
fz_annot_type
635
pdf_annot_type(fz_context *ctx, pdf_annot *annot)
636
{
637
return annot->annot_type;
638
}
639
640