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