1 #include "mupdf/fitz.h"
2 #include "mupdf/pdf.h"
3 
4 #include <string.h>
5 
6 pdf_annot *
pdf_keep_annot(fz_context * ctx,pdf_annot * annot)7 pdf_keep_annot(fz_context *ctx, pdf_annot *annot)
8 {
9 	return fz_keep_imp(ctx, annot, &annot->refs);
10 }
11 
12 void
pdf_drop_annot(fz_context * ctx,pdf_annot * annot)13 pdf_drop_annot(fz_context *ctx, pdf_annot *annot)
14 {
15 	if (fz_drop_imp(ctx, annot, &annot->refs))
16 	{
17 		pdf_drop_obj(ctx, annot->ap);
18 		pdf_drop_obj(ctx, annot->obj);
19 		fz_free(ctx, annot);
20 	}
21 }
22 
23 void
pdf_drop_annots(fz_context * ctx,pdf_annot * annot)24 pdf_drop_annots(fz_context *ctx, pdf_annot *annot)
25 {
26 	while (annot)
27 	{
28 		pdf_annot *next = annot->next;
29 		pdf_drop_annot(ctx, annot);
30 		annot = next;
31 	}
32 }
33 
34 fz_matrix
pdf_annot_transform(fz_context * ctx,pdf_annot * annot)35 pdf_annot_transform(fz_context *ctx, pdf_annot *annot)
36 {
37 	fz_rect bbox, rect;
38 	fz_matrix matrix;
39 	float w, h, x, y;
40 
41 	rect = pdf_dict_get_rect(ctx, annot->obj, PDF_NAME(Rect));
42 	bbox = pdf_xobject_bbox(ctx, annot->ap);
43 	matrix = pdf_xobject_matrix(ctx, annot->ap);
44 
45 	bbox = fz_transform_rect(bbox, matrix);
46 	if (bbox.x1 == bbox.x0)
47 		w = 0;
48 	else
49 		w = (rect.x1 - rect.x0) / (bbox.x1 - bbox.x0);
50 	if (bbox.y1 == bbox.y0)
51 		h = 0;
52 	else
53 		h = (rect.y1 - rect.y0) / (bbox.y1 - bbox.y0);
54 	x = rect.x0 - (bbox.x0 * w);
55 	y = rect.y0 - (bbox.y0 * h);
56 
57 	return fz_pre_scale(fz_translate(x, y), w, h);
58 }
59 
60 /*
61 	Internal function for creating a new pdf annotation.
62 */
63 static pdf_annot *
pdf_new_annot(fz_context * ctx,pdf_page * page,pdf_obj * obj)64 pdf_new_annot(fz_context *ctx, pdf_page *page, pdf_obj *obj)
65 {
66 	pdf_annot *annot;
67 
68 	annot = fz_malloc_struct(ctx, pdf_annot);
69 	annot->refs = 1;
70 	annot->page = page; /* only borrowed, as the page owns the annot */
71 	annot->obj = pdf_keep_obj(ctx, obj);
72 
73 	return annot;
74 }
75 
76 void
pdf_load_annots(fz_context * ctx,pdf_page * page,pdf_obj * annots)77 pdf_load_annots(fz_context *ctx, pdf_page *page, pdf_obj *annots)
78 {
79 	pdf_annot *annot;
80 	pdf_obj *subtype;
81 	int i, n;
82 
83 	n = pdf_array_len(ctx, annots);
84 	for (i = 0; i < n; ++i)
85 	{
86 		pdf_obj *obj = pdf_array_get(ctx, annots, i);
87 		if (pdf_is_dict(ctx, obj))
88 		{
89 			subtype = pdf_dict_get(ctx, obj, PDF_NAME(Subtype));
90 			if (pdf_name_eq(ctx, subtype, PDF_NAME(Link)))
91 				continue;
92 			if (pdf_name_eq(ctx, subtype, PDF_NAME(Popup)))
93 				continue;
94 
95 			annot = pdf_new_annot(ctx, page, obj);
96 			fz_try(ctx)
97 			{
98 				pdf_update_annot(ctx, annot);
99 				annot->has_new_ap = 0;
100 			}
101 			fz_catch(ctx)
102 				fz_warn(ctx, "could not update appearance for annotation");
103 
104 			if (pdf_name_eq(ctx, subtype, PDF_NAME(Widget)))
105 			{
106 				*page->widget_tailp = annot;
107 				page->widget_tailp = &annot->next;
108 			}
109 			else
110 			{
111 				*page->annot_tailp = annot;
112 				page->annot_tailp = &annot->next;
113 			}
114 		}
115 	}
116 }
117 
118 pdf_annot *
pdf_first_annot(fz_context * ctx,pdf_page * page)119 pdf_first_annot(fz_context *ctx, pdf_page *page)
120 {
121 	return page->annots;
122 }
123 
124 pdf_annot *
pdf_next_annot(fz_context * ctx,pdf_annot * annot)125 pdf_next_annot(fz_context *ctx, pdf_annot *annot)
126 {
127 	return annot->next;
128 }
129 
130 fz_rect
pdf_bound_annot(fz_context * ctx,pdf_annot * annot)131 pdf_bound_annot(fz_context *ctx, pdf_annot *annot)
132 {
133 	fz_matrix page_ctm;
134 	fz_rect rect;
135 	int flags;
136 
137 	rect = pdf_dict_get_rect(ctx, annot->obj, PDF_NAME(Rect));
138 	pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
139 
140 	flags = pdf_dict_get_int(ctx, annot->obj, PDF_NAME(F));
141 	if (flags & PDF_ANNOT_IS_NO_ROTATE)
142 	{
143 		int rotate = pdf_to_int(ctx, pdf_dict_get_inheritable(ctx, annot->page->obj, PDF_NAME(Rotate)));
144 		fz_point tp = fz_transform_point_xy(rect.x0, rect.y1, page_ctm);
145 		page_ctm = fz_concat(page_ctm, fz_translate(-tp.x, -tp.y));
146 		page_ctm = fz_concat(page_ctm, fz_rotate(-rotate));
147 		page_ctm = fz_concat(page_ctm, fz_translate(tp.x, tp.y));
148 	}
149 
150 	return fz_transform_rect(rect, page_ctm);
151 }
152 
153 void
pdf_dirty_annot(fz_context * ctx,pdf_annot * annot)154 pdf_dirty_annot(fz_context *ctx, pdf_annot *annot)
155 {
156 	annot->needs_new_ap = 1;
157 	if (annot->page && annot->page->doc)
158 		annot->page->doc->dirty = 1;
159 }
160 
161 const char *
pdf_string_from_annot_type(fz_context * ctx,enum pdf_annot_type type)162 pdf_string_from_annot_type(fz_context *ctx, enum pdf_annot_type type)
163 {
164 	switch (type)
165 	{
166 	case PDF_ANNOT_TEXT: return "Text";
167 	case PDF_ANNOT_LINK: return "Link";
168 	case PDF_ANNOT_FREE_TEXT: return "FreeText";
169 	case PDF_ANNOT_LINE: return "Line";
170 	case PDF_ANNOT_SQUARE: return "Square";
171 	case PDF_ANNOT_CIRCLE: return "Circle";
172 	case PDF_ANNOT_POLYGON: return "Polygon";
173 	case PDF_ANNOT_POLY_LINE: return "PolyLine";
174 	case PDF_ANNOT_HIGHLIGHT: return "Highlight";
175 	case PDF_ANNOT_UNDERLINE: return "Underline";
176 	case PDF_ANNOT_SQUIGGLY: return "Squiggly";
177 	case PDF_ANNOT_STRIKE_OUT: return "StrikeOut";
178 	case PDF_ANNOT_REDACT: return "Redact";
179 	case PDF_ANNOT_STAMP: return "Stamp";
180 	case PDF_ANNOT_CARET: return "Caret";
181 	case PDF_ANNOT_INK: return "Ink";
182 	case PDF_ANNOT_POPUP: return "Popup";
183 	case PDF_ANNOT_FILE_ATTACHMENT: return "FileAttachment";
184 	case PDF_ANNOT_SOUND: return "Sound";
185 	case PDF_ANNOT_MOVIE: return "Movie";
186 	case PDF_ANNOT_RICH_MEDIA: return "RichMedia";
187 	case PDF_ANNOT_WIDGET: return "Widget";
188 	case PDF_ANNOT_SCREEN: return "Screen";
189 	case PDF_ANNOT_PRINTER_MARK: return "PrinterMark";
190 	case PDF_ANNOT_TRAP_NET: return "TrapNet";
191 	case PDF_ANNOT_WATERMARK: return "Watermark";
192 	case PDF_ANNOT_3D: return "3D";
193 	case PDF_ANNOT_PROJECTION: return "Projection";
194 	default: return "UNKNOWN";
195 	}
196 }
197 
198 int
pdf_annot_type_from_string(fz_context * ctx,const char * subtype)199 pdf_annot_type_from_string(fz_context *ctx, const char *subtype)
200 {
201 	if (!strcmp("Text", subtype)) return PDF_ANNOT_TEXT;
202 	if (!strcmp("Link", subtype)) return PDF_ANNOT_LINK;
203 	if (!strcmp("FreeText", subtype)) return PDF_ANNOT_FREE_TEXT;
204 	if (!strcmp("Line", subtype)) return PDF_ANNOT_LINE;
205 	if (!strcmp("Square", subtype)) return PDF_ANNOT_SQUARE;
206 	if (!strcmp("Circle", subtype)) return PDF_ANNOT_CIRCLE;
207 	if (!strcmp("Polygon", subtype)) return PDF_ANNOT_POLYGON;
208 	if (!strcmp("PolyLine", subtype)) return PDF_ANNOT_POLY_LINE;
209 	if (!strcmp("Highlight", subtype)) return PDF_ANNOT_HIGHLIGHT;
210 	if (!strcmp("Underline", subtype)) return PDF_ANNOT_UNDERLINE;
211 	if (!strcmp("Squiggly", subtype)) return PDF_ANNOT_SQUIGGLY;
212 	if (!strcmp("StrikeOut", subtype)) return PDF_ANNOT_STRIKE_OUT;
213 	if (!strcmp("Redact", subtype)) return PDF_ANNOT_REDACT;
214 	if (!strcmp("Stamp", subtype)) return PDF_ANNOT_STAMP;
215 	if (!strcmp("Caret", subtype)) return PDF_ANNOT_CARET;
216 	if (!strcmp("Ink", subtype)) return PDF_ANNOT_INK;
217 	if (!strcmp("Popup", subtype)) return PDF_ANNOT_POPUP;
218 	if (!strcmp("FileAttachment", subtype)) return PDF_ANNOT_FILE_ATTACHMENT;
219 	if (!strcmp("Sound", subtype)) return PDF_ANNOT_SOUND;
220 	if (!strcmp("Movie", subtype)) return PDF_ANNOT_MOVIE;
221 	if (!strcmp("RichMedia", subtype)) return PDF_ANNOT_RICH_MEDIA;
222 	if (!strcmp("Widget", subtype)) return PDF_ANNOT_WIDGET;
223 	if (!strcmp("Screen", subtype)) return PDF_ANNOT_SCREEN;
224 	if (!strcmp("PrinterMark", subtype)) return PDF_ANNOT_PRINTER_MARK;
225 	if (!strcmp("TrapNet", subtype)) return PDF_ANNOT_TRAP_NET;
226 	if (!strcmp("Watermark", subtype)) return PDF_ANNOT_WATERMARK;
227 	if (!strcmp("3D", subtype)) return PDF_ANNOT_3D;
228 	if (!strcmp("Projection", subtype)) return PDF_ANNOT_PROJECTION;
229 	return PDF_ANNOT_UNKNOWN;
230 }
231 
is_allowed_subtype(fz_context * ctx,pdf_annot * annot,pdf_obj * property,pdf_obj ** allowed)232 static int is_allowed_subtype(fz_context *ctx, pdf_annot *annot, pdf_obj *property, pdf_obj **allowed)
233 {
234 	pdf_obj *subtype = pdf_dict_get(ctx, annot->obj, PDF_NAME(Subtype));
235 	while (*allowed) {
236 		if (pdf_name_eq(ctx, subtype, *allowed))
237 			return 1;
238 		allowed++;
239 	}
240 
241 	return 0;
242 }
243 
check_allowed_subtypes(fz_context * ctx,pdf_annot * annot,pdf_obj * property,pdf_obj ** allowed)244 static void check_allowed_subtypes(fz_context *ctx, pdf_annot *annot, pdf_obj *property, pdf_obj **allowed)
245 {
246 	pdf_obj *subtype = pdf_dict_get(ctx, annot->obj, PDF_NAME(Subtype));
247 	if (!is_allowed_subtype(ctx, annot, property, allowed))
248 		fz_throw(ctx, FZ_ERROR_GENERIC, "%s annotations have no %s property", pdf_to_name(ctx, subtype), pdf_to_name(ctx, property));
249 }
250 
251 pdf_annot *
pdf_create_annot_raw(fz_context * ctx,pdf_page * page,enum pdf_annot_type type)252 pdf_create_annot_raw(fz_context *ctx, pdf_page *page, enum pdf_annot_type type)
253 {
254 	pdf_annot *annot = NULL;
255 	pdf_document *doc = page->doc;
256 	pdf_obj *annot_obj = pdf_new_dict(ctx, doc, 0);
257 	pdf_obj *ind_obj = NULL;
258 
259 	fz_var(annot);
260 	fz_var(ind_obj);
261 	fz_try(ctx)
262 	{
263 		int ind_obj_num;
264 		const char *type_str;
265 		pdf_obj *annot_arr;
266 
267 		type_str = pdf_string_from_annot_type(ctx, type);
268 		if (type == PDF_ANNOT_UNKNOWN)
269 			fz_throw(ctx, FZ_ERROR_GENERIC, "cannot create unknown annotation");
270 
271 		annot_arr = pdf_dict_get(ctx, page->obj, PDF_NAME(Annots));
272 		if (annot_arr == NULL)
273 		{
274 			annot_arr = pdf_new_array(ctx, doc, 0);
275 			pdf_dict_put_drop(ctx, page->obj, PDF_NAME(Annots), annot_arr);
276 		}
277 
278 		pdf_dict_put(ctx, annot_obj, PDF_NAME(Type), PDF_NAME(Annot));
279 		pdf_dict_put_name(ctx, annot_obj, PDF_NAME(Subtype), type_str);
280 
281 		/*
282 			Both annotation object and annotation structure are now created.
283 			Insert the object in the hierarchy and the structure in the
284 			page's array.
285 		*/
286 		ind_obj_num = pdf_create_object(ctx, doc);
287 		pdf_update_object(ctx, doc, ind_obj_num, annot_obj);
288 		ind_obj = pdf_new_indirect(ctx, doc, ind_obj_num, 0);
289 		pdf_array_push(ctx, annot_arr, ind_obj);
290 
291 		annot = pdf_new_annot(ctx, page, ind_obj);
292 		annot->ap = NULL;
293 
294 		/*
295 			Linking must be done after any call that might throw because
296 			pdf_drop_annots below actually frees a list. Put the new annot
297 			at the end of the list, so that it will be drawn last.
298 		*/
299 		if (type == PDF_ANNOT_WIDGET)
300 		{
301 			*page->widget_tailp = annot;
302 			page->widget_tailp = &annot->next;
303 		}
304 		else
305 		{
306 			*page->annot_tailp = annot;
307 			page->annot_tailp = &annot->next;
308 		}
309 
310 		doc->dirty = 1;
311 	}
312 	fz_always(ctx)
313 	{
314 		pdf_drop_obj(ctx, annot_obj);
315 		pdf_drop_obj(ctx, ind_obj);
316 	}
317 	fz_catch(ctx)
318 	{
319 		pdf_drop_annots(ctx, annot);
320 		fz_rethrow(ctx);
321 	}
322 
323 	return annot;
324 }
325 
326 static pdf_obj *
pdf_add_popup_annot(fz_context * ctx,pdf_annot * annot)327 pdf_add_popup_annot(fz_context *ctx, pdf_annot *annot)
328 {
329 	pdf_obj *annots, *popup;
330 
331 	popup = pdf_dict_get(ctx, annot->obj, PDF_NAME(Popup));
332 	if (popup)
333 		return popup;
334 
335 	annots = pdf_dict_get(ctx, annot->page->obj, PDF_NAME(Annots));
336 	if (!annots)
337 		return NULL;
338 
339 	popup = pdf_add_new_dict(ctx, annot->page->doc, 4);
340 	pdf_array_push_drop(ctx, annots, popup);
341 
342 	pdf_dict_put(ctx, popup, PDF_NAME(Type), PDF_NAME(Annot));
343 	pdf_dict_put(ctx, popup, PDF_NAME(Subtype), PDF_NAME(Popup));
344 	pdf_dict_put(ctx, popup, PDF_NAME(Parent), annot->obj);
345 	pdf_dict_put_rect(ctx, popup, PDF_NAME(Rect), fz_make_rect(0,0,0,0));
346 
347 	pdf_dict_put(ctx, annot->obj, PDF_NAME(Popup), popup);
348 
349 	return popup;
350 }
351 
pdf_set_annot_popup(fz_context * ctx,pdf_annot * annot,fz_rect rect)352 void pdf_set_annot_popup(fz_context *ctx, pdf_annot *annot, fz_rect rect)
353 {
354 	fz_matrix page_ctm, inv_page_ctm;
355 	pdf_obj *popup;
356 	pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
357 	inv_page_ctm = fz_invert_matrix(page_ctm);
358 	rect = fz_transform_rect(rect, inv_page_ctm);
359 	popup = pdf_add_popup_annot(ctx, annot);
360 	pdf_dict_put_rect(ctx, popup, PDF_NAME(Rect), rect);
361 }
362 
pdf_annot_popup(fz_context * ctx,pdf_annot * annot)363 fz_rect pdf_annot_popup(fz_context *ctx, pdf_annot *annot)
364 {
365 	fz_matrix page_ctm;
366 	fz_rect rect;
367 	pdf_obj *popup;
368 	pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
369 	popup = pdf_dict_get(ctx, annot->obj, PDF_NAME(Popup));
370 	rect = pdf_dict_get_rect(ctx, popup, PDF_NAME(Rect));
371 	return fz_transform_rect(rect, page_ctm);
372 }
373 
374 pdf_annot *
pdf_create_annot(fz_context * ctx,pdf_page * page,enum pdf_annot_type type)375 pdf_create_annot(fz_context *ctx, pdf_page *page, enum pdf_annot_type type)
376 {
377 	static const float black[3] = { 0, 0, 0 };
378 	static const float red[3] = { 1, 0, 0 };
379 	static const float green[3] = { 0, 1, 0 };
380 	static const float blue[3] = { 0, 0, 1 };
381 	static const float yellow[3] = { 1, 1, 0 };
382 	static const float magenta[3] = { 1, 0, 1 };
383 
384 	int flags = PDF_ANNOT_IS_PRINT; /* Make printable as default */
385 
386 	pdf_annot *annot = pdf_create_annot_raw(ctx, page, type);
387 
388 	switch (type)
389 	{
390 	default:
391 		break;
392 
393 	case PDF_ANNOT_TEXT:
394 	case PDF_ANNOT_FILE_ATTACHMENT:
395 	case PDF_ANNOT_SOUND:
396 		{
397 			fz_rect icon_rect = { 12, 12, 12+20, 12+20 };
398 			flags = PDF_ANNOT_IS_PRINT | PDF_ANNOT_IS_NO_ZOOM | PDF_ANNOT_IS_NO_ROTATE;
399 			pdf_set_annot_rect(ctx, annot, icon_rect);
400 			pdf_set_annot_color(ctx, annot, 3, yellow);
401 			pdf_set_annot_popup(ctx, annot, fz_make_rect(32, 12, 32+200, 12+100));
402 		}
403 		break;
404 
405 	case PDF_ANNOT_FREE_TEXT:
406 		{
407 			fz_rect text_rect = { 12, 12, 12+200, 12+100 };
408 
409 			/* Use undocumented Adobe property to match page rotation. */
410 			int rot = pdf_to_int(ctx, pdf_dict_get_inheritable(ctx, page->obj, PDF_NAME(Rotate)));
411 			if (rot != 0)
412 				pdf_dict_put_int(ctx, annot->obj, PDF_NAME(Rotate), rot);
413 
414 			pdf_set_annot_rect(ctx, annot, text_rect);
415 			pdf_set_annot_border(ctx, annot, 0);
416 			pdf_set_annot_default_appearance(ctx, annot, "Helv", 12, black);
417 		}
418 		break;
419 
420 	case PDF_ANNOT_STAMP:
421 		{
422 			fz_rect stamp_rect = { 12, 12, 12+190, 12+50 };
423 			pdf_set_annot_rect(ctx, annot, stamp_rect);
424 			pdf_set_annot_color(ctx, annot, 3, red);
425 		}
426 		break;
427 
428 	case PDF_ANNOT_CARET:
429 		{
430 			fz_rect caret_rect = { 12, 12, 12+18, 12+15 };
431 			pdf_set_annot_rect(ctx, annot, caret_rect);
432 			pdf_set_annot_color(ctx, annot, 3, blue);
433 		}
434 		break;
435 
436 	case PDF_ANNOT_LINE:
437 		{
438 			fz_point a = { 12, 12 }, b = { 12 + 100, 12 + 50 };
439 			pdf_set_annot_line(ctx, annot, a, b);
440 			pdf_set_annot_border(ctx, annot, 1);
441 			pdf_set_annot_color(ctx, annot, 3, red);
442 		}
443 		break;
444 
445 	case PDF_ANNOT_SQUARE:
446 	case PDF_ANNOT_CIRCLE:
447 		{
448 			fz_rect shape_rect = { 12, 12, 12+100, 12+50 };
449 			pdf_set_annot_rect(ctx, annot, shape_rect);
450 			pdf_set_annot_border(ctx, annot, 1);
451 			pdf_set_annot_color(ctx, annot, 3, red);
452 		}
453 		break;
454 
455 	case PDF_ANNOT_POLYGON:
456 	case PDF_ANNOT_POLY_LINE:
457 	case PDF_ANNOT_INK:
458 		pdf_set_annot_border(ctx, annot, 1);
459 		pdf_set_annot_color(ctx, annot, 3, red);
460 		break;
461 
462 	case PDF_ANNOT_HIGHLIGHT:
463 		pdf_set_annot_color(ctx, annot, 3, yellow);
464 		break;
465 	case PDF_ANNOT_UNDERLINE:
466 		pdf_set_annot_color(ctx, annot, 3, green);
467 		break;
468 	case PDF_ANNOT_STRIKE_OUT:
469 		pdf_set_annot_color(ctx, annot, 3, red);
470 		break;
471 	case PDF_ANNOT_SQUIGGLY:
472 		pdf_set_annot_color(ctx, annot, 3, magenta);
473 		break;
474 	}
475 
476 	pdf_dict_put(ctx, annot->obj, PDF_NAME(P), page->obj);
477 	pdf_dict_put_int(ctx, annot->obj, PDF_NAME(F), flags);
478 
479 	return pdf_keep_annot(ctx, annot);
480 }
481 
482 void
pdf_delete_annot(fz_context * ctx,pdf_page * page,pdf_annot * annot)483 pdf_delete_annot(fz_context *ctx, pdf_page *page, pdf_annot *annot)
484 {
485 	pdf_document *doc = annot->page->doc;
486 	pdf_annot **annotptr;
487 	pdf_obj *annot_arr, *popup;
488 	int i;
489 
490 	if (annot == NULL)
491 		return;
492 
493 	/* Remove annot from page's list */
494 	for (annotptr = &page->annots; *annotptr; annotptr = &(*annotptr)->next)
495 	{
496 		if (*annotptr == annot)
497 			break;
498 	}
499 
500 	/* Check the passed annotation was of this page */
501 	if (*annotptr == NULL)
502 		return;
503 
504 	*annotptr = annot->next;
505 
506 	/* If the removed annotation was the last in the list adjust the end pointer */
507 	if (*annotptr == NULL)
508 		page->annot_tailp = annotptr;
509 
510 	/* Remove the annot from the "Annots" array. */
511 	annot_arr = pdf_dict_get(ctx, page->obj, PDF_NAME(Annots));
512 	i = pdf_array_find(ctx, annot_arr, annot->obj);
513 	if (i >= 0)
514 		pdf_array_delete(ctx, annot_arr, i);
515 
516 	/* Remove the associated Popup annotation from the Annots array */
517 	popup = pdf_dict_get(ctx, annot->obj, PDF_NAME(Popup));
518 	if (popup)
519 	{
520 		i = pdf_array_find(ctx, annot_arr, popup);
521 		if (i >= 0)
522 			pdf_array_delete(ctx, annot_arr, i);
523 	}
524 
525 	/* The garbage collection pass when saving will remove the annot object,
526 	 * removing it here may break files if multiple pages use the same annot. */
527 
528 	/* And free it. */
529 	pdf_drop_annot(ctx, annot);
530 
531 	doc->dirty = 1;
532 }
533 
534 enum pdf_annot_type
pdf_annot_type(fz_context * ctx,pdf_annot * annot)535 pdf_annot_type(fz_context *ctx, pdf_annot *annot)
536 {
537 	pdf_obj *obj = annot->obj;
538 	pdf_obj *subtype = pdf_dict_get(ctx, obj, PDF_NAME(Subtype));
539 	return pdf_annot_type_from_string(ctx, pdf_to_name(ctx, subtype));
540 }
541 
542 int
pdf_annot_flags(fz_context * ctx,pdf_annot * annot)543 pdf_annot_flags(fz_context *ctx, pdf_annot *annot)
544 {
545 	return pdf_dict_get_int(ctx, annot->obj, PDF_NAME(F));
546 }
547 
548 void
pdf_set_annot_flags(fz_context * ctx,pdf_annot * annot,int flags)549 pdf_set_annot_flags(fz_context *ctx, pdf_annot *annot, int flags)
550 {
551 	pdf_dict_put_int(ctx, annot->obj, PDF_NAME(F), flags);
552 	pdf_dirty_annot(ctx, annot);
553 }
554 
555 fz_rect
pdf_annot_rect(fz_context * ctx,pdf_annot * annot)556 pdf_annot_rect(fz_context *ctx, pdf_annot *annot)
557 {
558 	fz_matrix page_ctm;
559 	fz_rect annot_rect;
560 	pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
561 	annot_rect = pdf_dict_get_rect(ctx, annot->obj, PDF_NAME(Rect));
562 	return fz_transform_rect(annot_rect, page_ctm);
563 }
564 
565 void
pdf_set_annot_rect(fz_context * ctx,pdf_annot * annot,fz_rect rect)566 pdf_set_annot_rect(fz_context *ctx, pdf_annot *annot, fz_rect rect)
567 {
568 	fz_matrix page_ctm, inv_page_ctm;
569 
570 	pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
571 	inv_page_ctm = fz_invert_matrix(page_ctm);
572 	rect = fz_transform_rect(rect, inv_page_ctm);
573 
574 	pdf_dict_put_rect(ctx, annot->obj, PDF_NAME(Rect), rect);
575 	pdf_dirty_annot(ctx, annot);
576 }
577 
578 const char *
pdf_annot_contents(fz_context * ctx,pdf_annot * annot)579 pdf_annot_contents(fz_context *ctx, pdf_annot *annot)
580 {
581 	return pdf_dict_get_text_string(ctx, annot->obj, PDF_NAME(Contents));
582 }
583 
584 void
pdf_set_annot_contents(fz_context * ctx,pdf_annot * annot,const char * text)585 pdf_set_annot_contents(fz_context *ctx, pdf_annot *annot, const char *text)
586 {
587 	pdf_dict_put_text_string(ctx, annot->obj, PDF_NAME(Contents), text);
588 	pdf_dict_del(ctx, annot->obj, PDF_NAME(RC)); /* not supported */
589 	pdf_dirty_annot(ctx, annot);
590 }
591 
592 int
pdf_annot_has_open(fz_context * ctx,pdf_annot * annot)593 pdf_annot_has_open(fz_context *ctx, pdf_annot *annot)
594 {
595 	pdf_obj *subtype = pdf_dict_get(ctx, annot->obj, PDF_NAME(Subtype));
596 	pdf_obj *popup = pdf_dict_get(ctx, annot->obj, PDF_NAME(Popup));
597 	return (subtype == PDF_NAME(Text) || popup);
598 }
599 
600 int
pdf_annot_is_open(fz_context * ctx,pdf_annot * annot)601 pdf_annot_is_open(fz_context *ctx, pdf_annot *annot)
602 {
603 	pdf_obj *subtype = pdf_dict_get(ctx, annot->obj, PDF_NAME(Subtype));
604 	pdf_obj *popup = pdf_dict_get(ctx, annot->obj, PDF_NAME(Popup));
605 	if (popup)
606 		return pdf_dict_get_bool(ctx, popup, PDF_NAME(Open));
607 	else if (subtype == PDF_NAME(Text))
608 		return pdf_dict_get_bool(ctx, annot->obj, PDF_NAME(Open));
609 	return 0;
610 }
611 
612 void
pdf_set_annot_is_open(fz_context * ctx,pdf_annot * annot,int is_open)613 pdf_set_annot_is_open(fz_context *ctx, pdf_annot *annot, int is_open)
614 {
615 	pdf_obj *subtype = pdf_dict_get(ctx, annot->obj, PDF_NAME(Subtype));
616 	pdf_obj *popup = pdf_dict_get(ctx, annot->obj, PDF_NAME(Popup));
617 	if (popup)
618 	{
619 		pdf_dict_put_bool(ctx, popup, PDF_NAME(Open), is_open);
620 		pdf_dirty_annot(ctx, annot);
621 	}
622 	else if (subtype == PDF_NAME(Text))
623 	{
624 		pdf_dict_put_bool(ctx, annot->obj, PDF_NAME(Open), is_open);
625 		pdf_dirty_annot(ctx, annot);
626 	}
627 }
628 
629 static pdf_obj *icon_name_subtypes[] = {
630 	PDF_NAME(FileAttachment),
631 	PDF_NAME(Sound),
632 	PDF_NAME(Stamp),
633 	PDF_NAME(Text),
634 	NULL,
635 };
636 
637 int
pdf_annot_has_icon_name(fz_context * ctx,pdf_annot * annot)638 pdf_annot_has_icon_name(fz_context *ctx, pdf_annot *annot)
639 {
640 	return is_allowed_subtype(ctx, annot, PDF_NAME(Name), icon_name_subtypes);
641 }
642 
643 const char *
pdf_annot_icon_name(fz_context * ctx,pdf_annot * annot)644 pdf_annot_icon_name(fz_context *ctx, pdf_annot *annot)
645 {
646 	pdf_obj *name;
647 	check_allowed_subtypes(ctx, annot, PDF_NAME(Name), icon_name_subtypes);
648 	name = pdf_dict_get(ctx, annot->obj, PDF_NAME(Name));
649 	if (!name)
650 	{
651 		pdf_obj *subtype = pdf_dict_get(ctx, annot->obj, PDF_NAME(Subtype));
652 		if (pdf_name_eq(ctx, subtype, PDF_NAME(Text)))
653 			return "Note";
654 		if (pdf_name_eq(ctx, subtype, PDF_NAME(Stamp)))
655 			return "Draft";
656 		if (pdf_name_eq(ctx, subtype, PDF_NAME(FileAttachment)))
657 			return "PushPin";
658 		if (pdf_name_eq(ctx, subtype, PDF_NAME(Sound)))
659 			return "Speaker";
660 	}
661 	return pdf_to_name(ctx, name);
662 }
663 
664 void
pdf_set_annot_icon_name(fz_context * ctx,pdf_annot * annot,const char * name)665 pdf_set_annot_icon_name(fz_context *ctx, pdf_annot *annot, const char *name)
666 {
667 	check_allowed_subtypes(ctx, annot, PDF_NAME(Name), icon_name_subtypes);
668 	pdf_dict_put_name(ctx, annot->obj, PDF_NAME(Name), name);
669 	pdf_dirty_annot(ctx, annot);
670 }
671 
pdf_line_ending_from_name(fz_context * ctx,pdf_obj * end)672 enum pdf_line_ending pdf_line_ending_from_name(fz_context *ctx, pdf_obj *end)
673 {
674 	if (pdf_name_eq(ctx, end, PDF_NAME(None))) return PDF_ANNOT_LE_NONE;
675 	else if (pdf_name_eq(ctx, end, PDF_NAME(Square))) return PDF_ANNOT_LE_SQUARE;
676 	else if (pdf_name_eq(ctx, end, PDF_NAME(Circle))) return PDF_ANNOT_LE_CIRCLE;
677 	else if (pdf_name_eq(ctx, end, PDF_NAME(Diamond))) return PDF_ANNOT_LE_DIAMOND;
678 	else if (pdf_name_eq(ctx, end, PDF_NAME(OpenArrow))) return PDF_ANNOT_LE_OPEN_ARROW;
679 	else if (pdf_name_eq(ctx, end, PDF_NAME(ClosedArrow))) return PDF_ANNOT_LE_CLOSED_ARROW;
680 	else if (pdf_name_eq(ctx, end, PDF_NAME(Butt))) return PDF_ANNOT_LE_BUTT;
681 	else if (pdf_name_eq(ctx, end, PDF_NAME(ROpenArrow))) return PDF_ANNOT_LE_R_OPEN_ARROW;
682 	else if (pdf_name_eq(ctx, end, PDF_NAME(RClosedArrow))) return PDF_ANNOT_LE_R_CLOSED_ARROW;
683 	else if (pdf_name_eq(ctx, end, PDF_NAME(Slash))) return PDF_ANNOT_LE_SLASH;
684 	else return PDF_ANNOT_LE_NONE;
685 }
686 
pdf_line_ending_from_string(fz_context * ctx,const char * end)687 enum pdf_line_ending pdf_line_ending_from_string(fz_context *ctx, const char *end)
688 {
689 	if (!strcmp(end, "None")) return PDF_ANNOT_LE_NONE;
690 	else if (!strcmp(end, "Square")) return PDF_ANNOT_LE_SQUARE;
691 	else if (!strcmp(end, "Circle")) return PDF_ANNOT_LE_CIRCLE;
692 	else if (!strcmp(end, "Diamond")) return PDF_ANNOT_LE_DIAMOND;
693 	else if (!strcmp(end, "OpenArrow")) return PDF_ANNOT_LE_OPEN_ARROW;
694 	else if (!strcmp(end, "ClosedArrow")) return PDF_ANNOT_LE_CLOSED_ARROW;
695 	else if (!strcmp(end, "Butt")) return PDF_ANNOT_LE_BUTT;
696 	else if (!strcmp(end, "ROpenArrow")) return PDF_ANNOT_LE_R_OPEN_ARROW;
697 	else if (!strcmp(end, "RClosedArrow")) return PDF_ANNOT_LE_R_CLOSED_ARROW;
698 	else if (!strcmp(end, "Slash")) return PDF_ANNOT_LE_SLASH;
699 	else return PDF_ANNOT_LE_NONE;
700 }
701 
pdf_name_from_line_ending(fz_context * ctx,enum pdf_line_ending end)702 pdf_obj *pdf_name_from_line_ending(fz_context *ctx, enum pdf_line_ending end)
703 {
704 	switch (end)
705 	{
706 	default:
707 	case PDF_ANNOT_LE_NONE: return PDF_NAME(None);
708 	case PDF_ANNOT_LE_SQUARE: return PDF_NAME(Square);
709 	case PDF_ANNOT_LE_CIRCLE: return PDF_NAME(Circle);
710 	case PDF_ANNOT_LE_DIAMOND: return PDF_NAME(Diamond);
711 	case PDF_ANNOT_LE_OPEN_ARROW: return PDF_NAME(OpenArrow);
712 	case PDF_ANNOT_LE_CLOSED_ARROW: return PDF_NAME(ClosedArrow);
713 	case PDF_ANNOT_LE_BUTT: return PDF_NAME(Butt);
714 	case PDF_ANNOT_LE_R_OPEN_ARROW: return PDF_NAME(ROpenArrow);
715 	case PDF_ANNOT_LE_R_CLOSED_ARROW: return PDF_NAME(RClosedArrow);
716 	case PDF_ANNOT_LE_SLASH: return PDF_NAME(Slash);
717 	}
718 }
719 
pdf_string_from_line_ending(fz_context * ctx,enum pdf_line_ending end)720 const char *pdf_string_from_line_ending(fz_context *ctx, enum pdf_line_ending end)
721 {
722 	switch (end)
723 	{
724 	default:
725 	case PDF_ANNOT_LE_NONE: return "None";
726 	case PDF_ANNOT_LE_SQUARE: return "Square";
727 	case PDF_ANNOT_LE_CIRCLE: return "Circle";
728 	case PDF_ANNOT_LE_DIAMOND: return "Diamond";
729 	case PDF_ANNOT_LE_OPEN_ARROW: return "OpenArrow";
730 	case PDF_ANNOT_LE_CLOSED_ARROW: return "ClosedArrow";
731 	case PDF_ANNOT_LE_BUTT: return "Butt";
732 	case PDF_ANNOT_LE_R_OPEN_ARROW: return "ROpenArrow";
733 	case PDF_ANNOT_LE_R_CLOSED_ARROW: return "RClosedArrow";
734 	case PDF_ANNOT_LE_SLASH: return "Slash";
735 	}
736 }
737 
738 static pdf_obj *line_ending_subtypes[] = {
739 	PDF_NAME(FreeText),
740 	PDF_NAME(Line),
741 	PDF_NAME(PolyLine),
742 	PDF_NAME(Polygon),
743 	NULL,
744 };
745 
746 int
pdf_annot_has_line_ending_styles(fz_context * ctx,pdf_annot * annot)747 pdf_annot_has_line_ending_styles(fz_context *ctx, pdf_annot *annot)
748 {
749 	return is_allowed_subtype(ctx, annot, PDF_NAME(LE), line_ending_subtypes);
750 }
751 
752 void
pdf_annot_line_ending_styles(fz_context * ctx,pdf_annot * annot,enum pdf_line_ending * start_style,enum pdf_line_ending * end_style)753 pdf_annot_line_ending_styles(fz_context *ctx, pdf_annot *annot,
754 		enum pdf_line_ending *start_style,
755 		enum pdf_line_ending *end_style)
756 {
757 	pdf_obj *style;
758 	check_allowed_subtypes(ctx, annot, PDF_NAME(LE), line_ending_subtypes);
759 	style = pdf_dict_get(ctx, annot->obj, PDF_NAME(LE));
760 	*start_style = pdf_line_ending_from_name(ctx, pdf_array_get(ctx, style, 0));
761 	*end_style = pdf_line_ending_from_name(ctx, pdf_array_get(ctx, style, 1));
762 }
763 
764 enum pdf_line_ending
pdf_annot_line_start_style(fz_context * ctx,pdf_annot * annot)765 pdf_annot_line_start_style(fz_context *ctx, pdf_annot *annot)
766 {
767 	pdf_obj *le = pdf_dict_get(ctx, annot->obj, PDF_NAME(LE));
768 	return pdf_line_ending_from_name(ctx, pdf_array_get(ctx, le, 0));
769 }
770 
771 enum pdf_line_ending
pdf_annot_line_end_style(fz_context * ctx,pdf_annot * annot)772 pdf_annot_line_end_style(fz_context *ctx, pdf_annot *annot)
773 {
774 	pdf_obj *le = pdf_dict_get(ctx, annot->obj, PDF_NAME(LE));
775 	return pdf_line_ending_from_name(ctx, pdf_array_get(ctx, le, 1));
776 }
777 
778 void
pdf_set_annot_line_ending_styles(fz_context * ctx,pdf_annot * annot,enum pdf_line_ending start_style,enum pdf_line_ending end_style)779 pdf_set_annot_line_ending_styles(fz_context *ctx, pdf_annot *annot,
780 		enum pdf_line_ending start_style,
781 		enum pdf_line_ending end_style)
782 {
783 	pdf_document *doc = annot->page->doc;
784 	pdf_obj *style;
785 	check_allowed_subtypes(ctx, annot, PDF_NAME(LE), line_ending_subtypes);
786 	style = pdf_new_array(ctx, doc, 2);
787 	pdf_dict_put_drop(ctx, annot->obj, PDF_NAME(LE), style);
788 	pdf_array_put_drop(ctx, style, 0, pdf_name_from_line_ending(ctx, start_style));
789 	pdf_array_put_drop(ctx, style, 1, pdf_name_from_line_ending(ctx, end_style));
790 	pdf_dirty_annot(ctx, annot);
791 }
792 
793 void
pdf_set_annot_line_start_style(fz_context * ctx,pdf_annot * annot,enum pdf_line_ending s)794 pdf_set_annot_line_start_style(fz_context *ctx, pdf_annot *annot, enum pdf_line_ending s)
795 {
796 	enum pdf_line_ending e = pdf_annot_line_end_style(ctx, annot);
797 	pdf_set_annot_line_ending_styles(ctx, annot, s, e);
798 }
799 
800 void
pdf_set_annot_line_end_style(fz_context * ctx,pdf_annot * annot,enum pdf_line_ending e)801 pdf_set_annot_line_end_style(fz_context *ctx, pdf_annot *annot, enum pdf_line_ending e)
802 {
803 	enum pdf_line_ending s = pdf_annot_line_start_style(ctx, annot);
804 	pdf_set_annot_line_ending_styles(ctx, annot, s, e);
805 }
806 
807 float
pdf_annot_border(fz_context * ctx,pdf_annot * annot)808 pdf_annot_border(fz_context *ctx, pdf_annot *annot)
809 {
810 	pdf_obj *bs, *bs_w, *border;
811 	bs = pdf_dict_get(ctx, annot->obj, PDF_NAME(BS));
812 	bs_w = pdf_dict_get(ctx, bs, PDF_NAME(W));
813 	if (pdf_is_number(ctx, bs_w))
814 		return pdf_to_real(ctx, bs_w);
815 	border = pdf_dict_get(ctx, annot->obj, PDF_NAME(Border));
816 	bs_w = pdf_array_get(ctx, border, 2);
817 	if (pdf_is_number(ctx, bs_w))
818 		return pdf_to_real(ctx, bs_w);
819 	return 1;
820 }
821 
822 void
pdf_set_annot_border(fz_context * ctx,pdf_annot * annot,float w)823 pdf_set_annot_border(fz_context *ctx, pdf_annot *annot, float w)
824 {
825 	pdf_obj *bs = pdf_dict_get(ctx, annot->obj, PDF_NAME(BS));
826 	if (!pdf_is_dict(ctx, bs))
827 		bs = pdf_dict_put_dict(ctx, annot->obj, PDF_NAME(BS), 1);
828 	pdf_dict_put_real(ctx, bs, PDF_NAME(W), w);
829 
830 	pdf_dict_del(ctx, annot->obj, PDF_NAME(Border)); /* deprecated */
831 	pdf_dict_del(ctx, annot->obj, PDF_NAME(BE)); /* not supported */
832 
833 	pdf_dirty_annot(ctx, annot);
834 }
835 
836 fz_text_language
pdf_document_language(fz_context * ctx,pdf_document * doc)837 pdf_document_language(fz_context *ctx, pdf_document *doc)
838 {
839 	pdf_obj *trailer = pdf_trailer(ctx, doc);
840 	pdf_obj *root = pdf_dict_get(ctx, trailer, PDF_NAME(Root));
841 	pdf_obj *lang = pdf_dict_get(ctx, root, PDF_NAME(Lang));
842 	return fz_text_language_from_string(pdf_to_text_string(ctx, lang));
843 }
844 
pdf_set_document_language(fz_context * ctx,pdf_document * doc,fz_text_language lang)845 void pdf_set_document_language(fz_context *ctx, pdf_document *doc, fz_text_language lang)
846 {
847 	pdf_obj *trailer = pdf_trailer(ctx, doc);
848 	pdf_obj *root = pdf_dict_get(ctx, trailer, PDF_NAME(Root));
849 	char buf[8];
850 	if (lang == FZ_LANG_UNSET)
851 		pdf_dict_del(ctx, root, PDF_NAME(Lang));
852 	else
853 		pdf_dict_put_text_string(ctx, root, PDF_NAME(Lang), fz_string_from_text_language(buf, lang));
854 }
855 
856 fz_text_language
pdf_annot_language(fz_context * ctx,pdf_annot * annot)857 pdf_annot_language(fz_context *ctx, pdf_annot *annot)
858 {
859 	pdf_obj *lang = pdf_dict_get_inheritable(ctx, annot->obj, PDF_NAME(Lang));
860 	if (lang)
861 		return fz_text_language_from_string(pdf_to_str_buf(ctx, lang));
862 	return pdf_document_language(ctx, annot->page->doc);
863 }
864 
865 void
pdf_set_annot_language(fz_context * ctx,pdf_annot * annot,fz_text_language lang)866 pdf_set_annot_language(fz_context *ctx, pdf_annot *annot, fz_text_language lang)
867 {
868 	char buf[8];
869 	if (lang == FZ_LANG_UNSET)
870 		pdf_dict_del(ctx, annot->obj, PDF_NAME(Lang));
871 	else
872 		pdf_dict_put_text_string(ctx, annot->obj, PDF_NAME(Lang), fz_string_from_text_language(buf, lang));
873 	pdf_dirty_annot(ctx, annot);
874 }
875 
876 int
pdf_annot_quadding(fz_context * ctx,pdf_annot * annot)877 pdf_annot_quadding(fz_context *ctx, pdf_annot *annot)
878 {
879 	int q = pdf_dict_get_int(ctx, annot->obj, PDF_NAME(Q));
880 	return (q < 0 || q > 2) ? 0 : q;
881 }
882 
883 void
pdf_set_annot_quadding(fz_context * ctx,pdf_annot * annot,int q)884 pdf_set_annot_quadding(fz_context *ctx, pdf_annot *annot, int q)
885 {
886 	q = (q < 0 || q > 2) ? 0 : q;
887 	pdf_dict_put_int(ctx, annot->obj, PDF_NAME(Q), q);
888 	pdf_dirty_annot(ctx, annot);
889 }
890 
pdf_annot_opacity(fz_context * ctx,pdf_annot * annot)891 float pdf_annot_opacity(fz_context *ctx, pdf_annot *annot)
892 {
893 	pdf_obj *ca = pdf_dict_get(ctx, annot->obj, PDF_NAME(CA));
894 	if (pdf_is_number(ctx, ca))
895 		return pdf_to_real(ctx, ca);
896 	return 1;
897 }
898 
pdf_set_annot_opacity(fz_context * ctx,pdf_annot * annot,float opacity)899 void pdf_set_annot_opacity(fz_context *ctx, pdf_annot *annot, float opacity)
900 {
901 	if (opacity != 1)
902 		pdf_dict_put_real(ctx, annot->obj, PDF_NAME(CA), opacity);
903 	else
904 		pdf_dict_del(ctx, annot->obj, PDF_NAME(CA));
905 	pdf_dirty_annot(ctx, annot);
906 }
907 
pdf_annot_color_imp(fz_context * ctx,pdf_obj * arr,int * n,float color[4])908 static void pdf_annot_color_imp(fz_context *ctx, pdf_obj *arr, int *n, float color[4])
909 {
910 	switch (pdf_array_len(ctx, arr))
911 	{
912 	case 0:
913 		if (n)
914 			*n = 0;
915 		break;
916 	case 1:
917 	case 2:
918 		if (n)
919 			*n = 1;
920 		if (color)
921 			color[0] = pdf_array_get_real(ctx, arr, 0);
922 		break;
923 	case 3:
924 		if (n)
925 			*n = 3;
926 		if (color)
927 		{
928 			color[0] = pdf_array_get_real(ctx, arr, 0);
929 			color[1] = pdf_array_get_real(ctx, arr, 1);
930 			color[2] = pdf_array_get_real(ctx, arr, 2);
931 		}
932 		break;
933 	case 4:
934 	default:
935 		if (n)
936 			*n = 4;
937 		if (color)
938 		{
939 			color[0] = pdf_array_get_real(ctx, arr, 0);
940 			color[1] = pdf_array_get_real(ctx, arr, 1);
941 			color[2] = pdf_array_get_real(ctx, arr, 2);
942 			color[3] = pdf_array_get_real(ctx, arr, 3);
943 		}
944 		break;
945 	}
946 }
947 
pdf_annot_color_rgb(fz_context * ctx,pdf_obj * arr,float rgb[3])948 static int pdf_annot_color_rgb(fz_context *ctx, pdf_obj *arr, float rgb[3])
949 {
950 	float color[4];
951 	int n;
952 	pdf_annot_color_imp(ctx, arr, &n, color);
953 	if (n == 0)
954 	{
955 		return 0;
956 	}
957 	else if (n == 1)
958 	{
959 		rgb[0] = rgb[1] = rgb[2] = color[0];
960 	}
961 	else if (n == 3)
962 	{
963 		rgb[0] = color[0];
964 		rgb[1] = color[1];
965 		rgb[2] = color[2];
966 	}
967 	else if (n == 4)
968 	{
969 		rgb[0] = 1 - fz_min(1, color[0] + color[3]);
970 		rgb[1] = 1 - fz_min(1, color[1] + color[3]);
971 		rgb[2] = 1 - fz_min(1, color[2] + color[3]);
972 	}
973 	return 1;
974 }
975 
pdf_set_annot_color_imp(fz_context * ctx,pdf_annot * annot,pdf_obj * key,int n,const float * color,pdf_obj ** allowed)976 static void pdf_set_annot_color_imp(fz_context *ctx, pdf_annot *annot, pdf_obj *key, int n, const float *color, pdf_obj **allowed)
977 {
978 	pdf_document *doc = annot->page->doc;
979 	pdf_obj *arr;
980 
981 	if (allowed)
982 		check_allowed_subtypes(ctx, annot, key, allowed);
983 	if (n != 0 && n != 1 && n != 3 && n != 4)
984 		fz_throw(ctx, FZ_ERROR_GENERIC, "color must be 0, 1, 3 or 4 components");
985 	if (!color)
986 		fz_throw(ctx, FZ_ERROR_GENERIC, "no color given");
987 
988 	arr = pdf_new_array(ctx, doc, n);
989 	fz_try(ctx)
990 	{
991 		switch (n)
992 		{
993 		case 1:
994 			pdf_array_push_real(ctx, arr, color[0]);
995 			break;
996 		case 3:
997 			pdf_array_push_real(ctx, arr, color[0]);
998 			pdf_array_push_real(ctx, arr, color[1]);
999 			pdf_array_push_real(ctx, arr, color[2]);
1000 			break;
1001 		case 4:
1002 			pdf_array_push_real(ctx, arr, color[0]);
1003 			pdf_array_push_real(ctx, arr, color[1]);
1004 			pdf_array_push_real(ctx, arr, color[2]);
1005 			pdf_array_push_real(ctx, arr, color[3]);
1006 			break;
1007 		}
1008 	}
1009 	fz_catch(ctx)
1010 	{
1011 		pdf_drop_obj(ctx, arr);
1012 		fz_rethrow(ctx);
1013 	}
1014 
1015 	pdf_dict_put_drop(ctx, annot->obj, key, arr);
1016 	pdf_dirty_annot(ctx, annot);
1017 }
1018 
1019 void
pdf_annot_color(fz_context * ctx,pdf_annot * annot,int * n,float color[4])1020 pdf_annot_color(fz_context *ctx, pdf_annot *annot, int *n, float color[4])
1021 {
1022 	pdf_obj *c = pdf_dict_get(ctx, annot->obj, PDF_NAME(C));
1023 	pdf_annot_color_imp(ctx, c, n, color);
1024 }
1025 
1026 void
pdf_annot_MK_BG(fz_context * ctx,pdf_annot * annot,int * n,float color[4])1027 pdf_annot_MK_BG(fz_context *ctx, pdf_annot *annot, int *n, float color[4])
1028 {
1029 	pdf_obj *mk_bg = pdf_dict_get(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME(MK)), PDF_NAME(BG));
1030 	pdf_annot_color_imp(ctx, mk_bg, n, color);
1031 }
1032 
1033 int
pdf_annot_MK_BG_rgb(fz_context * ctx,pdf_annot * annot,float rgb[3])1034 pdf_annot_MK_BG_rgb(fz_context *ctx, pdf_annot *annot, float rgb[3])
1035 {
1036 	pdf_obj *mk_bg = pdf_dict_get(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME(MK)), PDF_NAME(BG));
1037 	return pdf_annot_color_rgb(ctx, mk_bg, rgb);
1038 }
1039 
1040 void
pdf_annot_MK_BC(fz_context * ctx,pdf_annot * annot,int * n,float color[4])1041 pdf_annot_MK_BC(fz_context *ctx, pdf_annot *annot, int *n, float color[4])
1042 {
1043 	pdf_obj *mk_bc = pdf_dict_get(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME(MK)), PDF_NAME(BC));
1044 	pdf_annot_color_imp(ctx, mk_bc, n, color);
1045 }
1046 
1047 int
pdf_annot_MK_BC_rgb(fz_context * ctx,pdf_annot * annot,float rgb[3])1048 pdf_annot_MK_BC_rgb(fz_context *ctx, pdf_annot *annot, float rgb[3])
1049 {
1050 	pdf_obj *mk_bc = pdf_dict_get(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME(MK)), PDF_NAME(BC));
1051 	return pdf_annot_color_rgb(ctx, mk_bc, rgb);
1052 }
1053 
1054 void
pdf_set_annot_color(fz_context * ctx,pdf_annot * annot,int n,const float * color)1055 pdf_set_annot_color(fz_context *ctx, pdf_annot *annot, int n, const float *color)
1056 {
1057 	pdf_set_annot_color_imp(ctx, annot, PDF_NAME(C), n, color, NULL);
1058 }
1059 
1060 static pdf_obj *interior_color_subtypes[] = {
1061 	PDF_NAME(Circle),
1062 	PDF_NAME(Line),
1063 	PDF_NAME(PolyLine),
1064 	PDF_NAME(Polygon),
1065 	PDF_NAME(Square),
1066 	NULL,
1067 };
1068 
1069 int
pdf_annot_has_interior_color(fz_context * ctx,pdf_annot * annot)1070 pdf_annot_has_interior_color(fz_context *ctx, pdf_annot *annot)
1071 {
1072 	return is_allowed_subtype(ctx, annot, PDF_NAME(IC), interior_color_subtypes);
1073 }
1074 
1075 void
pdf_annot_interior_color(fz_context * ctx,pdf_annot * annot,int * n,float color[4])1076 pdf_annot_interior_color(fz_context *ctx, pdf_annot *annot, int *n, float color[4])
1077 {
1078 	pdf_obj *ic = pdf_dict_get(ctx, annot->obj, PDF_NAME(IC));
1079 	pdf_annot_color_imp(ctx, ic, n, color);
1080 }
1081 
1082 void
pdf_set_annot_interior_color(fz_context * ctx,pdf_annot * annot,int n,const float * color)1083 pdf_set_annot_interior_color(fz_context *ctx, pdf_annot *annot, int n, const float *color)
1084 {
1085 	pdf_set_annot_color_imp(ctx, annot, PDF_NAME(IC), n, color, interior_color_subtypes);
1086 }
1087 
1088 static pdf_obj *line_subtypes[] = {
1089 	PDF_NAME(Line),
1090 	NULL,
1091 };
1092 
1093 int
pdf_annot_has_line(fz_context * ctx,pdf_annot * annot)1094 pdf_annot_has_line(fz_context *ctx, pdf_annot *annot)
1095 {
1096 	return is_allowed_subtype(ctx, annot, PDF_NAME(L), line_subtypes);
1097 }
1098 
1099 void
pdf_annot_line(fz_context * ctx,pdf_annot * annot,fz_point * a,fz_point * b)1100 pdf_annot_line(fz_context *ctx, pdf_annot *annot, fz_point *a, fz_point *b)
1101 {
1102 	fz_matrix page_ctm;
1103 	pdf_obj *line;
1104 
1105 	check_allowed_subtypes(ctx, annot, PDF_NAME(L), line_subtypes);
1106 
1107 	pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
1108 
1109 	line = pdf_dict_get(ctx, annot->obj, PDF_NAME(L));
1110 	a->x = pdf_array_get_real(ctx, line, 0);
1111 	a->y = pdf_array_get_real(ctx, line, 1);
1112 	b->x = pdf_array_get_real(ctx, line, 2);
1113 	b->y = pdf_array_get_real(ctx, line, 3);
1114 	*a = fz_transform_point(*a, page_ctm);
1115 	*b = fz_transform_point(*b, page_ctm);
1116 }
1117 
1118 void
pdf_set_annot_line(fz_context * ctx,pdf_annot * annot,fz_point a,fz_point b)1119 pdf_set_annot_line(fz_context *ctx, pdf_annot *annot, fz_point a, fz_point b)
1120 {
1121 	fz_matrix page_ctm, inv_page_ctm;
1122 	pdf_obj *line;
1123 
1124 	check_allowed_subtypes(ctx, annot, PDF_NAME(L), line_subtypes);
1125 
1126 	pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
1127 	inv_page_ctm = fz_invert_matrix(page_ctm);
1128 
1129 	a = fz_transform_point(a, inv_page_ctm);
1130 	b = fz_transform_point(b, inv_page_ctm);
1131 
1132 	line = pdf_new_array(ctx, annot->page->doc, 4);
1133 	pdf_dict_put_drop(ctx, annot->obj, PDF_NAME(L), line);
1134 	pdf_array_push_real(ctx, line, a.x);
1135 	pdf_array_push_real(ctx, line, a.y);
1136 	pdf_array_push_real(ctx, line, b.x);
1137 	pdf_array_push_real(ctx, line, b.y);
1138 
1139 	pdf_dirty_annot(ctx, annot);
1140 }
1141 
1142 static pdf_obj *vertices_subtypes[] = {
1143 	PDF_NAME(PolyLine),
1144 	PDF_NAME(Polygon),
1145 	NULL,
1146 };
1147 
1148 int
pdf_annot_has_vertices(fz_context * ctx,pdf_annot * annot)1149 pdf_annot_has_vertices(fz_context *ctx, pdf_annot *annot)
1150 {
1151 	return is_allowed_subtype(ctx, annot, PDF_NAME(Vertices), vertices_subtypes);
1152 }
1153 
1154 int
pdf_annot_vertex_count(fz_context * ctx,pdf_annot * annot)1155 pdf_annot_vertex_count(fz_context *ctx, pdf_annot *annot)
1156 {
1157 	pdf_obj *vertices;
1158 	check_allowed_subtypes(ctx, annot, PDF_NAME(Vertices), vertices_subtypes);
1159 	vertices = pdf_dict_get(ctx, annot->obj, PDF_NAME(Vertices));
1160 	return pdf_array_len(ctx, vertices) / 2;
1161 }
1162 
1163 fz_point
pdf_annot_vertex(fz_context * ctx,pdf_annot * annot,int i)1164 pdf_annot_vertex(fz_context *ctx, pdf_annot *annot, int i)
1165 {
1166 	pdf_obj *vertices;
1167 	fz_matrix page_ctm;
1168 	fz_point point;
1169 
1170 	check_allowed_subtypes(ctx, annot, PDF_NAME(Vertices), vertices_subtypes);
1171 
1172 	vertices = pdf_dict_get(ctx, annot->obj, PDF_NAME(Vertices));
1173 
1174 	pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
1175 
1176 	point.x = pdf_array_get_real(ctx, vertices, i * 2);
1177 	point.y = pdf_array_get_real(ctx, vertices, i * 2 + 1);
1178 	return fz_transform_point(point, page_ctm);
1179 }
1180 
1181 void
pdf_set_annot_vertices(fz_context * ctx,pdf_annot * annot,int n,const fz_point * v)1182 pdf_set_annot_vertices(fz_context *ctx, pdf_annot *annot, int n, const fz_point *v)
1183 {
1184 	pdf_document *doc = annot->page->doc;
1185 	fz_matrix page_ctm, inv_page_ctm;
1186 	pdf_obj *vertices;
1187 	fz_point point;
1188 	int i;
1189 
1190 	check_allowed_subtypes(ctx, annot, PDF_NAME(Vertices), vertices_subtypes);
1191 	if (n <= 0 || !v)
1192 		fz_throw(ctx, FZ_ERROR_GENERIC, "invalid number of vertices");
1193 
1194 	pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
1195 	inv_page_ctm = fz_invert_matrix(page_ctm);
1196 
1197 	vertices = pdf_new_array(ctx, doc, n * 2);
1198 	for (i = 0; i < n; ++i)
1199 	{
1200 		point = fz_transform_point(v[i], inv_page_ctm);
1201 		pdf_array_push_real(ctx, vertices, point.x);
1202 		pdf_array_push_real(ctx, vertices, point.y);
1203 	}
1204 	pdf_dict_put_drop(ctx, annot->obj, PDF_NAME(Vertices), vertices);
1205 	pdf_dirty_annot(ctx, annot);
1206 }
1207 
pdf_clear_annot_vertices(fz_context * ctx,pdf_annot * annot)1208 void pdf_clear_annot_vertices(fz_context *ctx, pdf_annot *annot)
1209 {
1210 	check_allowed_subtypes(ctx, annot, PDF_NAME(Vertices), vertices_subtypes);
1211 	pdf_dict_del(ctx, annot->obj, PDF_NAME(Vertices));
1212 	pdf_dirty_annot(ctx, annot);
1213 }
1214 
pdf_add_annot_vertex(fz_context * ctx,pdf_annot * annot,fz_point p)1215 void pdf_add_annot_vertex(fz_context *ctx, pdf_annot *annot, fz_point p)
1216 {
1217 	pdf_document *doc = annot->page->doc;
1218 	fz_matrix page_ctm, inv_page_ctm;
1219 	pdf_obj *vertices;
1220 
1221 	check_allowed_subtypes(ctx, annot, PDF_NAME(Vertices), vertices_subtypes);
1222 
1223 	pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
1224 	inv_page_ctm = fz_invert_matrix(page_ctm);
1225 
1226 	vertices = pdf_dict_get(ctx, annot->obj, PDF_NAME(Vertices));
1227 	if (!pdf_is_array(ctx, vertices))
1228 	{
1229 		vertices = pdf_new_array(ctx, doc, 32);
1230 		pdf_dict_put_drop(ctx, annot->obj, PDF_NAME(Vertices), vertices);
1231 	}
1232 
1233 	p = fz_transform_point(p, inv_page_ctm);
1234 	pdf_array_push_real(ctx, vertices, p.x);
1235 	pdf_array_push_real(ctx, vertices, p.y);
1236 
1237 	pdf_dirty_annot(ctx, annot);
1238 }
1239 
pdf_set_annot_vertex(fz_context * ctx,pdf_annot * annot,int i,fz_point p)1240 void pdf_set_annot_vertex(fz_context *ctx, pdf_annot *annot, int i, fz_point p)
1241 {
1242 	fz_matrix page_ctm, inv_page_ctm;
1243 	pdf_obj *vertices;
1244 
1245 	check_allowed_subtypes(ctx, annot, PDF_NAME(Vertices), vertices_subtypes);
1246 
1247 	pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
1248 	inv_page_ctm = fz_invert_matrix(page_ctm);
1249 
1250 	p = fz_transform_point(p, inv_page_ctm);
1251 
1252 	vertices = pdf_dict_get(ctx, annot->obj, PDF_NAME(Vertices));
1253 	pdf_array_put_drop(ctx, vertices, i * 2 + 0, pdf_new_real(ctx, p.x));
1254 	pdf_array_put_drop(ctx, vertices, i * 2 + 1, pdf_new_real(ctx, p.y));
1255 }
1256 
1257 static pdf_obj *quad_point_subtypes[] = {
1258 	PDF_NAME(Highlight),
1259 	PDF_NAME(Link),
1260 	PDF_NAME(Squiggly),
1261 	PDF_NAME(StrikeOut),
1262 	PDF_NAME(Underline),
1263 	PDF_NAME(Redact),
1264 	NULL,
1265 };
1266 
1267 int
pdf_annot_has_quad_points(fz_context * ctx,pdf_annot * annot)1268 pdf_annot_has_quad_points(fz_context *ctx, pdf_annot *annot)
1269 {
1270 	return is_allowed_subtype(ctx, annot, PDF_NAME(QuadPoints), quad_point_subtypes);
1271 }
1272 
1273 int
pdf_annot_quad_point_count(fz_context * ctx,pdf_annot * annot)1274 pdf_annot_quad_point_count(fz_context *ctx, pdf_annot *annot)
1275 {
1276 	pdf_obj *quad_points;
1277 	check_allowed_subtypes(ctx, annot, PDF_NAME(QuadPoints), quad_point_subtypes);
1278 	quad_points = pdf_dict_get(ctx, annot->obj, PDF_NAME(QuadPoints));
1279 	return pdf_array_len(ctx, quad_points) / 8;
1280 }
1281 
1282 fz_quad
pdf_annot_quad_point(fz_context * ctx,pdf_annot * annot,int idx)1283 pdf_annot_quad_point(fz_context *ctx, pdf_annot *annot, int idx)
1284 {
1285 	pdf_obj *quad_points;
1286 	fz_matrix page_ctm;
1287 	float v[8];
1288 	int i;
1289 
1290 	check_allowed_subtypes(ctx, annot, PDF_NAME(QuadPoints), quad_point_subtypes);
1291 	quad_points = pdf_dict_get(ctx, annot->obj, PDF_NAME(QuadPoints));
1292 	pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
1293 
1294 	for (i = 0; i < 8; i += 2)
1295 	{
1296 		fz_point point;
1297 		point.x = pdf_array_get_real(ctx, quad_points, idx * 8 + i + 0);
1298 		point.y = pdf_array_get_real(ctx, quad_points, idx * 8 + i + 1);
1299 		point = fz_transform_point(point, page_ctm);
1300 		v[i+0] = point.x;
1301 		v[i+1] = point.y;
1302 	}
1303 
1304 	return fz_make_quad(v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7]);
1305 }
1306 
1307 void
pdf_set_annot_quad_points(fz_context * ctx,pdf_annot * annot,int n,const fz_quad * q)1308 pdf_set_annot_quad_points(fz_context *ctx, pdf_annot *annot, int n, const fz_quad *q)
1309 {
1310 	pdf_document *doc = annot->page->doc;
1311 	fz_matrix page_ctm, inv_page_ctm;
1312 	pdf_obj *quad_points;
1313 	fz_quad quad;
1314 	int i;
1315 
1316 	check_allowed_subtypes(ctx, annot, PDF_NAME(QuadPoints), quad_point_subtypes);
1317 	if (n <= 0 || !q)
1318 		fz_throw(ctx, FZ_ERROR_GENERIC, "invalid number of quadrilaterals");
1319 
1320 	pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
1321 	inv_page_ctm = fz_invert_matrix(page_ctm);
1322 
1323 	quad_points = pdf_new_array(ctx, doc, n);
1324 	for (i = 0; i < n; ++i)
1325 	{
1326 		quad = fz_transform_quad(q[i], inv_page_ctm);
1327 		pdf_array_push_real(ctx, quad_points, quad.ul.x);
1328 		pdf_array_push_real(ctx, quad_points, quad.ul.y);
1329 		pdf_array_push_real(ctx, quad_points, quad.ur.x);
1330 		pdf_array_push_real(ctx, quad_points, quad.ur.y);
1331 		pdf_array_push_real(ctx, quad_points, quad.ll.x);
1332 		pdf_array_push_real(ctx, quad_points, quad.ll.y);
1333 		pdf_array_push_real(ctx, quad_points, quad.lr.x);
1334 		pdf_array_push_real(ctx, quad_points, quad.lr.y);
1335 	}
1336 	pdf_dict_put_drop(ctx, annot->obj, PDF_NAME(QuadPoints), quad_points);
1337 	pdf_dirty_annot(ctx, annot);
1338 }
1339 
1340 void
pdf_clear_annot_quad_points(fz_context * ctx,pdf_annot * annot)1341 pdf_clear_annot_quad_points(fz_context *ctx, pdf_annot *annot)
1342 {
1343 	check_allowed_subtypes(ctx, annot, PDF_NAME(QuadPoints), quad_point_subtypes);
1344 	pdf_dict_del(ctx, annot->obj, PDF_NAME(QuadPoints));
1345 	pdf_dirty_annot(ctx, annot);
1346 }
1347 
1348 void
pdf_add_annot_quad_point(fz_context * ctx,pdf_annot * annot,fz_quad quad)1349 pdf_add_annot_quad_point(fz_context *ctx, pdf_annot *annot, fz_quad quad)
1350 {
1351 	pdf_document *doc = annot->page->doc;
1352 	fz_matrix page_ctm, inv_page_ctm;
1353 	pdf_obj *quad_points;
1354 
1355 	check_allowed_subtypes(ctx, annot, PDF_NAME(QuadPoints), quad_point_subtypes);
1356 
1357 	pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
1358 	inv_page_ctm = fz_invert_matrix(page_ctm);
1359 
1360 	quad_points = pdf_dict_get(ctx, annot->obj, PDF_NAME(QuadPoints));
1361 	if (!pdf_is_array(ctx, quad_points))
1362 	{
1363 		quad_points = pdf_new_array(ctx, doc, 8);
1364 		pdf_dict_put_drop(ctx, annot->obj, PDF_NAME(QuadPoints), quad_points);
1365 	}
1366 
1367 	/* Contrary to the specification, the points within a QuadPoint are NOT ordered
1368 	 * in a counterclockwise fashion. Experiments with Adobe's implementation
1369 	 * indicates a cross-wise ordering is intended: ul, ur, ll, lr.
1370 	 */
1371 	quad = fz_transform_quad(quad, inv_page_ctm);
1372 	pdf_array_push_real(ctx, quad_points, quad.ul.x);
1373 	pdf_array_push_real(ctx, quad_points, quad.ul.y);
1374 	pdf_array_push_real(ctx, quad_points, quad.ur.x);
1375 	pdf_array_push_real(ctx, quad_points, quad.ur.y);
1376 	pdf_array_push_real(ctx, quad_points, quad.ll.x);
1377 	pdf_array_push_real(ctx, quad_points, quad.ll.y);
1378 	pdf_array_push_real(ctx, quad_points, quad.lr.x);
1379 	pdf_array_push_real(ctx, quad_points, quad.lr.y);
1380 
1381 	pdf_dirty_annot(ctx, annot);
1382 }
1383 
1384 static pdf_obj *ink_list_subtypes[] = {
1385 	PDF_NAME(Ink),
1386 	NULL,
1387 };
1388 
1389 int
pdf_annot_has_ink_list(fz_context * ctx,pdf_annot * annot)1390 pdf_annot_has_ink_list(fz_context *ctx, pdf_annot *annot)
1391 {
1392 	return is_allowed_subtype(ctx, annot, PDF_NAME(InkList), ink_list_subtypes);
1393 }
1394 
1395 int
pdf_annot_ink_list_count(fz_context * ctx,pdf_annot * annot)1396 pdf_annot_ink_list_count(fz_context *ctx, pdf_annot *annot)
1397 {
1398 	pdf_obj *ink_list;
1399 	check_allowed_subtypes(ctx, annot, PDF_NAME(InkList), ink_list_subtypes);
1400 	ink_list = pdf_dict_get(ctx, annot->obj, PDF_NAME(InkList));
1401 	return pdf_array_len(ctx, ink_list);
1402 }
1403 
1404 int
pdf_annot_ink_list_stroke_count(fz_context * ctx,pdf_annot * annot,int i)1405 pdf_annot_ink_list_stroke_count(fz_context *ctx, pdf_annot *annot, int i)
1406 {
1407 	pdf_obj *ink_list;
1408 	pdf_obj *stroke;
1409 	check_allowed_subtypes(ctx, annot, PDF_NAME(InkList), ink_list_subtypes);
1410 	ink_list = pdf_dict_get(ctx, annot->obj, PDF_NAME(InkList));
1411 	stroke = pdf_array_get(ctx, ink_list, i);
1412 	return pdf_array_len(ctx, stroke) / 2;
1413 }
1414 
1415 fz_point
pdf_annot_ink_list_stroke_vertex(fz_context * ctx,pdf_annot * annot,int i,int k)1416 pdf_annot_ink_list_stroke_vertex(fz_context *ctx, pdf_annot *annot, int i, int k)
1417 {
1418 	pdf_obj *ink_list;
1419 	pdf_obj *stroke;
1420 	fz_matrix page_ctm;
1421 	fz_point point;
1422 
1423 	check_allowed_subtypes(ctx, annot, PDF_NAME(InkList), ink_list_subtypes);
1424 
1425 	ink_list = pdf_dict_get(ctx, annot->obj, PDF_NAME(InkList));
1426 	stroke = pdf_array_get(ctx, ink_list, i);
1427 
1428 	pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
1429 
1430 	point.x = pdf_array_get_real(ctx, stroke, k * 2 + 0);
1431 	point.y = pdf_array_get_real(ctx, stroke, k * 2 + 1);
1432 	return fz_transform_point(point, page_ctm);
1433 }
1434 
1435 /* FIXME: try/catch required for memory exhaustion */
1436 void
pdf_set_annot_ink_list(fz_context * ctx,pdf_annot * annot,int n,const int * count,const fz_point * v)1437 pdf_set_annot_ink_list(fz_context *ctx, pdf_annot *annot, int n, const int *count, const fz_point *v)
1438 {
1439 	pdf_document *doc = annot->page->doc;
1440 	fz_matrix page_ctm, inv_page_ctm;
1441 	pdf_obj *ink_list, *stroke;
1442 	fz_point point;
1443 	int i, k;
1444 
1445 	check_allowed_subtypes(ctx, annot, PDF_NAME(InkList), ink_list_subtypes);
1446 
1447 	pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
1448 	inv_page_ctm = fz_invert_matrix(page_ctm);
1449 
1450 	// TODO: update Rect (in update appearance perhaps?)
1451 
1452 	ink_list = pdf_new_array(ctx, doc, n);
1453 	for (i = 0; i < n; ++i)
1454 	{
1455 		stroke = pdf_new_array(ctx, doc, count[i] * 2);
1456 		for (k = 0; k < count[i]; ++k)
1457 		{
1458 			point = fz_transform_point(*v++, inv_page_ctm);
1459 			pdf_array_push_real(ctx, stroke, point.x);
1460 			pdf_array_push_real(ctx, stroke, point.y);
1461 		}
1462 		pdf_array_push_drop(ctx, ink_list, stroke);
1463 	}
1464 	pdf_dict_put_drop(ctx, annot->obj, PDF_NAME(InkList), ink_list);
1465 	pdf_dirty_annot(ctx, annot);
1466 }
1467 
1468 void
pdf_clear_annot_ink_list(fz_context * ctx,pdf_annot * annot)1469 pdf_clear_annot_ink_list(fz_context *ctx, pdf_annot *annot)
1470 {
1471 	pdf_dict_del(ctx, annot->obj, PDF_NAME(InkList));
1472 	pdf_dirty_annot(ctx, annot);
1473 }
1474 
pdf_add_annot_ink_list_stroke(fz_context * ctx,pdf_annot * annot)1475 void pdf_add_annot_ink_list_stroke(fz_context *ctx, pdf_annot *annot)
1476 {
1477 	pdf_obj *ink_list;
1478 
1479 	ink_list = pdf_dict_get(ctx, annot->obj, PDF_NAME(InkList));
1480 	if (!pdf_is_array(ctx, ink_list))
1481 		ink_list = pdf_dict_put_array(ctx, annot->obj, PDF_NAME(InkList), 10);
1482 
1483 	pdf_array_push_array(ctx, ink_list, 16);
1484 
1485 	pdf_dirty_annot(ctx, annot);
1486 }
1487 
pdf_add_annot_ink_list_stroke_vertex(fz_context * ctx,pdf_annot * annot,fz_point p)1488 void pdf_add_annot_ink_list_stroke_vertex(fz_context *ctx, pdf_annot *annot, fz_point p)
1489 {
1490 	fz_matrix page_ctm, inv_page_ctm;
1491 	pdf_obj *ink_list, *stroke;
1492 
1493 	pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
1494 	inv_page_ctm = fz_invert_matrix(page_ctm);
1495 
1496 	ink_list = pdf_dict_get(ctx, annot->obj, PDF_NAME(InkList));
1497 	stroke = pdf_array_get(ctx, ink_list, pdf_array_len(ctx, ink_list)-1);
1498 
1499 	p = fz_transform_point(p, inv_page_ctm);
1500 	pdf_array_push_real(ctx, stroke, p.x);
1501 	pdf_array_push_real(ctx, stroke, p.y);
1502 
1503 	pdf_dirty_annot(ctx, annot);
1504 }
1505 
1506 void
pdf_add_annot_ink_list(fz_context * ctx,pdf_annot * annot,int n,fz_point p[])1507 pdf_add_annot_ink_list(fz_context *ctx, pdf_annot *annot, int n, fz_point p[])
1508 {
1509 	fz_matrix page_ctm, inv_page_ctm;
1510 	pdf_obj *ink_list, *stroke;
1511 	int i;
1512 
1513 	check_allowed_subtypes(ctx, annot, PDF_NAME(InkList), ink_list_subtypes);
1514 
1515 	pdf_page_transform(ctx, annot->page, NULL, &page_ctm);
1516 	inv_page_ctm = fz_invert_matrix(page_ctm);
1517 
1518 	ink_list = pdf_dict_get(ctx, annot->obj, PDF_NAME(InkList));
1519 	if (!pdf_is_array(ctx, ink_list))
1520 		ink_list = pdf_dict_put_array(ctx, annot->obj, PDF_NAME(InkList), 10);
1521 
1522 	stroke = pdf_array_push_array(ctx, ink_list, n * 2);
1523 	for (i = 0; i < n; ++i)
1524 	{
1525 		fz_point tp = fz_transform_point(p[i], inv_page_ctm);
1526 		pdf_array_push_real(ctx, stroke, tp.x);
1527 		pdf_array_push_real(ctx, stroke, tp.y);
1528 	}
1529 
1530 	pdf_dirty_annot(ctx, annot);
1531 }
1532 
1533 static pdf_obj *markup_subtypes[] = {
1534 	PDF_NAME(Text),
1535 	PDF_NAME(FreeText),
1536 	PDF_NAME(Line),
1537 	PDF_NAME(Square),
1538 	PDF_NAME(Circle),
1539 	PDF_NAME(Polygon),
1540 	PDF_NAME(PolyLine),
1541 	PDF_NAME(Highlight),
1542 	PDF_NAME(Underline),
1543 	PDF_NAME(Squiggly),
1544 	PDF_NAME(StrikeOut),
1545 	PDF_NAME(Redact),
1546 	PDF_NAME(Stamp),
1547 	PDF_NAME(Caret),
1548 	PDF_NAME(Ink),
1549 	PDF_NAME(FileAttachment),
1550 	PDF_NAME(Sound),
1551 	NULL,
1552 };
1553 
1554 /*
1555 	Get annotation's modification date in seconds since the epoch.
1556 */
1557 int64_t
pdf_annot_modification_date(fz_context * ctx,pdf_annot * annot)1558 pdf_annot_modification_date(fz_context *ctx, pdf_annot *annot)
1559 {
1560 	return pdf_dict_get_date(ctx, annot->obj, PDF_NAME(M));
1561 }
1562 
1563 /*
1564 	Get annotation's creation date in seconds since the epoch.
1565 */
1566 int64_t
pdf_annot_creation_date(fz_context * ctx,pdf_annot * annot)1567 pdf_annot_creation_date(fz_context *ctx, pdf_annot *annot)
1568 {
1569 	return pdf_dict_get_date(ctx, annot->obj, PDF_NAME(CreationDate));
1570 }
1571 
1572 /*
1573 	Set annotation's modification date in seconds since the epoch.
1574 */
1575 void
pdf_set_annot_modification_date(fz_context * ctx,pdf_annot * annot,int64_t secs)1576 pdf_set_annot_modification_date(fz_context *ctx, pdf_annot *annot, int64_t secs)
1577 {
1578 	check_allowed_subtypes(ctx, annot, PDF_NAME(M), markup_subtypes);
1579 	pdf_dict_put_date(ctx, annot->obj, PDF_NAME(M), secs);
1580 	pdf_dirty_annot(ctx, annot);
1581 }
1582 
1583 /*
1584 	Set annotation's creation date in seconds since the epoch.
1585 */
1586 void
pdf_set_annot_creation_date(fz_context * ctx,pdf_annot * annot,int64_t secs)1587 pdf_set_annot_creation_date(fz_context *ctx, pdf_annot *annot, int64_t secs)
1588 {
1589 	check_allowed_subtypes(ctx, annot, PDF_NAME(CreationDate), markup_subtypes);
1590 	pdf_dict_put_date(ctx, annot->obj, PDF_NAME(CreationDate), secs);
1591 	pdf_dirty_annot(ctx, annot);
1592 }
1593 
1594 int
pdf_annot_has_author(fz_context * ctx,pdf_annot * annot)1595 pdf_annot_has_author(fz_context *ctx, pdf_annot *annot)
1596 {
1597 	return is_allowed_subtype(ctx, annot, PDF_NAME(T), markup_subtypes);
1598 }
1599 
1600 const char *
pdf_annot_author(fz_context * ctx,pdf_annot * annot)1601 pdf_annot_author(fz_context *ctx, pdf_annot *annot)
1602 {
1603 	check_allowed_subtypes(ctx, annot, PDF_NAME(T), markup_subtypes);
1604 	return pdf_dict_get_text_string(ctx, annot->obj, PDF_NAME(T));
1605 }
1606 
1607 void
pdf_set_annot_author(fz_context * ctx,pdf_annot * annot,const char * author)1608 pdf_set_annot_author(fz_context *ctx, pdf_annot *annot, const char *author)
1609 {
1610 	check_allowed_subtypes(ctx, annot, PDF_NAME(T), markup_subtypes);
1611 	pdf_dict_put_text_string(ctx, annot->obj, PDF_NAME(T), author);
1612 	pdf_dirty_annot(ctx, annot);
1613 }
1614 
1615 void
pdf_parse_default_appearance(fz_context * ctx,const char * da,const char ** font,float * size,float color[3])1616 pdf_parse_default_appearance(fz_context *ctx, const char *da, const char **font, float *size, float color[3])
1617 {
1618 	char buf[100], *p = buf, *tok, *end;
1619 	float stack[3] = { 0, 0, 0 };
1620 	int top = 0;
1621 
1622 	*font = "Helv";
1623 	*size = 12;
1624 	color[0] = color[1] = color[2] = 0;
1625 
1626 	fz_strlcpy(buf, da, sizeof buf);
1627 	while ((tok = fz_strsep(&p, " \n\r\t")) != NULL)
1628 	{
1629 		if (tok[0] == 0)
1630 			;
1631 		else if (tok[0] == '/')
1632 		{
1633 			if (!strcmp(tok+1, "Cour")) *font = "Cour";
1634 			if (!strcmp(tok+1, "Helv")) *font = "Helv";
1635 			if (!strcmp(tok+1, "TiRo")) *font = "TiRo";
1636 			if (!strcmp(tok+1, "Symb")) *font = "Symb";
1637 			if (!strcmp(tok+1, "ZaDb")) *font = "ZaDb";
1638 		}
1639 		else if (!strcmp(tok, "Tf"))
1640 		{
1641 			*size = stack[0];
1642 			top = 0;
1643 		}
1644 		else if (!strcmp(tok, "g"))
1645 		{
1646 			color[0] = color[1] = color[2] = stack[0];
1647 			top = 0;
1648 		}
1649 		else if (!strcmp(tok, "rg"))
1650 		{
1651 			color[0] = stack[0];
1652 			color[1] = stack[1];
1653 			color[2] = stack[2];
1654 			top=0;
1655 		}
1656 		else
1657 		{
1658 			if (top < 3)
1659 				stack[top] = fz_strtof(tok, &end);
1660 			if (*end == 0)
1661 				++top;
1662 			else
1663 				top = 0;
1664 		}
1665 	}
1666 }
1667 
1668 void
pdf_print_default_appearance(fz_context * ctx,char * buf,int nbuf,const char * font,float size,const float color[3])1669 pdf_print_default_appearance(fz_context *ctx, char *buf, int nbuf, const char *font, float size, const float color[3])
1670 {
1671 	if (color[0] > 0 || color[1] > 0 || color[2] > 0)
1672 		fz_snprintf(buf, nbuf, "/%s %g Tf %g %g %g rg", font, size, color[0], color[1], color[2]);
1673 	else
1674 		fz_snprintf(buf, nbuf, "/%s %g Tf", font, size);
1675 }
1676 
1677 void
pdf_annot_default_appearance(fz_context * ctx,pdf_annot * annot,const char ** font,float * size,float color[3])1678 pdf_annot_default_appearance(fz_context *ctx, pdf_annot *annot, const char **font, float *size, float color[3])
1679 {
1680 	pdf_obj *da = pdf_dict_get_inheritable(ctx, annot->obj, PDF_NAME(DA));
1681 	if (!da)
1682 	{
1683 		pdf_obj *trailer = pdf_trailer(ctx, annot->page->doc);
1684 		da = pdf_dict_getl(ctx, trailer, PDF_NAME(Root), PDF_NAME(AcroForm), PDF_NAME(DA), NULL);
1685 	}
1686 	pdf_parse_default_appearance(ctx, pdf_to_str_buf(ctx, da), font, size, color);
1687 }
1688 
1689 void
pdf_set_annot_default_appearance(fz_context * ctx,pdf_annot * annot,const char * font,float size,const float color[3])1690 pdf_set_annot_default_appearance(fz_context *ctx, pdf_annot *annot, const char *font, float size, const float color[3])
1691 {
1692 	char buf[100];
1693 
1694 	pdf_print_default_appearance(ctx, buf, sizeof buf, font, size, color);
1695 
1696 	pdf_dict_put_string(ctx, annot->obj, PDF_NAME(DA), buf, strlen(buf));
1697 
1698 	pdf_dict_del(ctx, annot->obj, PDF_NAME(DS)); /* not supported */
1699 	pdf_dict_del(ctx, annot->obj, PDF_NAME(RC)); /* not supported */
1700 
1701 	pdf_dirty_annot(ctx, annot);
1702 }
1703