Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
7639 views
1
#include "mupdf/pdf.h"
2
3
#define TEXT_ANNOT_SIZE (25.0)
4
5
static const char *annot_type_str(fz_annot_type type)
6
{
7
switch (type)
8
{
9
case FZ_ANNOT_TEXT: return "Text";
10
case FZ_ANNOT_LINK: return "Link";
11
case FZ_ANNOT_FREETEXT: return "FreeText";
12
case FZ_ANNOT_LINE: return "Line";
13
case FZ_ANNOT_SQUARE: return "Square";
14
case FZ_ANNOT_CIRCLE: return "Circle";
15
case FZ_ANNOT_POLYGON: return "Polygon";
16
case FZ_ANNOT_POLYLINE: return "PolyLine";
17
case FZ_ANNOT_HIGHLIGHT: return "Highlight";
18
case FZ_ANNOT_UNDERLINE: return "Underline";
19
case FZ_ANNOT_SQUIGGLY: return "Squiggly";
20
case FZ_ANNOT_STRIKEOUT: return "StrikeOut";
21
case FZ_ANNOT_STAMP: return "Stamp";
22
case FZ_ANNOT_CARET: return "Caret";
23
case FZ_ANNOT_INK: return "Ink";
24
case FZ_ANNOT_POPUP: return "Popup";
25
case FZ_ANNOT_FILEATTACHMENT: return "FileAttachment";
26
case FZ_ANNOT_SOUND: return "Sound";
27
case FZ_ANNOT_MOVIE: return "Movie";
28
case FZ_ANNOT_WIDGET: return "Widget";
29
case FZ_ANNOT_SCREEN: return "Screen";
30
case FZ_ANNOT_PRINTERMARK: return "PrinterMark";
31
case FZ_ANNOT_TRAPNET: return "TrapNet";
32
case FZ_ANNOT_WATERMARK: return "Watermark";
33
case FZ_ANNOT_3D: return "3D";
34
default: return "";
35
}
36
}
37
38
void
39
pdf_update_annot(fz_context *ctx, pdf_document *doc, pdf_annot *annot)
40
{
41
pdf_obj *obj, *ap, *as, *n;
42
43
if (doc->update_appearance)
44
doc->update_appearance(ctx, doc, annot);
45
46
obj = annot->obj;
47
48
ap = pdf_dict_get(ctx, obj, PDF_NAME_AP);
49
as = pdf_dict_get(ctx, obj, PDF_NAME_AS);
50
51
if (pdf_is_dict(ctx, ap))
52
{
53
pdf_hotspot *hp = &doc->hotspot;
54
55
n = NULL;
56
57
if (hp->num == pdf_to_num(ctx, obj)
58
&& hp->gen == pdf_to_gen(ctx, obj)
59
&& (hp->state & HOTSPOT_POINTER_DOWN))
60
{
61
n = pdf_dict_get(ctx, ap, PDF_NAME_D); /* down state */
62
}
63
64
if (n == NULL)
65
n = pdf_dict_get(ctx, ap, PDF_NAME_N); /* normal state */
66
67
/* lookup current state in sub-dictionary */
68
if (!pdf_is_stream(ctx, doc, pdf_to_num(ctx, n), pdf_to_gen(ctx, n)))
69
n = pdf_dict_get(ctx, n, as);
70
71
pdf_drop_xobject(ctx, annot->ap);
72
annot->ap = NULL;
73
74
if (pdf_is_stream(ctx, doc, pdf_to_num(ctx, n), pdf_to_gen(ctx, n)))
75
{
76
fz_try(ctx)
77
{
78
annot->ap = pdf_load_xobject(ctx, doc, n);
79
pdf_transform_annot(ctx, annot);
80
annot->ap_iteration = annot->ap->iteration;
81
}
82
fz_catch(ctx)
83
{
84
fz_rethrow_if(ctx, FZ_ERROR_TRYLATER);
85
fz_warn(ctx, "ignoring broken annotation");
86
}
87
}
88
}
89
}
90
91
pdf_annot *
92
pdf_create_annot(fz_context *ctx, pdf_document *doc, pdf_page *page, fz_annot_type type)
93
{
94
pdf_annot *annot = NULL;
95
pdf_obj *annot_obj = pdf_new_dict(ctx, doc, 0);
96
pdf_obj *ind_obj = NULL;
97
98
fz_var(annot);
99
fz_var(ind_obj);
100
fz_try(ctx)
101
{
102
int ind_obj_num;
103
fz_rect rect = {0.0, 0.0, 0.0, 0.0};
104
const char *type_str = annot_type_str(type);
105
pdf_obj *annot_arr = pdf_dict_get(ctx, page->me, PDF_NAME_Annots);
106
if (annot_arr == NULL)
107
{
108
annot_arr = pdf_new_array(ctx, doc, 0);
109
pdf_dict_put_drop(ctx, page->me, PDF_NAME_Annots, annot_arr);
110
}
111
112
pdf_dict_put_drop(ctx, annot_obj, PDF_NAME_Type, PDF_NAME_Annot);
113
114
pdf_dict_put_drop(ctx, annot_obj, PDF_NAME_Subtype, pdf_new_name(ctx, doc, type_str));
115
pdf_dict_put_drop(ctx, annot_obj, PDF_NAME_Rect, pdf_new_rect(ctx, doc, &rect));
116
117
/* Make printable as default */
118
pdf_dict_put_drop(ctx, annot_obj, PDF_NAME_F, pdf_new_int(ctx, doc, F_Print));
119
120
annot = fz_malloc_struct(ctx, pdf_annot);
121
annot->page = page;
122
annot->rect = rect;
123
annot->pagerect = rect;
124
annot->ap = NULL;
125
annot->widget_type = PDF_WIDGET_TYPE_NOT_WIDGET;
126
annot->annot_type = type;
127
128
/*
129
Both annotation object and annotation structure are now created.
130
Insert the object in the hierarchy and the structure in the
131
page's array.
132
*/
133
ind_obj_num = pdf_create_object(ctx, doc);
134
pdf_update_object(ctx, doc, ind_obj_num, annot_obj);
135
ind_obj = pdf_new_indirect(ctx, doc, ind_obj_num, 0);
136
pdf_array_push(ctx, annot_arr, ind_obj);
137
annot->obj = pdf_keep_obj(ctx, ind_obj);
138
139
/*
140
Linking must be done after any call that might throw because
141
pdf_drop_annot below actually frees a list. Put the new annot
142
at the end of the list, so that it will be drawn last.
143
*/
144
*page->annot_tailp = annot;
145
page->annot_tailp = &annot->next;
146
147
doc->dirty = 1;
148
}
149
fz_always(ctx)
150
{
151
pdf_drop_obj(ctx, annot_obj);
152
pdf_drop_obj(ctx, ind_obj);
153
}
154
fz_catch(ctx)
155
{
156
pdf_drop_annot(ctx, annot);
157
fz_rethrow(ctx);
158
}
159
160
return annot;
161
}
162
163
void
164
pdf_delete_annot(fz_context *ctx, pdf_document *doc, pdf_page *page, pdf_annot *annot)
165
{
166
pdf_annot **annotptr;
167
pdf_obj *old_annot_arr;
168
pdf_obj *annot_arr;
169
170
if (annot == NULL)
171
return;
172
173
/* Remove annot from page's list */
174
for (annotptr = &page->annots; *annotptr; annotptr = &(*annotptr)->next)
175
{
176
if (*annotptr == annot)
177
break;
178
}
179
180
/* Check the passed annotation was of this page */
181
if (*annotptr == NULL)
182
return;
183
184
*annotptr = annot->next;
185
/* If the removed annotation was the last in the list adjust the end pointer */
186
if (*annotptr == NULL)
187
page->annot_tailp = annotptr;
188
189
/* Stick it in the deleted list */
190
annot->next = page->deleted_annots;
191
page->deleted_annots = annot;
192
193
pdf_drop_xobject(ctx, annot->ap);
194
annot->ap = NULL;
195
196
/* Recreate the "Annots" array with this annot removed */
197
old_annot_arr = pdf_dict_get(ctx, page->me, PDF_NAME_Annots);
198
199
if (old_annot_arr)
200
{
201
int i, n = pdf_array_len(ctx, old_annot_arr);
202
annot_arr = pdf_new_array(ctx, doc, n?(n-1):0);
203
204
fz_try(ctx)
205
{
206
for (i = 0; i < n; i++)
207
{
208
pdf_obj *obj = pdf_array_get(ctx, old_annot_arr, i);
209
210
if (obj != annot->obj)
211
pdf_array_push(ctx, annot_arr, obj);
212
}
213
214
if (pdf_is_indirect(ctx, old_annot_arr))
215
pdf_update_object(ctx, doc, pdf_to_num(ctx, old_annot_arr), annot_arr);
216
else
217
pdf_dict_put(ctx, page->me, PDF_NAME_Annots, annot_arr);
218
219
if (pdf_is_indirect(ctx, annot->obj))
220
pdf_delete_object(ctx, doc, pdf_to_num(ctx, annot->obj));
221
}
222
fz_always(ctx)
223
{
224
pdf_drop_obj(ctx, annot_arr);
225
}
226
fz_catch(ctx)
227
{
228
fz_rethrow(ctx);
229
}
230
}
231
232
pdf_drop_obj(ctx, annot->obj);
233
annot->obj = NULL;
234
doc->dirty = 1;
235
}
236
237
void
238
pdf_set_markup_annot_quadpoints(fz_context *ctx, pdf_document *doc, pdf_annot *annot, fz_point *qp, int n)
239
{
240
fz_matrix ctm;
241
pdf_obj *arr = pdf_new_array(ctx, doc, n*2);
242
int i;
243
244
fz_invert_matrix(&ctm, &annot->page->ctm);
245
246
pdf_dict_put_drop(ctx, annot->obj, PDF_NAME_QuadPoints, arr);
247
248
for (i = 0; i < n; i++)
249
{
250
fz_point pt = qp[i];
251
pdf_obj *r;
252
253
fz_transform_point(&pt, &ctm);
254
r = pdf_new_real(ctx, doc, pt.x);
255
pdf_array_push_drop(ctx, arr, r);
256
r = pdf_new_real(ctx, doc, pt.y);
257
pdf_array_push_drop(ctx, arr, r);
258
}
259
}
260
261
static void update_rect(fz_context *ctx, pdf_annot *annot)
262
{
263
pdf_to_rect(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME_Rect), &annot->rect);
264
annot->pagerect = annot->rect;
265
fz_transform_rect(&annot->pagerect, &annot->page->ctm);
266
}
267
268
void
269
pdf_set_ink_annot_list(fz_context *ctx, pdf_document *doc, pdf_annot *annot, fz_point *pts, int *counts, int ncount, float color[3], float thickness)
270
{
271
fz_matrix ctm;
272
pdf_obj *list = pdf_new_array(ctx, doc, ncount);
273
pdf_obj *bs, *col;
274
fz_rect rect;
275
int i, k = 0;
276
277
fz_invert_matrix(&ctm, &annot->page->ctm);
278
279
pdf_dict_put_drop(ctx, annot->obj, PDF_NAME_InkList, list);
280
281
for (i = 0; i < ncount; i++)
282
{
283
int j;
284
pdf_obj *arc = pdf_new_array(ctx, doc, counts[i]);
285
286
pdf_array_push_drop(ctx, list, arc);
287
288
for (j = 0; j < counts[i]; j++)
289
{
290
fz_point pt = pts[k];
291
292
fz_transform_point(&pt, &ctm);
293
294
if (i == 0 && j == 0)
295
{
296
rect.x0 = rect.x1 = pt.x;
297
rect.y0 = rect.y1 = pt.y;
298
}
299
else
300
{
301
fz_include_point_in_rect(&rect, &pt);
302
}
303
304
pdf_array_push_drop(ctx, arc, pdf_new_real(ctx, doc, pt.x));
305
pdf_array_push_drop(ctx, arc, pdf_new_real(ctx, doc, pt.y));
306
k++;
307
}
308
}
309
310
/*
311
Expand the rectangle by thickness all around. We cannot use
312
fz_expand_rect because the rectangle might be empty in the
313
single point case
314
*/
315
if (k > 0)
316
{
317
rect.x0 -= thickness;
318
rect.y0 -= thickness;
319
rect.x1 += thickness;
320
rect.y1 += thickness;
321
}
322
323
pdf_dict_put_drop(ctx, annot->obj, PDF_NAME_Rect, pdf_new_rect(ctx, doc, &rect));
324
update_rect(ctx, annot);
325
326
bs = pdf_new_dict(ctx, doc, 1);
327
pdf_dict_put_drop(ctx, annot->obj, PDF_NAME_BS, bs);
328
pdf_dict_put_drop(ctx, bs, PDF_NAME_W, pdf_new_real(ctx, doc, thickness));
329
330
col = pdf_new_array(ctx, doc, 3);
331
pdf_dict_put_drop(ctx, annot->obj, PDF_NAME_C, col);
332
for (i = 0; i < 3; i++)
333
pdf_array_push_drop(ctx, col, pdf_new_real(ctx, doc, color[i]));
334
}
335
336
static void find_free_font_name(fz_context *ctx, pdf_obj *fdict, char *buf, int buf_size)
337
{
338
int i;
339
340
/* Find a number X such that /FX doesn't occur as a key in fdict */
341
for (i = 0; 1; i++)
342
{
343
snprintf(buf, buf_size, "F%d", i);
344
345
if (!pdf_dict_gets(ctx, fdict, buf))
346
break;
347
}
348
}
349
350
void pdf_set_text_annot_position(fz_context *ctx, pdf_document *doc, pdf_annot *annot, fz_point pt)
351
{
352
fz_matrix ctm;
353
fz_rect rect;
354
int flags;
355
356
fz_invert_matrix(&ctm, &annot->page->ctm);
357
rect.x0 = pt.x;
358
rect.x1 = pt.x + TEXT_ANNOT_SIZE;
359
rect.y0 = pt.y;
360
rect.y1 = pt.y + TEXT_ANNOT_SIZE;
361
fz_transform_rect(&rect, &ctm);
362
363
pdf_dict_put_drop(ctx, annot->obj, PDF_NAME_Rect, pdf_new_rect(ctx, doc, &rect));
364
365
flags = pdf_to_int(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME_F));
366
flags |= (F_NoZoom|F_NoRotate);
367
pdf_dict_put_drop(ctx, annot->obj, PDF_NAME_F, pdf_new_int(ctx, doc, flags));
368
369
update_rect(ctx, annot);
370
}
371
372
void pdf_set_annot_contents(fz_context *ctx, pdf_document *doc, pdf_annot *annot, char *text)
373
{
374
pdf_dict_put_drop(ctx, annot->obj, PDF_NAME_Contents, pdf_new_string(ctx, doc, text, strlen(text)));
375
}
376
377
char *pdf_annot_contents(fz_context *ctx, pdf_document *doc, pdf_annot *annot)
378
{
379
return pdf_to_str_buf(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME_Contents));
380
}
381
382
void pdf_set_free_text_details(fz_context *ctx, pdf_document *doc, pdf_annot *annot, fz_point *pos, char *text, char *font_name, float font_size, float color[3])
383
{
384
char nbuf[32];
385
pdf_obj *dr;
386
pdf_obj *form_fonts;
387
pdf_obj *font = NULL;
388
pdf_obj *ref;
389
pdf_font_desc *font_desc = NULL;
390
pdf_da_info da_info;
391
fz_buffer *fzbuf = NULL;
392
fz_matrix ctm;
393
fz_point page_pos;
394
395
fz_invert_matrix(&ctm, &annot->page->ctm);
396
397
dr = pdf_dict_get(ctx, annot->page->me, PDF_NAME_Resources);
398
if (!dr)
399
{
400
dr = pdf_new_dict(ctx, doc, 1);
401
pdf_dict_put_drop(ctx, annot->page->me, PDF_NAME_Resources, dr);
402
}
403
404
/* Ensure the resource dictionary includes a font dict */
405
form_fonts = pdf_dict_get(ctx, dr, PDF_NAME_Font);
406
if (!form_fonts)
407
{
408
form_fonts = pdf_new_dict(ctx, doc, 1);
409
pdf_dict_put_drop(ctx, dr, PDF_NAME_Font, form_fonts);
410
/* form_fonts is still valid if execution continues past the above call */
411
}
412
413
fz_var(fzbuf);
414
fz_var(font);
415
fz_try(ctx)
416
{
417
unsigned char *da_str;
418
int da_len;
419
fz_rect bounds;
420
421
find_free_font_name(ctx, form_fonts, nbuf, sizeof(nbuf));
422
423
font = pdf_new_dict(ctx, doc, 5);
424
ref = pdf_new_ref(ctx, doc, font);
425
pdf_dict_puts_drop(ctx, form_fonts, nbuf, ref);
426
427
pdf_dict_put_drop(ctx, font, PDF_NAME_Type, PDF_NAME_Font);
428
pdf_dict_put_drop(ctx, font, PDF_NAME_Subtype, PDF_NAME_Type1);
429
pdf_dict_put_drop(ctx, font, PDF_NAME_BaseFont, pdf_new_name(ctx, doc, font_name));
430
pdf_dict_put_drop(ctx, font, PDF_NAME_Encoding, PDF_NAME_WinAnsiEncoding);
431
432
memcpy(da_info.col, color, sizeof(float)*3);
433
da_info.col_size = 3;
434
da_info.font_name = nbuf;
435
da_info.font_size = font_size;
436
437
fzbuf = fz_new_buffer(ctx, 0);
438
pdf_fzbuf_print_da(ctx, fzbuf, &da_info);
439
440
da_len = fz_buffer_storage(ctx, fzbuf, &da_str);
441
pdf_dict_put_drop(ctx, annot->obj, PDF_NAME_DA, pdf_new_string(ctx, doc, (char *)da_str, da_len));
442
443
/* FIXME: should convert to WinAnsiEncoding */
444
pdf_dict_put_drop(ctx, annot->obj, PDF_NAME_Contents, pdf_new_string(ctx, doc, text, strlen(text)));
445
446
font_desc = pdf_load_font(ctx, doc, NULL, font, 0);
447
pdf_measure_text(ctx, font_desc, (unsigned char *)text, strlen(text), &bounds);
448
449
page_pos = *pos;
450
fz_transform_point(&page_pos, &ctm);
451
452
bounds.x0 *= font_size;
453
bounds.x1 *= font_size;
454
bounds.y0 *= font_size;
455
bounds.y1 *= font_size;
456
457
bounds.x0 += page_pos.x;
458
bounds.x1 += page_pos.x;
459
bounds.y0 += page_pos.y;
460
bounds.y1 += page_pos.y;
461
462
pdf_dict_put_drop(ctx, annot->obj, PDF_NAME_Rect, pdf_new_rect(ctx, doc, &bounds));
463
update_rect(ctx, annot);
464
}
465
fz_always(ctx)
466
{
467
pdf_drop_obj(ctx, font);
468
fz_drop_buffer(ctx, fzbuf);
469
pdf_drop_font(ctx, font_desc);
470
}
471
fz_catch(ctx)
472
{
473
fz_rethrow(ctx);
474
}
475
}
476
477