1 #include "mupdf/fitz.h"
2 #include "mupdf/pdf.h"
3 #include "mupdf/ucdn.h"
4 
5 #include <float.h>
6 #include <limits.h>
7 #include <math.h>
8 #include <string.h>
9 
10 #include <stdio.h>
11 
12 #include "annotation-icons.h"
13 
14 #define REPLACEMENT 0xB7
15 #define CIRCLE_MAGIC 0.551915f
16 
pdf_write_border_appearance(fz_context * ctx,pdf_annot * annot,fz_buffer * buf)17 static float pdf_write_border_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf)
18 {
19 	float w = pdf_annot_border(ctx, annot);
20 	fz_append_printf(ctx, buf, "%g w\n", w);
21 	return w;
22 }
23 
pdf_write_stroke_color_appearance(fz_context * ctx,pdf_annot * annot,fz_buffer * buf)24 static int pdf_write_stroke_color_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf)
25 {
26 	float color[4];
27 	int n;
28 	pdf_annot_color(ctx, annot, &n, color);
29 	switch (n)
30 	{
31 	default: return 0;
32 	case 1: fz_append_printf(ctx, buf, "%g G\n", color[0]); break;
33 	case 3: fz_append_printf(ctx, buf, "%g %g %g RG\n", color[0], color[1], color[2]); break;
34 	case 4: fz_append_printf(ctx, buf, "%g %g %g %g K\n", color[0], color[1], color[2], color[3]); break;
35 	}
36 	return 1;
37 }
38 
pdf_is_dark_fill_color(fz_context * ctx,pdf_annot * annot)39 static int pdf_is_dark_fill_color(fz_context *ctx, pdf_annot *annot)
40 {
41 	float color[4], gray;
42 	int n;
43 	pdf_annot_color(ctx, annot, &n, color);
44 	switch (n)
45 	{
46 	default:
47 		gray = 1;
48 		break;
49 	case 1:
50 		gray = color[0];
51 		break;
52 	case 3:
53 		gray = color[0] * 0.3f + color[1] * 0.59f + color[2] * 0.11f;
54 		break;
55 	case 4:
56 		gray = color[0] * 0.3f + color[1] * 0.59f + color[2] * 0.11f + color[3];
57 		gray = 1 - fz_min(gray, 1);
58 		break;
59 	}
60 	return gray < 0.25f;
61 }
62 
pdf_write_fill_color_appearance(fz_context * ctx,pdf_annot * annot,fz_buffer * buf)63 static int pdf_write_fill_color_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf)
64 {
65 	float color[4];
66 	int n;
67 	pdf_annot_color(ctx, annot, &n, color);
68 	switch (n)
69 	{
70 	default: return 0;
71 	case 1: fz_append_printf(ctx, buf, "%g g\n", color[0]); break;
72 	case 3: fz_append_printf(ctx, buf, "%g %g %g rg\n", color[0], color[1], color[2]); break;
73 	case 4: fz_append_printf(ctx, buf, "%g %g %g %g k\n", color[0], color[1], color[2], color[3]); break;
74 	}
75 	return 1;
76 }
77 
pdf_write_interior_fill_color_appearance(fz_context * ctx,pdf_annot * annot,fz_buffer * buf)78 static int pdf_write_interior_fill_color_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf)
79 {
80 	float color[4];
81 	int n;
82 	pdf_annot_interior_color(ctx, annot, &n, color);
83 	switch (n)
84 	{
85 	default: return 0;
86 	case 1: fz_append_printf(ctx, buf, "%g g\n", color[0]); break;
87 	case 3: fz_append_printf(ctx, buf, "%g %g %g rg\n", color[0], color[1], color[2]); break;
88 	case 4: fz_append_printf(ctx, buf, "%g %g %g %g k\n", color[0], color[1], color[2], color[3]); break;
89 	}
90 	return 1;
91 }
92 
pdf_write_MK_BG_appearance(fz_context * ctx,pdf_annot * annot,fz_buffer * buf)93 static int pdf_write_MK_BG_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf)
94 {
95 	float color[4];
96 	int n;
97 	pdf_annot_MK_BG(ctx, annot, &n, color);
98 	switch (n)
99 	{
100 	default: return 0;
101 	case 1: fz_append_printf(ctx, buf, "%g g\n", color[0]); break;
102 	case 3: fz_append_printf(ctx, buf, "%g %g %g rg\n", color[0], color[1], color[2]); break;
103 	case 4: fz_append_printf(ctx, buf, "%g %g %g %g k\n", color[0], color[1], color[2], color[3]); break;
104 	}
105 	return 1;
106 }
107 
pdf_write_MK_BC_appearance(fz_context * ctx,pdf_annot * annot,fz_buffer * buf)108 static int pdf_write_MK_BC_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf)
109 {
110 	float color[4];
111 	int n;
112 	pdf_annot_MK_BC(ctx, annot, &n, color);
113 	switch (n)
114 	{
115 	default: return 0;
116 	case 1: fz_append_printf(ctx, buf, "%g G\n", color[0]); break;
117 	case 3: fz_append_printf(ctx, buf, "%g %g %g RG\n", color[0], color[1], color[2]); break;
118 	case 4: fz_append_printf(ctx, buf, "%g %g %g %g K\n", color[0], color[1], color[2], color[3]); break;
119 	}
120 	return 1;
121 }
122 
maybe_stroke_and_fill(fz_context * ctx,fz_buffer * buf,int sc,int ic)123 static void maybe_stroke_and_fill(fz_context *ctx, fz_buffer *buf, int sc, int ic)
124 {
125 	if (sc)
126 		fz_append_string(ctx, buf, ic ? "b\n" : "s\n");
127 	else
128 		fz_append_string(ctx, buf, ic ? "f\n" : "n\n");
129 }
130 
maybe_stroke(fz_context * ctx,fz_buffer * buf,int sc)131 static void maybe_stroke(fz_context *ctx, fz_buffer *buf, int sc)
132 {
133 	fz_append_string(ctx, buf, sc ? "S\n" : "n\n");
134 }
135 
rotate_vector(float angle,float x,float y)136 static fz_point rotate_vector(float angle, float x, float y)
137 {
138 	float ca = cosf(angle);
139 	float sa = sinf(angle);
140 	return fz_make_point(x*ca - y*sa, x*sa + y*ca);
141 }
142 
pdf_write_arrow_appearance(fz_context * ctx,fz_buffer * buf,fz_rect * rect,float x,float y,float dx,float dy,float w)143 static void pdf_write_arrow_appearance(fz_context *ctx, fz_buffer *buf, fz_rect *rect, float x, float y, float dx, float dy, float w)
144 {
145 	float r = fz_max(1, w);
146 	float angle = atan2f(dy, dx);
147 	fz_point v, a, b;
148 
149 	v = rotate_vector(angle, 8.8f*r, 4.5f*r);
150 	a = fz_make_point(x + v.x, y + v.y);
151 	v = rotate_vector(angle, 8.8f*r, -4.5f*r);
152 	b = fz_make_point(x + v.x, y + v.y);
153 
154 	*rect = fz_include_point_in_rect(*rect, a);
155 	*rect = fz_include_point_in_rect(*rect, b);
156 	*rect = fz_expand_rect(*rect, w);
157 
158 	fz_append_printf(ctx, buf, "%g %g m\n", a.x, a.y);
159 	fz_append_printf(ctx, buf, "%g %g l\n", x, y);
160 	fz_append_printf(ctx, buf, "%g %g l\n", b.x, b.y);
161 }
162 
include_cap(fz_rect * rect,float x,float y,float r)163 static void include_cap(fz_rect *rect, float x, float y, float r)
164 {
165 	rect->x0 = fz_min(rect->x0, x-r);
166 	rect->y0 = fz_min(rect->y0, y-r);
167 	rect->x1 = fz_max(rect->x1, x+r);
168 	rect->y1 = fz_max(rect->y1, y+r);
169 }
170 
171 static void
pdf_write_line_cap_appearance(fz_context * ctx,fz_buffer * buf,fz_rect * rect,float x,float y,float dx,float dy,float w,int sc,int ic,pdf_obj * cap)172 pdf_write_line_cap_appearance(fz_context *ctx, fz_buffer *buf, fz_rect *rect,
173 		float x, float y, float dx, float dy, float w,
174 		int sc, int ic, pdf_obj *cap)
175 {
176 	if (cap == PDF_NAME(Square))
177 	{
178 		float r = fz_max(2.5f, w * 2.5f);
179 		fz_append_printf(ctx, buf, "%g %g %g %g re\n", x-r, y-r, r*2, r*2);
180 		maybe_stroke_and_fill(ctx, buf, sc, ic);
181 		include_cap(rect, x, y, r);
182 	}
183 	else if (cap == PDF_NAME(Circle))
184 	{
185 		float r = fz_max(2.5f, w * 2.5f);
186 		float m = r * CIRCLE_MAGIC;
187 		fz_append_printf(ctx, buf, "%g %g m\n", x, y+r);
188 		fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n", x+m, y+r, x+r, y+m, x+r, y);
189 		fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n", x+r, y-m, x+m, y-r, x, y-r);
190 		fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n", x-m, y-r, x-r, y-m, x-r, y);
191 		fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n", x-r, y+m, x-m, y+r, x, y+r);
192 		maybe_stroke_and_fill(ctx, buf, sc, ic);
193 		include_cap(rect, x, y, r);
194 	}
195 	else if (cap == PDF_NAME(Diamond))
196 	{
197 		float r = fz_max(2.5f, w * 2.5f);
198 		fz_append_printf(ctx, buf, "%g %g m\n", x, y+r);
199 		fz_append_printf(ctx, buf, "%g %g l\n", x+r, y);
200 		fz_append_printf(ctx, buf, "%g %g l\n", x, y-r);
201 		fz_append_printf(ctx, buf, "%g %g l\n", x-r, y);
202 		maybe_stroke_and_fill(ctx, buf, sc, ic);
203 		include_cap(rect, x, y, r);
204 	}
205 	else if (cap == PDF_NAME(OpenArrow))
206 	{
207 		pdf_write_arrow_appearance(ctx, buf, rect, x, y, dx, dy, w);
208 		maybe_stroke(ctx, buf, sc);
209 	}
210 	else if (cap == PDF_NAME(ClosedArrow))
211 	{
212 		pdf_write_arrow_appearance(ctx, buf, rect, x, y, dx, dy, w);
213 		maybe_stroke_and_fill(ctx, buf, sc, ic);
214 	}
215 	/* PDF 1.5 */
216 	else if (cap == PDF_NAME(Butt))
217 	{
218 		float r = fz_max(3, w * 3);
219 		fz_point a = { x-dy*r, y+dx*r };
220 		fz_point b = { x+dy*r, y-dx*r };
221 		fz_append_printf(ctx, buf, "%g %g m\n", a.x, a.y);
222 		fz_append_printf(ctx, buf, "%g %g l\n", b.x, b.y);
223 		maybe_stroke(ctx, buf, sc);
224 		*rect = fz_include_point_in_rect(*rect, a);
225 		*rect = fz_include_point_in_rect(*rect, b);
226 	}
227 	else if (cap == PDF_NAME(ROpenArrow))
228 	{
229 		pdf_write_arrow_appearance(ctx, buf, rect, x, y, -dx, -dy, w);
230 		maybe_stroke(ctx, buf, sc);
231 	}
232 	else if (cap == PDF_NAME(RClosedArrow))
233 	{
234 		pdf_write_arrow_appearance(ctx, buf, rect, x, y, -dx, -dy, w);
235 		maybe_stroke_and_fill(ctx, buf, sc, ic);
236 	}
237 	/* PDF 1.6 */
238 	else if (cap == PDF_NAME(Slash))
239 	{
240 		float r = fz_max(5, w * 5);
241 		float angle = atan2f(dy, dx) - (30 * FZ_PI / 180);
242 		fz_point a, b, v;
243 		v = rotate_vector(angle, 0, r);
244 		a = fz_make_point(x + v.x, y + v.y);
245 		v = rotate_vector(angle, 0, -r);
246 		b = fz_make_point(x + v.x, y + v.y);
247 		fz_append_printf(ctx, buf, "%g %g m\n", a.x, a.y);
248 		fz_append_printf(ctx, buf, "%g %g l\n", b.x, b.y);
249 		maybe_stroke(ctx, buf, sc);
250 		*rect = fz_include_point_in_rect(*rect, a);
251 		*rect = fz_include_point_in_rect(*rect, b);
252 	}
253 }
254 
255 static void
pdf_write_line_appearance(fz_context * ctx,pdf_annot * annot,fz_buffer * buf,fz_rect * rect)256 pdf_write_line_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect)
257 {
258 	pdf_obj *line, *le;
259 	fz_point a, b;
260 	float w;
261 	int sc;
262 	int ic;
263 
264 	w = pdf_write_border_appearance(ctx, annot, buf);
265 	sc = pdf_write_stroke_color_appearance(ctx, annot, buf);
266 	ic = pdf_write_interior_fill_color_appearance(ctx, annot, buf);
267 
268 	line = pdf_dict_get(ctx, annot->obj, PDF_NAME(L));
269 	a.x = pdf_array_get_real(ctx, line, 0);
270 	a.y = pdf_array_get_real(ctx, line, 1);
271 	b.x = pdf_array_get_real(ctx, line, 2);
272 	b.y = pdf_array_get_real(ctx, line, 3);
273 
274 	fz_append_printf(ctx, buf, "%g %g m\n%g %g l\n", a.x, a.y, b.x, b.y);
275 	maybe_stroke(ctx, buf, sc);
276 
277 	rect->x0 = fz_min(a.x, b.x);
278 	rect->y0 = fz_min(a.y, b.y);
279 	rect->x1 = fz_max(a.x, b.x);
280 	rect->y1 = fz_max(a.y, b.y);
281 
282 	le = pdf_dict_get(ctx, annot->obj, PDF_NAME(LE));
283 	if (pdf_array_len(ctx, le) == 2)
284 	{
285 		float dx = b.x - a.x;
286 		float dy = b.y - a.y;
287 		float l = sqrtf(dx*dx + dy*dy);
288 		pdf_write_line_cap_appearance(ctx, buf, rect, a.x, a.y, dx/l, dy/l, w, sc, ic, pdf_array_get(ctx, le, 0));
289 		pdf_write_line_cap_appearance(ctx, buf, rect, b.x, b.y, -dx/l, -dy/l, w, sc, ic, pdf_array_get(ctx, le, 1));
290 	}
291 	*rect = fz_expand_rect(*rect, fz_max(1, w));
292 }
293 
294 static void
pdf_write_square_appearance(fz_context * ctx,pdf_annot * annot,fz_buffer * buf,fz_rect * rect)295 pdf_write_square_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect)
296 {
297 	float x, y, w, h;
298 	float lw;
299 	int sc;
300 	int ic;
301 
302 	lw = pdf_write_border_appearance(ctx, annot, buf);
303 	sc = pdf_write_stroke_color_appearance(ctx, annot, buf);
304 	ic = pdf_write_interior_fill_color_appearance(ctx, annot, buf);
305 
306 	x = rect->x0 + lw;
307 	y = rect->y0 + lw;
308 	w = rect->x1 - x - lw;
309 	h = rect->y1 - y - lw;
310 
311 	fz_append_printf(ctx, buf, "%g %g %g %g re\n", x, y, w, h);
312 	maybe_stroke_and_fill(ctx, buf, sc, ic);
313 }
314 
315 static void
draw_circle(fz_context * ctx,fz_buffer * buf,float rx,float ry,float cx,float cy)316 draw_circle(fz_context *ctx, fz_buffer *buf, float rx, float ry, float cx, float cy)
317 {
318 	float mx = rx * CIRCLE_MAGIC;
319 	float my = ry * CIRCLE_MAGIC;
320 	fz_append_printf(ctx, buf, "%g %g m\n", cx, cy+ry);
321 	fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n", cx+mx, cy+ry, cx+rx, cy+my, cx+rx, cy);
322 	fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n", cx+rx, cy-my, cx+mx, cy-ry, cx, cy-ry);
323 	fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n", cx-mx, cy-ry, cx-rx, cy-my, cx-rx, cy);
324 	fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n", cx-rx, cy+my, cx-mx, cy+ry, cx, cy+ry);
325 }
326 
327 static void
draw_circle_in_box(fz_context * ctx,fz_buffer * buf,float lw,float x0,float y0,float x1,float y1)328 draw_circle_in_box(fz_context *ctx, fz_buffer *buf, float lw, float x0, float y0, float x1, float y1)
329 {
330 	float rx = (x1 - x0) / 2 - lw;
331 	float ry = (y1 - y0) / 2 - lw;
332 	float cx = x0 + lw + rx;
333 	float cy = y0 + lw + ry;
334 	draw_circle(ctx, buf, rx, ry, cx, cy);
335 }
336 
337 static void
pdf_write_circle_appearance(fz_context * ctx,pdf_annot * annot,fz_buffer * buf,fz_rect * rect)338 pdf_write_circle_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect)
339 {
340 	float lw;
341 	int sc;
342 	int ic;
343 
344 	lw = pdf_write_border_appearance(ctx, annot, buf);
345 	sc = pdf_write_stroke_color_appearance(ctx, annot, buf);
346 	ic = pdf_write_interior_fill_color_appearance(ctx, annot, buf);
347 
348 	draw_circle_in_box(ctx, buf, lw, rect->x0, rect->y0, rect->x1, rect->y1);
349 	maybe_stroke_and_fill(ctx, buf, sc, ic);
350 }
351 
352 static void
pdf_write_polygon_appearance(fz_context * ctx,pdf_annot * annot,fz_buffer * buf,fz_rect * rect,int close)353 pdf_write_polygon_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, int close)
354 {
355 	pdf_obj *verts;
356 	fz_point p;
357 	int i, n;
358 	float lw;
359 	int sc;
360 
361 	lw = pdf_write_border_appearance(ctx, annot, buf);
362 	sc = pdf_write_stroke_color_appearance(ctx, annot, buf);
363 
364 	*rect = fz_empty_rect;
365 
366 	verts = pdf_dict_get(ctx, annot->obj, PDF_NAME(Vertices));
367 	n = pdf_array_len(ctx, verts) / 2;
368 	if (n > 0)
369 	{
370 		for (i = 0; i < n; ++i)
371 		{
372 			p.x = pdf_array_get_real(ctx, verts, i*2+0);
373 			p.y = pdf_array_get_real(ctx, verts, i*2+1);
374 			if (i == 0)
375 			{
376 				rect->x0 = rect->x1 = p.x;
377 				rect->y0 = rect->y1 = p.y;
378 			}
379 			else
380 				*rect = fz_include_point_in_rect(*rect, p);
381 			if (i == 0)
382 				fz_append_printf(ctx, buf, "%g %g m\n", p.x, p.y);
383 			else
384 				fz_append_printf(ctx, buf, "%g %g l\n", p.x, p.y);
385 		}
386 		if (close)
387 			fz_append_string(ctx, buf, "h\n");
388 		maybe_stroke(ctx, buf, sc);
389 		*rect = fz_expand_rect(*rect, lw);
390 	}
391 }
392 
393 static void
pdf_write_ink_appearance(fz_context * ctx,pdf_annot * annot,fz_buffer * buf,fz_rect * rect)394 pdf_write_ink_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect)
395 {
396 	pdf_obj *ink_list, *stroke;
397 	int i, n, k, m;
398 	float lw;
399 	fz_point p;
400 	int sc;
401 
402 	lw = pdf_write_border_appearance(ctx, annot, buf);
403 	sc = pdf_write_stroke_color_appearance(ctx, annot, buf);
404 
405 	*rect = fz_empty_rect;
406 
407 	fz_append_printf(ctx, buf, "1 J\n1 j\n");
408 
409 	ink_list = pdf_dict_get(ctx, annot->obj, PDF_NAME(InkList));
410 	n = pdf_array_len(ctx, ink_list);
411 	for (i = 0; i < n; ++i)
412 	{
413 		stroke = pdf_array_get(ctx, ink_list, i);
414 		m = pdf_array_len(ctx, stroke) / 2;
415 		for (k = 0; k < m; ++k)
416 		{
417 			p.x = pdf_array_get_real(ctx, stroke, k*2+0);
418 			p.y = pdf_array_get_real(ctx, stroke, k*2+1);
419 			if (i == 0 && k == 0)
420 			{
421 				rect->x0 = rect->x1 = p.x;
422 				rect->y0 = rect->y1 = p.y;
423 			}
424 			else
425 				*rect = fz_include_point_in_rect(*rect, p);
426 			fz_append_printf(ctx, buf, "%g %g %c\n", p.x, p.y, k == 0 ? 'm' : 'l');
427 		}
428 
429 		if (m == 1)
430 			fz_append_printf(ctx, buf, "%g %g %c\n", p.x, p.y, 'l');
431 	}
432 	maybe_stroke(ctx, buf, sc);
433 	*rect = fz_expand_rect(*rect, lw);
434 }
435 
436 /* Contrary to the specification, the points within a QuadPoint are NOT
437  * ordered in a counter-clockwise fashion starting with the lower left.
438  * Experiments with Adobe's implementation indicates a cross-wise
439  * ordering is intended: ul, ur, ll, lr.
440  */
441 enum { UL, UR, LL, LR };
442 
443 static float
extract_quad(fz_context * ctx,fz_point * quad,pdf_obj * obj,int i)444 extract_quad(fz_context *ctx, fz_point *quad, pdf_obj *obj, int i)
445 {
446 	float dx, dy;
447 	quad[0].x = pdf_array_get_real(ctx, obj, i+0);
448 	quad[0].y = pdf_array_get_real(ctx, obj, i+1);
449 	quad[1].x = pdf_array_get_real(ctx, obj, i+2);
450 	quad[1].y = pdf_array_get_real(ctx, obj, i+3);
451 	quad[2].x = pdf_array_get_real(ctx, obj, i+4);
452 	quad[2].y = pdf_array_get_real(ctx, obj, i+5);
453 	quad[3].x = pdf_array_get_real(ctx, obj, i+6);
454 	quad[3].y = pdf_array_get_real(ctx, obj, i+7);
455 	dx = quad[UL].x - quad[LL].x;
456 	dy = quad[UL].y - quad[LL].y;
457 	return sqrtf(dx * dx + dy * dy);
458 }
459 
460 static void
union_quad(fz_rect * rect,const fz_point quad[4],float lw)461 union_quad(fz_rect *rect, const fz_point quad[4], float lw)
462 {
463 	fz_rect qbox;
464 	qbox.x0 = fz_min(fz_min(quad[0].x, quad[1].x), fz_min(quad[2].x, quad[3].x));
465 	qbox.y0 = fz_min(fz_min(quad[0].y, quad[1].y), fz_min(quad[2].y, quad[3].y));
466 	qbox.x1 = fz_max(fz_max(quad[0].x, quad[1].x), fz_max(quad[2].x, quad[3].x));
467 	qbox.y1 = fz_max(fz_max(quad[0].y, quad[1].y), fz_max(quad[2].y, quad[3].y));
468 	*rect = fz_union_rect(*rect, fz_expand_rect(qbox, lw));
469 }
470 
471 static fz_point
lerp_point(fz_point a,fz_point b,float t)472 lerp_point(fz_point a, fz_point b, float t)
473 {
474 	return fz_make_point(a.x + t * (b.x - a.x), a.y + t * (b.y - a.y));
475 }
476 
477 static void
pdf_write_highlight_appearance(fz_context * ctx,pdf_annot * annot,fz_buffer * buf,fz_rect * rect,pdf_obj ** res)478 pdf_write_highlight_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, pdf_obj **res)
479 {
480 	pdf_obj *res_egs, *res_egs_h;
481 	pdf_obj *qp;
482 	fz_point quad[4], mquad[4], v;
483 	float opacity, h, m, dx, dy, vn;
484 	int i, n;
485 
486 	*rect = fz_empty_rect;
487 
488 	/* /Resources << /ExtGState << /H << /Type/ExtGState /BM/Multiply /CA %g >> >> >> */
489 	*res = pdf_new_dict(ctx, annot->page->doc, 1);
490 	res_egs = pdf_dict_put_dict(ctx, *res, PDF_NAME(ExtGState), 1);
491 	res_egs_h = pdf_dict_put_dict(ctx, res_egs, PDF_NAME(H), 2);
492 	pdf_dict_put(ctx, res_egs_h, PDF_NAME(Type), PDF_NAME(ExtGState));
493 	pdf_dict_put(ctx, res_egs_h, PDF_NAME(BM), PDF_NAME(Multiply));
494 	opacity = pdf_annot_opacity(ctx, annot);
495 	if (opacity < 1)
496 		pdf_dict_put_real(ctx, res_egs_h, PDF_NAME(ca), opacity);
497 
498 	pdf_write_fill_color_appearance(ctx, annot, buf);
499 
500 	fz_append_printf(ctx, buf, "/H gs\n");
501 
502 	qp = pdf_dict_get(ctx, annot->obj, PDF_NAME(QuadPoints));
503 	n = pdf_array_len(ctx, qp);
504 	if (n > 0)
505 	{
506 		for (i = 0; i < n; i += 8)
507 		{
508 			h = extract_quad(ctx, quad, qp, i);
509 			m = h / 4.2425f; /* magic number that matches adobe's appearance */
510 			dx = quad[LR].x - quad[LL].x;
511 			dy = quad[LR].y - quad[LL].y;
512 			vn = sqrtf(dx * dx + dy * dy);
513 			v = fz_make_point(dx * m / vn, dy * m / vn);
514 
515 			mquad[LL].x = quad[LL].x - v.x - v.y;
516 			mquad[LL].y = quad[LL].y - v.y + v.x;
517 			mquad[UL].x = quad[UL].x - v.x + v.y;
518 			mquad[UL].y = quad[UL].y - v.y - v.x;
519 			mquad[LR].x = quad[LR].x + v.x - v.y;
520 			mquad[LR].y = quad[LR].y + v.y + v.x;
521 			mquad[UR].x = quad[UR].x + v.x + v.y;
522 			mquad[UR].y = quad[UR].y + v.y - v.x;
523 
524 			fz_append_printf(ctx, buf, "%g %g m\n", quad[LL].x, quad[LL].y);
525 			fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n",
526 				mquad[LL].x, mquad[LL].y,
527 				mquad[UL].x, mquad[UL].y,
528 				quad[UL].x, quad[UL].y);
529 			fz_append_printf(ctx, buf, "%g %g l\n", quad[UR].x, quad[UR].y);
530 			fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n",
531 				mquad[UR].x, mquad[UR].y,
532 				mquad[LR].x, mquad[LR].y,
533 				quad[LR].x, quad[LR].y);
534 			fz_append_printf(ctx, buf, "f\n");
535 
536 			union_quad(rect, quad, h/16);
537 			union_quad(rect, mquad, 0);
538 		}
539 	}
540 }
541 
542 static void
pdf_write_underline_appearance(fz_context * ctx,pdf_annot * annot,fz_buffer * buf,fz_rect * rect)543 pdf_write_underline_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect)
544 {
545 	fz_point quad[4], a, b;
546 	float h;
547 	pdf_obj *qp;
548 	int i, n;
549 
550 	*rect = fz_empty_rect;
551 
552 	pdf_write_stroke_color_appearance(ctx, annot, buf);
553 
554 	qp = pdf_dict_get(ctx, annot->obj, PDF_NAME(QuadPoints));
555 	n = pdf_array_len(ctx, qp);
556 	if (n > 0)
557 	{
558 		for (i = 0; i < n; i += 8)
559 		{
560 			/* Acrobat draws the line at 1/7 of the box width from the bottom
561 			 * of the box and 1/16 thick of the box width. */
562 
563 			h = extract_quad(ctx, quad, qp, i);
564 			a = lerp_point(quad[LL], quad[UL], 1/7.0f);
565 			b = lerp_point(quad[LR], quad[UR], 1/7.0f);
566 
567 			fz_append_printf(ctx, buf, "%g w\n", h/16);
568 			fz_append_printf(ctx, buf, "%g %g m\n", a.x, a.y);
569 			fz_append_printf(ctx, buf, "%g %g l\n", b.x, b.y);
570 			fz_append_printf(ctx, buf, "S\n");
571 
572 			union_quad(rect, quad, h/16);
573 		}
574 	}
575 }
576 
577 static void
pdf_write_strike_out_appearance(fz_context * ctx,pdf_annot * annot,fz_buffer * buf,fz_rect * rect)578 pdf_write_strike_out_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect)
579 {
580 	fz_point quad[4], a, b;
581 	float h;
582 	pdf_obj *qp;
583 	int i, n;
584 
585 	pdf_write_stroke_color_appearance(ctx, annot, buf);
586 
587 	qp = pdf_dict_get(ctx, annot->obj, PDF_NAME(QuadPoints));
588 	n = pdf_array_len(ctx, qp);
589 	if (n > 0)
590 	{
591 		*rect = fz_empty_rect;
592 		for (i = 0; i < n; i += 8)
593 		{
594 			/* Acrobat draws the line at 3/7 of the box width from the bottom
595 			 * of the box and 1/16 thick of the box width. */
596 
597 			h = extract_quad(ctx, quad, qp, i);
598 			a = lerp_point(quad[LL], quad[UL], 3/7.0f);
599 			b = lerp_point(quad[LR], quad[UR], 3/7.0f);
600 
601 			fz_append_printf(ctx, buf, "%g w\n", h/16);
602 			fz_append_printf(ctx, buf, "%g %g m\n", a.x, a.y);
603 			fz_append_printf(ctx, buf, "%g %g l\n", b.x, b.y);
604 			fz_append_printf(ctx, buf, "S\n");
605 
606 			union_quad(rect, quad, h/16);
607 		}
608 	}
609 }
610 
611 static void
pdf_write_squiggly_appearance(fz_context * ctx,pdf_annot * annot,fz_buffer * buf,fz_rect * rect)612 pdf_write_squiggly_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect)
613 {
614 	fz_point quad[4], a, b, c, v;
615 	float h, x, w;
616 	pdf_obj *qp;
617 	int i, n;
618 
619 	*rect = fz_empty_rect;
620 
621 	pdf_write_stroke_color_appearance(ctx, annot, buf);
622 
623 	qp = pdf_dict_get(ctx, annot->obj, PDF_NAME(QuadPoints));
624 	n = pdf_array_len(ctx, qp);
625 	if (n > 0)
626 	{
627 		for (i = 0; i < n; i += 8)
628 		{
629 			int up = 1;
630 			h = extract_quad(ctx, quad, qp, i);
631 			v = fz_make_point(quad[LR].x - quad[LL].x, quad[LR].y - quad[LL].y);
632 			w = sqrtf(v.x * v.x + v.y * v.y);
633 			x = 0;
634 
635 			fz_append_printf(ctx, buf, "%g w\n", h/16);
636 			fz_append_printf(ctx, buf, "%g %g m\n", quad[LL].x, quad[LL].y);
637 			while (x < w)
638 			{
639 				x += h/7;
640 				a = lerp_point(quad[LL], quad[LR], x/w);
641 				if (up)
642 				{
643 					b = lerp_point(quad[UL], quad[UR], x/w);
644 					c = lerp_point(a, b, 1/7.0f);
645 					fz_append_printf(ctx, buf, "%g %g l\n", c.x, c.y);
646 				}
647 				else
648 					fz_append_printf(ctx, buf, "%g %g l\n", a.x, a.y);
649 				up = !up;
650 			}
651 			fz_append_printf(ctx, buf, "S\n");
652 
653 			union_quad(rect, quad, h/16);
654 		}
655 	}
656 }
657 
658 static void
pdf_write_redact_appearance(fz_context * ctx,pdf_annot * annot,fz_buffer * buf,fz_rect * rect)659 pdf_write_redact_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect)
660 {
661 	fz_point quad[4];
662 	pdf_obj *qp;
663 	int i, n;
664 
665 	fz_append_printf(ctx, buf, "1 0 0 RG\n");
666 
667 	qp = pdf_dict_get(ctx, annot->obj, PDF_NAME(QuadPoints));
668 	n = pdf_array_len(ctx, qp);
669 	if (n > 0)
670 	{
671 		*rect = fz_empty_rect;
672 		for (i = 0; i < n; i += 8)
673 		{
674 			extract_quad(ctx, quad, qp, i);
675 			fz_append_printf(ctx, buf, "%g %g m\n", quad[LL].x, quad[LL].y);
676 			fz_append_printf(ctx, buf, "%g %g l\n", quad[LR].x, quad[LR].y);
677 			fz_append_printf(ctx, buf, "%g %g l\n", quad[UR].x, quad[UR].y);
678 			fz_append_printf(ctx, buf, "%g %g l\n", quad[UL].x, quad[UL].y);
679 			fz_append_printf(ctx, buf, "s\n");
680 			union_quad(rect, quad, 1);
681 		}
682 	}
683 	else
684 	{
685 		fz_append_printf(ctx, buf, "%g %g m\n", rect->x0+1, rect->y0+1);
686 		fz_append_printf(ctx, buf, "%g %g l\n", rect->x1-1, rect->y0+1);
687 		fz_append_printf(ctx, buf, "%g %g l\n", rect->x1-1, rect->y1-1);
688 		fz_append_printf(ctx, buf, "%g %g l\n", rect->x0+1, rect->y1-1);
689 		fz_append_printf(ctx, buf, "s\n");
690 	}
691 }
692 
693 static void
pdf_write_caret_appearance(fz_context * ctx,pdf_annot * annot,fz_buffer * buf,fz_rect * rect,fz_rect * bbox)694 pdf_write_caret_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, fz_rect *bbox)
695 {
696 	float xc = (rect->x0 + rect->x1) / 2;
697 	float yc = (rect->y0 + rect->y1) / 2;
698 
699 	pdf_write_fill_color_appearance(ctx, annot, buf);
700 
701 	fz_append_string(ctx, buf, "0 0 m\n");
702 	fz_append_string(ctx, buf, "10 0 10 7 10 14 c\n");
703 	fz_append_string(ctx, buf, "10 7 10 0 20 0 c\n");
704 	fz_append_string(ctx, buf, "f\n");
705 
706 	*rect = fz_make_rect(xc - 10, yc - 7, xc + 10, yc + 7);
707 	*bbox = fz_make_rect(0, 0, 20, 14);
708 }
709 
710 static void
pdf_write_icon_appearance(fz_context * ctx,pdf_annot * annot,fz_buffer * buf,fz_rect * rect,fz_rect * bbox)711 pdf_write_icon_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, fz_rect *bbox)
712 {
713 	const char *name;
714 	float xc = (rect->x0 + rect->x1) / 2;
715 	float yc = (rect->y0 + rect->y1) / 2;
716 
717 	if (!pdf_write_fill_color_appearance(ctx, annot, buf))
718 		fz_append_string(ctx, buf, "1 g\n");
719 
720 	fz_append_string(ctx, buf, "1 w\n0.5 0.5 15 15 re\nb\n");
721 	fz_append_string(ctx, buf, "1 0 0 -1 4 12 cm\n");
722 
723 	if (pdf_is_dark_fill_color(ctx, annot))
724 		fz_append_string(ctx, buf, "1 g\n");
725 	else
726 		fz_append_string(ctx, buf, "0 g\n");
727 
728 	name = pdf_annot_icon_name(ctx, annot);
729 
730 	/* Text names */
731 	if (!strcmp(name, "Comment"))
732 		fz_append_string(ctx, buf, icon_comment);
733 	else if (!strcmp(name, "Key"))
734 		fz_append_string(ctx, buf, icon_key);
735 	else if (!strcmp(name, "Note"))
736 		fz_append_string(ctx, buf, icon_note);
737 	else if (!strcmp(name, "Help"))
738 		fz_append_string(ctx, buf, icon_help);
739 	else if (!strcmp(name, "NewParagraph"))
740 		fz_append_string(ctx, buf, icon_new_paragraph);
741 	else if (!strcmp(name, "Paragraph"))
742 		fz_append_string(ctx, buf, icon_paragraph);
743 	else if (!strcmp(name, "Insert"))
744 		fz_append_string(ctx, buf, icon_insert);
745 
746 	/* FileAttachment names */
747 	else if (!strcmp(name, "Graph"))
748 		fz_append_string(ctx, buf, icon_graph);
749 	else if (!strcmp(name, "PushPin"))
750 		fz_append_string(ctx, buf, icon_push_pin);
751 	else if (!strcmp(name, "Paperclip"))
752 		fz_append_string(ctx, buf, icon_paperclip);
753 	else if (!strcmp(name, "Tag"))
754 		fz_append_string(ctx, buf, icon_tag);
755 
756 	/* Sound names */
757 	else if (!strcmp(name, "Speaker"))
758 		fz_append_string(ctx, buf, icon_speaker);
759 	else if (!strcmp(name, "Mic"))
760 		fz_append_string(ctx, buf, icon_mic);
761 
762 	/* Unknown */
763 	else
764 		fz_append_string(ctx, buf, icon_star);
765 
766 	*rect = fz_make_rect(xc - 9, yc - 9, xc + 9, yc + 9);
767 	*bbox = fz_make_rect(0, 0, 16, 16);
768 }
769 
770 static float
measure_stamp_string(fz_context * ctx,fz_font * font,const char * text)771 measure_stamp_string(fz_context *ctx, fz_font *font, const char *text)
772 {
773 	float w = 0;
774 	while (*text)
775 	{
776 		int c, g;
777 		text += fz_chartorune(&c, text);
778 		if (fz_windows_1252_from_unicode(c) < 0)
779 			c = REPLACEMENT;
780 		g = fz_encode_character(ctx, font, c);
781 		w += fz_advance_glyph(ctx, font, g, 0);
782 	}
783 	return w;
784 }
785 
786 static void
write_stamp_string(fz_context * ctx,fz_buffer * buf,fz_font * font,const char * text)787 write_stamp_string(fz_context *ctx, fz_buffer *buf, fz_font *font, const char *text)
788 {
789 	fz_append_byte(ctx, buf, '(');
790 	while (*text)
791 	{
792 		int c;
793 		text += fz_chartorune(&c, text);
794 		c = fz_windows_1252_from_unicode(c);
795 		if (c < 0) c = REPLACEMENT;
796 		if (c == '(' || c == ')' || c == '\\')
797 			fz_append_byte(ctx, buf, '\\');
798 		fz_append_byte(ctx, buf, c);
799 	}
800 	fz_append_byte(ctx, buf, ')');
801 }
802 
803 static void
write_stamp(fz_context * ctx,fz_buffer * buf,fz_font * font,const char * text,float y,float h)804 write_stamp(fz_context *ctx, fz_buffer *buf, fz_font *font, const char *text, float y, float h)
805 {
806 	float tw = measure_stamp_string(ctx, font, text) * h;
807 	fz_append_string(ctx, buf, "BT\n");
808 	fz_append_printf(ctx, buf, "/Times %g Tf\n", h);
809 	fz_append_printf(ctx, buf, "%g %g Td\n", (190-tw)/2, y);
810 	write_stamp_string(ctx, buf, font, text);
811 	fz_append_string(ctx, buf, " Tj\n");
812 	fz_append_string(ctx, buf, "ET\n");
813 }
814 
815 static void
pdf_write_stamp_appearance(fz_context * ctx,pdf_annot * annot,fz_buffer * buf,fz_rect * rect,fz_rect * bbox,pdf_obj ** res)816 pdf_write_stamp_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, fz_rect *bbox, pdf_obj **res)
817 {
818 	fz_font *font;
819 	pdf_obj *res_font;
820 	pdf_obj *name;
821 	float w, h, xs, ys;
822 	fz_matrix rotate;
823 
824 	name = pdf_dict_get(ctx, annot->obj, PDF_NAME(Name));
825 	if (!name)
826 		name = PDF_NAME(Draft);
827 
828 	h = rect->y1 - rect->y0;
829 	w = rect->x1 - rect->x0;
830 	xs = w / 190;
831 	ys = h / 50;
832 
833 	font = fz_new_base14_font(ctx, "Times-Bold");
834 	fz_try(ctx)
835 	{
836 		/* /Resources << /Font << /Times %d 0 R >> >> */
837 		*res = pdf_new_dict(ctx, annot->page->doc, 1);
838 		res_font = pdf_dict_put_dict(ctx, *res, PDF_NAME(Font), 1);
839 		pdf_dict_put_drop(ctx, res_font, PDF_NAME(Times), pdf_add_simple_font(ctx, annot->page->doc, font, 0));
840 
841 		pdf_write_fill_color_appearance(ctx, annot, buf);
842 		pdf_write_stroke_color_appearance(ctx, annot, buf);
843 		rotate = fz_rotate(0.6f);
844 		fz_append_printf(ctx, buf, "%M cm\n", &rotate);
845 		fz_append_string(ctx, buf, "2 w\n2 2 186 44 re\nS\n");
846 
847 		if (name == PDF_NAME(Approved))
848 			write_stamp(ctx, buf, font, "APPROVED", 13, 30);
849 		else if (name == PDF_NAME(AsIs))
850 			write_stamp(ctx, buf, font, "AS IS", 13, 30);
851 		else if (name == PDF_NAME(Confidential))
852 			write_stamp(ctx, buf, font, "CONFIDENTIAL", 17, 20);
853 		else if (name == PDF_NAME(Departmental))
854 			write_stamp(ctx, buf, font, "DEPARTMENTAL", 17, 20);
855 		else if (name == PDF_NAME(Experimental))
856 			write_stamp(ctx, buf, font, "EXPERIMENTAL", 17, 20);
857 		else if (name == PDF_NAME(Expired))
858 			write_stamp(ctx, buf, font, "EXPIRED", 13, 30);
859 		else if (name == PDF_NAME(Final))
860 			write_stamp(ctx, buf, font, "FINAL", 13, 30);
861 		else if (name == PDF_NAME(ForComment))
862 			write_stamp(ctx, buf, font, "FOR COMMENT", 17, 20);
863 		else if (name == PDF_NAME(ForPublicRelease))
864 		{
865 			write_stamp(ctx, buf, font, "FOR PUBLIC", 26, 18);
866 			write_stamp(ctx, buf, font, "RELEASE", 8.5f, 18);
867 		}
868 		else if (name == PDF_NAME(NotApproved))
869 			write_stamp(ctx, buf, font, "NOT APPROVED", 17, 20);
870 		else if (name == PDF_NAME(NotForPublicRelease))
871 		{
872 			write_stamp(ctx, buf, font, "NOT FOR", 26, 18);
873 			write_stamp(ctx, buf, font, "PUBLIC RELEASE", 8.5, 18);
874 		}
875 		else if (name == PDF_NAME(Sold))
876 			write_stamp(ctx, buf, font, "SOLD", 13, 30);
877 		else if (name == PDF_NAME(TopSecret))
878 			write_stamp(ctx, buf, font, "TOP SECRET", 14, 26);
879 		else if (name == PDF_NAME(Draft))
880 			write_stamp(ctx, buf, font, "DRAFT", 13, 30);
881 		else
882 			write_stamp(ctx, buf, font, pdf_to_name(ctx, name), 17, 20);
883 	}
884 	fz_always(ctx)
885 		fz_drop_font(ctx, font);
886 	fz_catch(ctx)
887 		fz_rethrow(ctx);
888 
889 	*bbox = fz_make_rect(0, 0, 190, 50);
890 	if (xs > ys)
891 	{
892 		float xc = (rect->x1+rect->x0) / 2;
893 		rect->x0 = xc - 95 * ys;
894 		rect->x1 = xc + 95 * ys;
895 	}
896 	else
897 	{
898 		float yc = (rect->y1+rect->y0) / 2;
899 		rect->y0 = yc - 25 * xs;
900 		rect->y1 = yc + 25 * xs;
901 	}
902 }
903 
904 static void
add_required_fonts(fz_context * ctx,pdf_document * doc,pdf_obj * res_font,fz_text_language lang,fz_font * font,const char * fontname,const char * text)905 add_required_fonts(fz_context *ctx, pdf_document *doc, pdf_obj *res_font,
906 	fz_text_language lang, fz_font *font, const char *fontname, const char *text)
907 {
908 	fz_font *cjk_font;
909 	char buf[40];
910 
911 	int add_latin = 0;
912 	int add_greek = 0;
913 	int add_cyrillic = 0;
914 	int add_korean = 0;
915 	int add_japanese = 0;
916 	int add_bopomofo = 0;
917 	int add_han = 0;
918 	int add_hans = 0;
919 	int add_hant = 0;
920 
921 	while (*text)
922 	{
923 		int c;
924 		text += fz_chartorune(&c, text);
925 		switch (ucdn_get_script(c))
926 		{
927 		default: add_latin = 1; /* for fallback bullet character */ break;
928 		case UCDN_SCRIPT_COMMON: break;
929 		case UCDN_SCRIPT_INHERITED: break;
930 		case UCDN_SCRIPT_LATIN: add_latin = 1; break;
931 		case UCDN_SCRIPT_GREEK: add_greek = 1; break;
932 		case UCDN_SCRIPT_CYRILLIC: add_cyrillic = 1; break;
933 		case UCDN_SCRIPT_HANGUL: add_korean = 1; break;
934 		case UCDN_SCRIPT_HIRAGANA: add_japanese = 1; break;
935 		case UCDN_SCRIPT_KATAKANA: add_japanese = 1; break;
936 		case UCDN_SCRIPT_BOPOMOFO: add_bopomofo = 1; break;
937 		case UCDN_SCRIPT_HAN: add_han = 1; break;
938 		}
939 	}
940 
941 	if (add_han)
942 	{
943 		switch (lang)
944 		{
945 		case FZ_LANG_ko: add_korean = 1; break;
946 		default: /* fall through */
947 		case FZ_LANG_ja: add_japanese = 1; break;
948 		case FZ_LANG_zh: /* fall through */
949 		case FZ_LANG_zh_Hant: add_hant = 1; break;
950 		case FZ_LANG_zh_Hans: add_hans = 1; break;
951 		}
952 	}
953 
954 	if (add_bopomofo)
955 	{
956 		if (lang == FZ_LANG_zh_Hans)
957 			add_hans = 1;
958 		else
959 			add_hant = 1;
960 	}
961 
962 	if (!add_greek && !add_cyrillic && !add_korean && !add_japanese && !add_hant && !add_hans)
963 		add_latin = 1;
964 
965 	if (add_latin)
966 	{
967 		if (!pdf_dict_gets(ctx, res_font, fontname))
968 			pdf_dict_puts_drop(ctx, res_font, fontname,
969 				pdf_add_simple_font(ctx, doc, font, PDF_SIMPLE_ENCODING_LATIN));
970 	}
971 	if (add_greek)
972 	{
973 		fz_snprintf(buf, sizeof buf, "%sGRK", fontname);
974 		if (!pdf_dict_gets(ctx, res_font, buf))
975 			pdf_dict_puts_drop(ctx, res_font, buf,
976 				pdf_add_simple_font(ctx, doc, font, PDF_SIMPLE_ENCODING_GREEK));
977 	}
978 	if (add_cyrillic)
979 	{
980 		fz_snprintf(buf, sizeof buf, "%sCYR", fontname);
981 		if (!pdf_dict_gets(ctx, res_font, buf))
982 			pdf_dict_puts_drop(ctx, res_font, buf,
983 				pdf_add_simple_font(ctx, doc, font, PDF_SIMPLE_ENCODING_CYRILLIC));
984 	}
985 	if (add_korean && !pdf_dict_gets(ctx, res_font, "Batang"))
986 	{
987 		cjk_font = fz_new_cjk_font(ctx, FZ_ADOBE_KOREA);
988 		pdf_dict_puts_drop(ctx, res_font, "Batang",
989 			pdf_add_cjk_font(ctx, doc, font, FZ_ADOBE_KOREA, 0, 1));
990 		fz_drop_font(ctx, cjk_font);
991 	}
992 	if (add_japanese && !pdf_dict_gets(ctx, res_font, "Mincho"))
993 	{
994 		cjk_font = fz_new_cjk_font(ctx, FZ_ADOBE_JAPAN);
995 		pdf_dict_puts_drop(ctx, res_font, "Mincho",
996 			pdf_add_cjk_font(ctx, doc, font, FZ_ADOBE_JAPAN, 0, 1));
997 		fz_drop_font(ctx, cjk_font);
998 	}
999 	if (add_hant && !pdf_dict_gets(ctx, res_font, "Ming"))
1000 	{
1001 		cjk_font = fz_new_cjk_font(ctx, FZ_ADOBE_CNS);
1002 		pdf_dict_puts_drop(ctx, res_font, "Ming",
1003 			pdf_add_cjk_font(ctx, doc, font, FZ_ADOBE_CNS, 0, 1));
1004 		fz_drop_font(ctx, cjk_font);
1005 	}
1006 	if (add_hans && !pdf_dict_gets(ctx, res_font, "Song"))
1007 	{
1008 		cjk_font = fz_new_cjk_font(ctx, FZ_ADOBE_GB);
1009 		pdf_dict_puts_drop(ctx, res_font, "Song",
1010 			pdf_add_cjk_font(ctx, doc, font, FZ_ADOBE_GB, 0, 1));
1011 		fz_drop_font(ctx, cjk_font);
1012 	}
1013 }
1014 
find_initial_script(const char * text)1015 static int find_initial_script(const char *text)
1016 {
1017 	int script = UCDN_SCRIPT_COMMON;
1018 	int c;
1019 	while (*text)
1020 	{
1021 		text += fz_chartorune(&c, text);
1022 		script = ucdn_get_script(c);
1023 		if (script != UCDN_SCRIPT_COMMON && script != UCDN_SCRIPT_INHERITED)
1024 			break;
1025 	}
1026 	if (script == UCDN_SCRIPT_COMMON || script == UCDN_SCRIPT_INHERITED)
1027 		script = UCDN_SCRIPT_LATIN;
1028 	return script;
1029 }
1030 
1031 enum { ENC_LATIN = 1, ENC_GREEK, ENC_CYRILLIC, ENC_KOREAN, ENC_JAPANESE, ENC_HANT, ENC_HANS };
1032 
1033 struct text_walk_state
1034 {
1035 	const char *text, *end;
1036 	fz_font *font;
1037 	fz_text_language lang;
1038 	int enc, u, c, n, last_script;
1039 	float w;
1040 };
1041 
init_text_walk(fz_context * ctx,struct text_walk_state * state,fz_text_language lang,fz_font * font,const char * text,const char * end)1042 static void init_text_walk(fz_context *ctx, struct text_walk_state *state, fz_text_language lang, fz_font *font, const char *text, const char *end)
1043 {
1044 	state->text = text;
1045 	state->end = end ? end : text + strlen(text);
1046 	state->lang = lang;
1047 	state->font = font;
1048 	state->last_script = find_initial_script(text);
1049 	state->n = 0;
1050 }
1051 
next_text_walk(fz_context * ctx,struct text_walk_state * state)1052 static int next_text_walk(fz_context *ctx, struct text_walk_state *state)
1053 {
1054 	int script, g;
1055 
1056 	state->text += state->n;
1057 	if (state->text >= state->end)
1058 	{
1059 		state->n = 0;
1060 		return 0;
1061 	}
1062 
1063 	state->n = fz_chartorune(&state->u, state->text);
1064 	script = ucdn_get_script(state->u);
1065 	if (script == UCDN_SCRIPT_COMMON || script == UCDN_SCRIPT_INHERITED)
1066 		script = state->last_script;
1067 	state->last_script = script;
1068 
1069 	switch (script)
1070 	{
1071 	default:
1072 		state->enc = ENC_LATIN;
1073 		state->c = REPLACEMENT;
1074 		break;
1075 	case UCDN_SCRIPT_LATIN:
1076 		state->enc = ENC_LATIN;
1077 		state->c = fz_windows_1252_from_unicode(state->u);
1078 		break;
1079 	case UCDN_SCRIPT_GREEK:
1080 		state->enc = ENC_GREEK;
1081 		state->c = fz_iso8859_7_from_unicode(state->u);
1082 		break;
1083 	case UCDN_SCRIPT_CYRILLIC:
1084 		state->enc = ENC_CYRILLIC;
1085 		state->c = fz_koi8u_from_unicode(state->u);
1086 		break;
1087 	case UCDN_SCRIPT_HANGUL:
1088 		state->enc = ENC_KOREAN;
1089 		state->c = state->u;
1090 		break;
1091 	case UCDN_SCRIPT_HIRAGANA:
1092 	case UCDN_SCRIPT_KATAKANA:
1093 		state->enc = ENC_JAPANESE;
1094 		state->c = state->u;
1095 		break;
1096 	case UCDN_SCRIPT_BOPOMOFO:
1097 		state->enc = (state->lang == FZ_LANG_zh_Hans) ? ENC_HANS : ENC_HANT;
1098 		state->c = state->u;
1099 		break;
1100 	case UCDN_SCRIPT_HAN:
1101 		switch (state->lang)
1102 		{
1103 		case FZ_LANG_ko: state->enc = ENC_KOREAN; break;
1104 		default: /* fall through */
1105 		case FZ_LANG_ja: state->enc = ENC_JAPANESE; break;
1106 		case FZ_LANG_zh: /* fall through */
1107 		case FZ_LANG_zh_Hant: state->enc = ENC_HANT; break;
1108 		case FZ_LANG_zh_Hans: state->enc = ENC_HANS; break;
1109 		}
1110 		state->c = state->u;
1111 		break;
1112 	}
1113 
1114 	/* TODO: check that character is encodable with ENC_KOREAN/etc */
1115 	if (state->c < 0)
1116 	{
1117 		state->enc = ENC_LATIN;
1118 		state->c = REPLACEMENT;
1119 	}
1120 
1121 	if (state->enc >= ENC_KOREAN)
1122 	{
1123 		state->w = 1;
1124 	}
1125 	else
1126 	{
1127 		if (state->font != NULL)
1128 		{
1129 			g = fz_encode_character(ctx, state->font, state->u);
1130 			state->w = fz_advance_glyph(ctx, state->font, g, 0);
1131 		}
1132 	}
1133 
1134 	return 1;
1135 }
1136 
1137 static float
measure_string(fz_context * ctx,fz_text_language lang,fz_font * font,const char * a)1138 measure_string(fz_context *ctx, fz_text_language lang, fz_font *font, const char *a)
1139 {
1140 	struct text_walk_state state;
1141 	float w = 0;
1142 	init_text_walk(ctx, &state, lang, font, a, NULL);
1143 	while (next_text_walk(ctx, &state))
1144 		w += state.w;
1145 	return w;
1146 }
1147 
1148 
1149 static float
break_string(fz_context * ctx,fz_text_language lang,fz_font * font,float size,const char * text,const char ** endp,float maxw)1150 break_string(fz_context *ctx, fz_text_language lang, fz_font *font, float size, const char *text, const char **endp, float maxw)
1151 {
1152 	struct text_walk_state state;
1153 	const char *space = NULL;
1154 	float space_x, x = 0;
1155 	init_text_walk(ctx, &state, lang, font, text, NULL);
1156 	while (next_text_walk(ctx, &state))
1157 	{
1158 		if (state.u == '\n' || state.u == '\r')
1159 			break;
1160 		if (state.u == ' ')
1161 		{
1162 			space = state.text + state.n;
1163 			space_x = x;
1164 		}
1165 		x += state.w * size;
1166 		if (space && x > maxw)
1167 			return *endp = space, space_x;
1168 	}
1169 	return *endp = state.text + state.n, x;
1170 }
1171 
1172 static void
write_string(fz_context * ctx,fz_buffer * buf,fz_text_language lang,fz_font * font,const char * fontname,float size,const char * text,const char * end)1173 write_string(fz_context *ctx, fz_buffer *buf,
1174 	fz_text_language lang, fz_font *font, const char *fontname, float size, const char *text, const char *end)
1175 {
1176 	struct text_walk_state state;
1177 	int last_enc = 0;
1178 	init_text_walk(ctx, &state, lang, font, text, end);
1179 	while (next_text_walk(ctx, &state))
1180 	{
1181 		if (state.enc != last_enc)
1182 		{
1183 			if (last_enc)
1184 			{
1185 				if (last_enc < ENC_KOREAN)
1186 					fz_append_byte(ctx, buf, ')');
1187 				else
1188 					fz_append_byte(ctx, buf, '>');
1189 				fz_append_string(ctx, buf, " Tj\n");
1190 			}
1191 
1192 			switch (state.enc)
1193 			{
1194 			case ENC_LATIN: fz_append_printf(ctx, buf, "/%s %g Tf\n", fontname, size); break;
1195 			case ENC_GREEK: fz_append_printf(ctx, buf, "/%sGRK %g Tf\n", fontname, size); break;
1196 			case ENC_CYRILLIC: fz_append_printf(ctx, buf, "/%sCYR %g Tf\n", fontname, size); break;
1197 			case ENC_KOREAN: fz_append_printf(ctx, buf, "/Batang %g Tf\n", size); break;
1198 			case ENC_JAPANESE: fz_append_printf(ctx, buf, "/Mincho %g Tf\n", size); break;
1199 			case ENC_HANT: fz_append_printf(ctx, buf, "/Ming %g Tf\n", size); break;
1200 			case ENC_HANS: fz_append_printf(ctx, buf, "/Song %g Tf\n", size); break;
1201 			}
1202 
1203 			if (state.enc < ENC_KOREAN)
1204 				fz_append_byte(ctx, buf, '(');
1205 			else
1206 				fz_append_byte(ctx, buf, '<');
1207 
1208 			last_enc = state.enc;
1209 		}
1210 
1211 		if (state.enc < ENC_KOREAN)
1212 		{
1213 			if (state.c == '(' || state.c == ')' || state.c == '\\')
1214 				fz_append_byte(ctx, buf, '\\');
1215 			fz_append_byte(ctx, buf, state.c);
1216 		}
1217 		else
1218 		{
1219 			fz_append_printf(ctx, buf, "%04x", state.c);
1220 		}
1221 	}
1222 
1223 	if (last_enc)
1224 	{
1225 		if (last_enc < ENC_KOREAN)
1226 			fz_append_byte(ctx, buf, ')');
1227 		else
1228 			fz_append_byte(ctx, buf, '>');
1229 		fz_append_string(ctx, buf, " Tj\n");
1230 	}
1231 }
1232 
1233 static void
write_string_with_quadding(fz_context * ctx,fz_buffer * buf,fz_text_language lang,const char * fontname,fz_font * font,float size,float lineheight,const char * a,float maxw,int q)1234 write_string_with_quadding(fz_context *ctx, fz_buffer *buf,
1235 	fz_text_language lang, const char *fontname,
1236 	fz_font *font, float size, float lineheight,
1237 	const char *a, float maxw, int q)
1238 {
1239 	const char *b;
1240 	float px = 0, x = 0, w;
1241 	while (*a)
1242 	{
1243 		w = break_string(ctx, lang, font, size, a, &b, maxw);
1244 		if (b > a)
1245 		{
1246 			if (q == 0)
1247 				x = 0;
1248 			else if (q == 1)
1249 				x = (maxw - w) / 2;
1250 			else
1251 				x = (maxw - w);
1252 			fz_append_printf(ctx, buf, "%g %g Td\n", x - px, -lineheight);
1253 			if (b[-1] == '\n' || b[-1] == '\r')
1254 				write_string(ctx, buf, lang, font, fontname, size, a, b-1);
1255 			else
1256 				write_string(ctx, buf, lang, font, fontname, size, a, b);
1257 			a = b;
1258 			px = x;
1259 		}
1260 	}
1261 }
1262 
1263 static void
write_comb_string(fz_context * ctx,fz_buffer * buf,fz_text_language lang,const char * fontname,fz_font * font,float size,const char * text,float cell_w)1264 write_comb_string(fz_context *ctx, fz_buffer *buf,
1265 	fz_text_language lang, const char *fontname,
1266 	fz_font *font, float size, const char *text, float cell_w)
1267 {
1268 	struct text_walk_state state;
1269 	int last_enc = 0;
1270 	float pad, carry = 0;
1271 
1272 	init_text_walk(ctx, &state, lang, font, text, text + strlen(text));
1273 
1274 	while (next_text_walk(ctx, &state))
1275 	{
1276 		if (state.enc != last_enc)
1277 		{
1278 			if (last_enc)
1279 				fz_append_string(ctx, buf, "] TJ\n");
1280 
1281 			switch (state.enc)
1282 			{
1283 			case ENC_LATIN: fz_append_printf(ctx, buf, "/%s %g Tf\n", fontname, size); break;
1284 			case ENC_GREEK: fz_append_printf(ctx, buf, "/%sGRK %g Tf\n", fontname, size); break;
1285 			case ENC_CYRILLIC: fz_append_printf(ctx, buf, "/%sCYR %g Tf\n", fontname, size); break;
1286 			case ENC_KOREAN: fz_append_printf(ctx, buf, "/Batang %g Tf\n", size); break;
1287 			case ENC_JAPANESE: fz_append_printf(ctx, buf, "/Mincho %g Tf\n", size); break;
1288 			case ENC_HANT: fz_append_printf(ctx, buf, "/Ming %g Tf\n", size); break;
1289 			case ENC_HANS: fz_append_printf(ctx, buf, "/Song %g Tf\n", size); break;
1290 			}
1291 
1292 			fz_append_byte(ctx, buf, '[');
1293 
1294 			last_enc = state.enc;
1295 		}
1296 
1297 		pad = (cell_w - state.w * 1000) / 2;
1298 		fz_append_printf(ctx, buf, "%g", -(carry + pad));
1299 		carry = pad;
1300 
1301 		if (state.enc < ENC_KOREAN)
1302 		{
1303 			fz_append_byte(ctx, buf, '(');
1304 			if (state.c == '(' || state.c == ')' || state.c == '\\')
1305 				fz_append_byte(ctx, buf, '\\');
1306 			fz_append_byte(ctx, buf, state.c);
1307 			fz_append_byte(ctx, buf, ')');
1308 		}
1309 		else
1310 		{
1311 			fz_append_printf(ctx, buf, "<%04x>", state.c);
1312 		}
1313 	}
1314 	if (last_enc)
1315 		fz_append_string(ctx, buf, "] TJ\n");
1316 }
1317 
1318 static void
layout_comb_string(fz_context * ctx,fz_layout_block * out,float x,float y,const char * a,const char * b,fz_font * font,float size,float cell_w)1319 layout_comb_string(fz_context *ctx, fz_layout_block *out, float x, float y,
1320 	const char *a, const char *b, fz_font *font, float size, float cell_w)
1321 {
1322 	int n, c, g;
1323 	int first = 1;
1324 	float w;
1325 	if (a == b)
1326 		fz_add_layout_line(ctx, out, x + cell_w / 2, y, size, a);
1327 	while (a < b)
1328 	{
1329 		n = fz_chartorune(&c, a);
1330 		c = fz_windows_1252_from_unicode(c);
1331 		if (c < 0) c = REPLACEMENT;
1332 		g = fz_encode_character(ctx, font, c);
1333 		w = fz_advance_glyph(ctx, font, g, 0) * size;
1334 		if (first)
1335 		{
1336 			fz_add_layout_line(ctx, out, x + (cell_w - w) / 2, y, size, a);
1337 			first = 0;
1338 		}
1339 		fz_add_layout_char(ctx, out, x + (cell_w - w) / 2, w, a);
1340 		a += n;
1341 		x += cell_w;
1342 	}
1343 }
1344 
1345 static void
layout_string(fz_context * ctx,fz_layout_block * out,fz_text_language lang,fz_font * font,float size,float x,float y,const char * a,const char * b)1346 layout_string(fz_context *ctx, fz_layout_block *out,
1347 	fz_text_language lang, fz_font *font, float size,
1348 	float x, float y, const char *a, const char *b)
1349 {
1350 	struct text_walk_state state;
1351 	fz_add_layout_line(ctx, out, x, y, size, a);
1352 	init_text_walk(ctx, &state, lang, font, a, b);
1353 	while (next_text_walk(ctx, &state))
1354 	{
1355 		fz_add_layout_char(ctx, out, x, state.w * size, state.text);
1356 		x += state.w * size;
1357 	}
1358 }
1359 
1360 static void
layout_string_with_quadding(fz_context * ctx,fz_layout_block * out,fz_text_language lang,fz_font * font,float size,float lineheight,float xorig,float y,const char * a,float maxw,int q)1361 layout_string_with_quadding(fz_context *ctx, fz_layout_block *out,
1362 	fz_text_language lang, fz_font *font, float size, float lineheight,
1363 	float xorig, float y, const char *a, float maxw, int q)
1364 {
1365 	const char *b;
1366 	float x = 0, w;
1367 	int add_line_at_end = 0;
1368 
1369 	if (!*a)
1370 		add_line_at_end = 1;
1371 
1372 	while (*a)
1373 	{
1374 		w = break_string(ctx, lang, font, size, a, &b, maxw);
1375 		if (b > a)
1376 		{
1377 			if (q > 0)
1378 			{
1379 				if (q == 1)
1380 					x = (maxw - w) / 2;
1381 				else
1382 					x = (maxw - w);
1383 			}
1384 			if (b[-1] == '\n' || b[-1] == '\r')
1385 			{
1386 				layout_string(ctx, out, lang, font, size, xorig+x, y, a, b-1);
1387 				add_line_at_end = 1;
1388 			}
1389 			else
1390 			{
1391 				layout_string(ctx, out, lang, font, size, xorig+x, y, a, b);
1392 				add_line_at_end = 0;
1393 			}
1394 			a = b;
1395 			y -= lineheight;
1396 		}
1397 	}
1398 	if (add_line_at_end)
1399 		fz_add_layout_line(ctx, out, xorig, y, size, a);
1400 }
1401 
full_font_name(const char ** name)1402 static const char *full_font_name(const char **name)
1403 {
1404 	if (!strcmp(*name, "Cour")) return "Courier";
1405 	if (!strcmp(*name, "Helv")) return "Helvetica";
1406 	if (!strcmp(*name, "TiRo")) return "Times-Roman";
1407 	if (!strcmp(*name, "Symb")) return "Symbol";
1408 	if (!strcmp(*name, "ZaDb")) return "ZapfDingbats";
1409 	return *name = "Helv", "Helvetica";
1410 }
1411 
1412 static void
write_variable_text(fz_context * ctx,pdf_annot * annot,fz_buffer * buf,pdf_obj ** res,fz_text_language lang,const char * text,const char * fontname,float size,float color[3],int q,float w,float h,float padding,float baseline,float lineheight,int multiline,int comb,int adjust_baseline)1413 write_variable_text(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, pdf_obj **res,
1414 	fz_text_language lang, const char *text,
1415 	const char *fontname, float size, float color[3], int q,
1416 	float w, float h, float padding, float baseline, float lineheight,
1417 	int multiline, int comb, int adjust_baseline)
1418 {
1419 	fz_font *font;
1420 	pdf_obj *res_font;
1421 
1422 	w -= padding * 2;
1423 	h -= padding * 2;
1424 
1425 	font = fz_new_base14_font(ctx, full_font_name(&fontname));
1426 	fz_try(ctx)
1427 	{
1428 		*res = pdf_new_dict(ctx, annot->page->doc, 1);
1429 		res_font = pdf_dict_put_dict(ctx, *res, PDF_NAME(Font), 1);
1430 		add_required_fonts(ctx, annot->page->doc, res_font, lang, font, fontname, text);
1431 
1432 		if (size == 0)
1433 		{
1434 			if (multiline)
1435 				size = 12;
1436 			else
1437 			{
1438 				size = w / measure_string(ctx, lang, font, text);
1439 				if (size > h)
1440 					size = h;
1441 			}
1442 		}
1443 
1444 		lineheight = size * lineheight;
1445 		baseline = size * baseline;
1446 
1447 		if (adjust_baseline)
1448 		{
1449 			/* Make sure baseline is inside rectangle */
1450 			if (baseline + 0.2f * size > h)
1451 				baseline = h - 0.2f * size;
1452 		}
1453 
1454 		fz_append_string(ctx, buf, "BT\n");
1455 		fz_append_printf(ctx, buf, "%g %g %g rg\n", color[0], color[1], color[2]);
1456 		if (multiline)
1457 		{
1458 			fz_append_printf(ctx, buf, "%g %g Td\n", padding, padding+h-baseline+lineheight);
1459 			write_string_with_quadding(ctx, buf, lang, fontname, font, size, lineheight, text, w, q);
1460 		}
1461 		else if (comb > 0)
1462 		{
1463 			float ty = (h - size) / 2;
1464 			fz_append_printf(ctx, buf, "%g %g Td\n", padding, padding+h-baseline-ty);
1465 			write_comb_string(ctx, buf, lang, fontname, font, size, text, (w * 1000 / size) / comb);
1466 		}
1467 		else
1468 		{
1469 			float tx = 0, ty = (h - size) / 2;
1470 			if (q > 0)
1471 			{
1472 				float tw = measure_string(ctx, lang, font, text) * size;
1473 				if (q == 1)
1474 					tx = (w - tw) / 2;
1475 				else
1476 					tx = (w - tw);
1477 			}
1478 			fz_append_printf(ctx, buf, "%g %g Td\n", padding+tx, padding+h-baseline-ty);
1479 			write_string(ctx, buf, lang, font, fontname, size, text, text + strlen(text));
1480 		}
1481 		fz_append_string(ctx, buf, "ET\n");
1482 	}
1483 	fz_always(ctx)
1484 		fz_drop_font(ctx, font);
1485 	fz_catch(ctx)
1486 		fz_rethrow(ctx);
1487 }
1488 
1489 static void
layout_variable_text(fz_context * ctx,fz_layout_block * out,const char * text,fz_text_language lang,const char * fontname,float size,int q,float x,float y,float w,float h,float padding,float baseline,float lineheight,int multiline,int comb,int adjust_baseline)1490 layout_variable_text(fz_context *ctx, fz_layout_block *out,
1491 	const char *text, fz_text_language lang, const char *fontname, float size, int q,
1492 	float x, float y, float w, float h, float padding, float baseline, float lineheight,
1493 	int multiline, int comb, int adjust_baseline)
1494 {
1495 	fz_font *font;
1496 
1497 	w -= padding * 2;
1498 	h -= padding * 2;
1499 
1500 	font = fz_new_base14_font(ctx, full_font_name(&fontname));
1501 	fz_try(ctx)
1502 	{
1503 		if (size == 0)
1504 		{
1505 			if (multiline)
1506 				size = 12;
1507 			else
1508 			{
1509 				size = w / measure_string(ctx, lang, font, text);
1510 				if (size > h)
1511 					size = h;
1512 			}
1513 		}
1514 
1515 		lineheight = size * lineheight;
1516 		baseline = size * baseline;
1517 
1518 		if (adjust_baseline)
1519 		{
1520 			/* Make sure baseline is inside rectangle */
1521 			if (baseline + 0.2f * size > h)
1522 				baseline = h - 0.2f * size;
1523 		}
1524 
1525 		if (multiline)
1526 		{
1527 			x += padding;
1528 			y += padding + h - baseline;
1529 			layout_string_with_quadding(ctx, out, lang, font, size, lineheight, x, y, text, w, q);
1530 		}
1531 		else if (comb > 0)
1532 		{
1533 			float ty = (h - size) / 2;
1534 			x += padding;
1535 			y += padding + h - baseline - ty;
1536 			layout_comb_string(ctx, out, x, y, text, text + strlen(text), font, size, w / comb);
1537 		}
1538 		else
1539 		{
1540 			float tx = 0, ty = (h - size) / 2;
1541 			if (q > 0)
1542 			{
1543 				float tw = measure_string(ctx, lang, font, text) * size;
1544 				if (q == 1)
1545 					tx = (w - tw) / 2;
1546 				else
1547 					tx = (w - tw);
1548 			}
1549 			x += padding + tx;
1550 			y += padding + h - baseline - ty;
1551 			layout_string(ctx, out, lang, font, size, x, y, text, text + strlen(text));
1552 		}
1553 	}
1554 	fz_always(ctx)
1555 		fz_drop_font(ctx, font);
1556 	fz_catch(ctx)
1557 		fz_rethrow(ctx);
1558 }
1559 
1560 static void
pdf_write_free_text_appearance(fz_context * ctx,pdf_annot * annot,fz_buffer * buf,fz_rect * rect,fz_rect * bbox,fz_matrix * matrix,pdf_obj ** res)1561 pdf_write_free_text_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf,
1562 	fz_rect *rect, fz_rect *bbox, fz_matrix *matrix, pdf_obj **res)
1563 {
1564 	const char *font;
1565 	float size, color[3];
1566 	const char *text;
1567 	float w, h, t, b;
1568 	int q, r;
1569 	int lang;
1570 
1571 	/* /Rotate is an undocumented annotation property supported by Adobe */
1572 	text = pdf_annot_contents(ctx, annot);
1573 	r = pdf_dict_get_int(ctx, annot->obj, PDF_NAME(Rotate));
1574 	q = pdf_annot_quadding(ctx, annot);
1575 	pdf_annot_default_appearance(ctx, annot, &font, &size, color);
1576 	lang = pdf_annot_language(ctx, annot);
1577 
1578 	w = rect->x1 - rect->x0;
1579 	h = rect->y1 - rect->y0;
1580 	if (r == 90 || r == 270)
1581 		t = h, h = w, w = t;
1582 
1583 	*matrix = fz_rotate(r);
1584 	*bbox = fz_make_rect(0, 0, w, h);
1585 
1586 	if (pdf_write_fill_color_appearance(ctx, annot, buf))
1587 		fz_append_printf(ctx, buf, "0 0 %g %g re\nf\n", w, h);
1588 
1589 	b = pdf_write_border_appearance(ctx, annot, buf);
1590 	if (b > 0)
1591 	{
1592 		fz_append_printf(ctx, buf, "%g %g %g RG\n", color[0], color[1], color[2]);
1593 		fz_append_printf(ctx, buf, "%g %g %g %g re\nS\n", b/2, b/2, w-b, h-b);
1594 	}
1595 
1596 	fz_append_printf(ctx, buf, "%g %g %g %g re\nW\nn\n", b, b, w-b*2, h-b*2);
1597 
1598 	write_variable_text(ctx, annot, buf, res, lang, text, font, size, color, q, w, h, b*2,
1599 		0.8f, 1.2f, 1, 0, 0);
1600 }
1601 
1602 static void
pdf_write_tx_widget_appearance(fz_context * ctx,pdf_annot * annot,fz_buffer * buf,fz_rect * rect,fz_rect * bbox,fz_matrix * matrix,pdf_obj ** res,const char * text,int ff)1603 pdf_write_tx_widget_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf,
1604 	fz_rect *rect, fz_rect *bbox, fz_matrix *matrix, pdf_obj **res,
1605 	const char *text, int ff)
1606 {
1607 	fz_text_language lang;
1608 	const char *font;
1609 	float size, color[3];
1610 	float w, h, t, b;
1611 	int has_bc = 0;
1612 	int q, r;
1613 
1614 	r = pdf_dict_get_int(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME(MK)), PDF_NAME(R));
1615 	q = pdf_annot_quadding(ctx, annot);
1616 	pdf_annot_default_appearance(ctx, annot, &font, &size, color);
1617 	lang = pdf_annot_language(ctx, annot);
1618 
1619 	w = rect->x1 - rect->x0;
1620 	h = rect->y1 - rect->y0;
1621 	r = r % 360;
1622 	if (r == 90 || r == 270)
1623 		t = h, h = w, w = t;
1624 	*matrix = fz_rotate(r);
1625 	*bbox = fz_make_rect(0, 0, w, h);
1626 
1627 	fz_append_string(ctx, buf, "/Tx BMC\nq\n");
1628 
1629 	if (pdf_write_MK_BG_appearance(ctx, annot, buf))
1630 		fz_append_printf(ctx, buf, "0 0 %g %g re\nf\n", w, h);
1631 
1632 	b = pdf_write_border_appearance(ctx, annot, buf);
1633 	if (b > 0 && pdf_write_MK_BC_appearance(ctx, annot, buf))
1634 	{
1635 		fz_append_printf(ctx, buf, "%g %g %g %g re\ns\n", b/2, b/2, w-b, h-b);
1636 		has_bc = 1;
1637 	}
1638 
1639 	fz_append_printf(ctx, buf, "%g %g %g %g re\nW\nn\n", b, b, w-b*2, h-b*2);
1640 
1641 	if (ff & PDF_TX_FIELD_IS_MULTILINE)
1642 	{
1643 		write_variable_text(ctx, annot, buf, res, lang, text, font, size, color, q, w, h, b*2,
1644 			1.116f, 1.116f, 1, 0, 1);
1645 	}
1646 	else if (ff & PDF_TX_FIELD_IS_COMB)
1647 	{
1648 		int maxlen = pdf_to_int(ctx, pdf_dict_get_inheritable(ctx, annot->obj, PDF_NAME(MaxLen)));
1649 		if (has_bc && maxlen > 1)
1650 		{
1651 			float cell_w = (w - 2 * b) / maxlen;
1652 			int i;
1653 			for (i = 1; i < maxlen; ++i)
1654 			{
1655 				float x = b + cell_w * i;
1656 				fz_append_printf(ctx, buf, "%g %g m %g %g l s\n", x, b, x, h-b);
1657 			}
1658 		}
1659 		write_variable_text(ctx, annot, buf, res, lang, text, font, size, color, q, w, h, 0,
1660 			0.8f, 1.2f, 0, maxlen, 0);
1661 	}
1662 	else
1663 	{
1664 		write_variable_text(ctx, annot, buf, res, lang, text, font, size, color, q, w, h, b*2,
1665 			0.8f, 1.2f, 0, 0, 0);
1666 	}
1667 
1668 	fz_append_string(ctx, buf, "Q\nEMC\n");
1669 }
1670 
1671 fz_layout_block *
pdf_layout_text_widget(fz_context * ctx,pdf_annot * annot)1672 pdf_layout_text_widget(fz_context *ctx, pdf_annot *annot)
1673 {
1674 	fz_text_language lang;
1675 	fz_layout_block *out;
1676 	const char *font;
1677 	const char *text;
1678 	fz_rect rect;
1679 	float size, color[3];
1680 	float w, h, t, b, x, y;
1681 	int q, r;
1682 	int ff;
1683 
1684 	rect = pdf_dict_get_rect(ctx, annot->obj, PDF_NAME(Rect));
1685 	text = pdf_field_value(ctx, annot->obj);
1686 	ff = pdf_field_flags(ctx, annot->obj);
1687 
1688 	b = pdf_annot_border(ctx, annot);
1689 	r = pdf_dict_get_int(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME(MK)), PDF_NAME(R));
1690 	q = pdf_annot_quadding(ctx, annot);
1691 	pdf_annot_default_appearance(ctx, annot, &font, &size, color);
1692 	lang = pdf_annot_language(ctx, annot);
1693 
1694 	w = rect.x1 - rect.x0;
1695 	h = rect.y1 - rect.y0;
1696 	r = r % 360;
1697 	if (r == 90 || r == 270)
1698 		t = h, h = w, w = t;
1699 
1700 	x = rect.x0;
1701 	y = rect.y0;
1702 
1703 	out = fz_new_layout(ctx);
1704 	fz_try(ctx)
1705 	{
1706 		pdf_page_transform(ctx, annot->page, NULL, &out->matrix);
1707 		out->matrix = fz_concat(out->matrix, fz_rotate(r));
1708 		out->inv_matrix = fz_invert_matrix(out->matrix);
1709 
1710 		if (ff & PDF_TX_FIELD_IS_MULTILINE)
1711 		{
1712 			layout_variable_text(ctx, out, text, lang, font, size, q, x, y, w, h, b*2, 1.116f, 1.116f, 1, 0, 1);
1713 		}
1714 		else if (ff & PDF_TX_FIELD_IS_COMB)
1715 		{
1716 			int maxlen = pdf_to_int(ctx, pdf_dict_get_inheritable(ctx, annot->obj, PDF_NAME(MaxLen)));
1717 			layout_variable_text(ctx, out, text, lang, font, size, q, x, y, w, h, 0, 0.8f, 1.2f, 0, maxlen, 0);
1718 		}
1719 		else
1720 		{
1721 			layout_variable_text(ctx, out, text, lang, font, size, q, x, y, w, h, b*2, 0.8f, 1.2f, 0, 0, 0);
1722 		}
1723 	}
1724 	fz_catch(ctx)
1725 	{
1726 		fz_drop_layout(ctx, out);
1727 		fz_rethrow(ctx);
1728 	}
1729 	return out;
1730 }
1731 
1732 static void
pdf_write_ch_widget_appearance(fz_context * ctx,pdf_annot * annot,fz_buffer * buf,fz_rect * rect,fz_rect * bbox,fz_matrix * matrix,pdf_obj ** res)1733 pdf_write_ch_widget_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf,
1734 	fz_rect *rect, fz_rect *bbox, fz_matrix *matrix, pdf_obj **res)
1735 {
1736 	int ff = pdf_field_flags(ctx, annot->obj);
1737 	if (ff & PDF_CH_FIELD_IS_COMBO)
1738 	{
1739 		/* TODO: Pop-down arrow */
1740 		pdf_write_tx_widget_appearance(ctx, annot, buf, rect, bbox, matrix, res,
1741 			pdf_field_value(ctx, annot->obj), 0);
1742 	}
1743 	else
1744 	{
1745 		fz_buffer *text = fz_new_buffer(ctx, 1024);
1746 		fz_try(ctx)
1747 		{
1748 			pdf_obj *opt = pdf_dict_get(ctx, annot->obj, PDF_NAME(Opt));
1749 			int i = pdf_dict_get_int(ctx, annot->obj, PDF_NAME(TI));
1750 			int n = pdf_array_len(ctx, opt);
1751 			/* TODO: Scrollbar */
1752 			/* TODO: Highlight selected items */
1753 			if (i < 0)
1754 				i = 0;
1755 			for (; i < n; ++i)
1756 			{
1757 				pdf_obj *val = pdf_array_get(ctx, opt, i);
1758 				if (pdf_is_array(ctx, val))
1759 					fz_append_string(ctx, text, pdf_array_get_text_string(ctx, val, 1));
1760 				else
1761 					fz_append_string(ctx, text, pdf_to_text_string(ctx, val));
1762 				fz_append_byte(ctx, text, '\n');
1763 			}
1764 			pdf_write_tx_widget_appearance(ctx, annot, buf, rect, bbox, matrix, res,
1765 				fz_string_from_buffer(ctx, text), PDF_TX_FIELD_IS_MULTILINE);
1766 		}
1767 		fz_always(ctx)
1768 			fz_drop_buffer(ctx, text);
1769 		fz_catch(ctx)
1770 			fz_rethrow(ctx);
1771 	}
1772 }
1773 
1774 static void
pdf_write_sig_widget_appearance(fz_context * ctx,pdf_annot * annot,fz_buffer * buf,fz_rect * rect,fz_rect * bbox,fz_matrix * matrix,pdf_obj ** res)1775 pdf_write_sig_widget_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf,
1776 	fz_rect *rect, fz_rect *bbox, fz_matrix *matrix, pdf_obj **res)
1777 {
1778 	float x0 = rect->x0 + 1;
1779 	float y0 = rect->y0 + 1;
1780 	float x1 = rect->x1 - 1;
1781 	float y1 = rect->y1 - 1;
1782 	float w = x1 - x0;
1783 	float h = y1 - y0;
1784 	fz_append_printf(ctx, buf, "1 w\n0 G\n");
1785 	fz_append_printf(ctx, buf, "%g %g %g %g re\n", x0, y0, w, h);
1786 	fz_append_printf(ctx, buf, "%g %g m %g %g l\n", x0, y0, x1, y1);
1787 	fz_append_printf(ctx, buf, "%g %g m %g %g l\n", x1, y0, x0, y1);
1788 	fz_append_printf(ctx, buf, "s\n");
1789 	*bbox = *rect;
1790 	*matrix = fz_identity;
1791 }
1792 
1793 static void
pdf_write_widget_appearance(fz_context * ctx,pdf_annot * annot,fz_buffer * buf,fz_rect * rect,fz_rect * bbox,fz_matrix * matrix,pdf_obj ** res)1794 pdf_write_widget_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf,
1795 	fz_rect *rect, fz_rect *bbox, fz_matrix *matrix, pdf_obj **res)
1796 {
1797 	pdf_obj *ft = pdf_dict_get_inheritable(ctx, annot->obj, PDF_NAME(FT));
1798 	if (pdf_name_eq(ctx, ft, PDF_NAME(Tx)))
1799 	{
1800 		int ff = pdf_field_flags(ctx, annot->obj);
1801 		char *format = NULL;
1802 		const char *text = NULL;
1803 		if (!annot->ignore_trigger_events)
1804 		{
1805 			format = pdf_field_event_format(ctx, annot->page->doc, annot->obj);
1806 			if (format)
1807 				text = format;
1808 			else
1809 				text = pdf_field_value(ctx, annot->obj);
1810 		}
1811 		else
1812 		{
1813 			text = pdf_field_value(ctx, annot->obj);
1814 		}
1815 		fz_try(ctx)
1816 			pdf_write_tx_widget_appearance(ctx, annot, buf, rect, bbox, matrix, res, text, ff);
1817 		fz_always(ctx)
1818 			fz_free(ctx, format);
1819 		fz_catch(ctx)
1820 			fz_rethrow(ctx);
1821 	}
1822 	else if (pdf_name_eq(ctx, ft, PDF_NAME(Ch)))
1823 	{
1824 		pdf_write_ch_widget_appearance(ctx, annot, buf, rect, bbox, matrix, res);
1825 	}
1826 	else if (pdf_name_eq(ctx, ft, PDF_NAME(Sig)))
1827 	{
1828 		pdf_write_sig_widget_appearance(ctx, annot, buf, rect, bbox, matrix, res);
1829 	}
1830 	else
1831 	{
1832 		fz_throw(ctx, FZ_ERROR_GENERIC, "cannot create appearance stream for %s widgets", pdf_to_name(ctx, ft));
1833 	}
1834 }
1835 
1836 static void
pdf_write_appearance(fz_context * ctx,pdf_annot * annot,fz_buffer * buf,fz_rect * rect,fz_rect * bbox,fz_matrix * matrix,pdf_obj ** res)1837 pdf_write_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf,
1838 	fz_rect *rect, fz_rect *bbox, fz_matrix *matrix, pdf_obj **res)
1839 {
1840 	switch (pdf_annot_type(ctx, annot))
1841 	{
1842 	default:
1843 		fz_throw(ctx, FZ_ERROR_GENERIC, "cannot create appearance stream for %s annotations",
1844 			pdf_dict_get_name(ctx, annot->obj, PDF_NAME(Subtype)));
1845 	case PDF_ANNOT_WIDGET:
1846 		pdf_write_widget_appearance(ctx, annot, buf, rect, bbox, matrix, res);
1847 		break;
1848 	case PDF_ANNOT_INK:
1849 		pdf_write_ink_appearance(ctx, annot, buf, rect);
1850 		*matrix = fz_identity;
1851 		*bbox = *rect;
1852 		break;
1853 	case PDF_ANNOT_POLYGON:
1854 		pdf_write_polygon_appearance(ctx, annot, buf, rect, 1);
1855 		*matrix = fz_identity;
1856 		*bbox = *rect;
1857 		break;
1858 	case PDF_ANNOT_POLY_LINE:
1859 		pdf_write_polygon_appearance(ctx, annot, buf, rect, 0);
1860 		*matrix = fz_identity;
1861 		*bbox = *rect;
1862 		break;
1863 	case PDF_ANNOT_LINE:
1864 		pdf_write_line_appearance(ctx, annot, buf, rect);
1865 		*matrix = fz_identity;
1866 		*bbox = *rect;
1867 		break;
1868 	case PDF_ANNOT_SQUARE:
1869 		pdf_write_square_appearance(ctx, annot, buf, rect);
1870 		*matrix = fz_identity;
1871 		*bbox = *rect;
1872 		break;
1873 	case PDF_ANNOT_CIRCLE:
1874 		pdf_write_circle_appearance(ctx, annot, buf, rect);
1875 		*matrix = fz_identity;
1876 		*bbox = *rect;
1877 		break;
1878 	case PDF_ANNOT_CARET:
1879 		pdf_write_caret_appearance(ctx, annot, buf, rect, bbox);
1880 		*matrix = fz_identity;
1881 		break;
1882 	case PDF_ANNOT_TEXT:
1883 	case PDF_ANNOT_FILE_ATTACHMENT:
1884 	case PDF_ANNOT_SOUND:
1885 		pdf_write_icon_appearance(ctx, annot, buf, rect, bbox);
1886 		*matrix = fz_identity;
1887 		break;
1888 	case PDF_ANNOT_HIGHLIGHT:
1889 		pdf_write_highlight_appearance(ctx, annot, buf, rect, res);
1890 		*matrix = fz_identity;
1891 		*bbox = *rect;
1892 		break;
1893 	case PDF_ANNOT_UNDERLINE:
1894 		pdf_write_underline_appearance(ctx, annot, buf, rect);
1895 		*matrix = fz_identity;
1896 		*bbox = *rect;
1897 		break;
1898 	case PDF_ANNOT_STRIKE_OUT:
1899 		pdf_write_strike_out_appearance(ctx, annot, buf, rect);
1900 		*matrix = fz_identity;
1901 		*bbox = *rect;
1902 		break;
1903 	case PDF_ANNOT_SQUIGGLY:
1904 		pdf_write_squiggly_appearance(ctx, annot, buf, rect);
1905 		*matrix = fz_identity;
1906 		*bbox = *rect;
1907 		break;
1908 	case PDF_ANNOT_REDACT:
1909 		pdf_write_redact_appearance(ctx, annot, buf, rect);
1910 		*matrix = fz_identity;
1911 		*bbox = *rect;
1912 		break;
1913 	case PDF_ANNOT_STAMP:
1914 		pdf_write_stamp_appearance(ctx, annot, buf, rect, bbox, res);
1915 		*matrix = fz_identity;
1916 		break;
1917 	case PDF_ANNOT_FREE_TEXT:
1918 		pdf_write_free_text_appearance(ctx, annot, buf, rect, bbox, matrix, res);
1919 		break;
1920 	}
1921 }
1922 
draw_push_button(fz_context * ctx,pdf_annot * annot,fz_rect bbox,fz_matrix matrix,float w,float h,const char * caption,const char * font,float size,float color[3],int down)1923 static pdf_obj *draw_push_button(fz_context *ctx, pdf_annot *annot, fz_rect bbox, fz_matrix matrix, float w, float h,
1924 	const char *caption, const char *font, float size, float color[3],
1925 	int down)
1926 {
1927 	pdf_obj *ap, *res = NULL;
1928 	fz_buffer *buf;
1929 	float bc[3] = { 0, 0, 0 };
1930 	float bg[3] = { 0.8f, 0.8f, 0.8f };
1931 	float hi[3], sh[3];
1932 	int has_bg, has_bc;
1933 	float b;
1934 	int i;
1935 
1936 	buf = fz_new_buffer(ctx, 1024);
1937 	fz_var(res);
1938 	fz_try(ctx)
1939 	{
1940 		b = pdf_annot_border(ctx, annot);
1941 		has_bc = pdf_annot_MK_BC_rgb(ctx, annot, bc);
1942 		has_bg = pdf_annot_MK_BG_rgb(ctx, annot, bg);
1943 
1944 		for (i = 0; i < 3; ++i)
1945 		{
1946 			if (down)
1947 			{
1948 				sh[i] = 1 - (1 - bg[i]) / 2;
1949 				hi[i] = bg[i] / 2;
1950 			}
1951 			else
1952 			{
1953 				hi[i] = 1 - (1 - bg[i]) / 2;
1954 				sh[i] = bg[i] / 2;
1955 			}
1956 		}
1957 
1958 		fz_append_string(ctx, buf, "q\n");
1959 		fz_append_printf(ctx, buf, "%g w\n", b);
1960 		if (has_bg)
1961 		{
1962 			fz_append_printf(ctx, buf, "%g %g %g rg\n", bg[0], bg[1], bg[2]);
1963 			fz_append_printf(ctx, buf, "0 0 %g %g re\nf\n", 0, 0, w, h);
1964 		}
1965 		if (has_bc && b > 0)
1966 		{
1967 			fz_append_printf(ctx, buf, "%g %g %g RG\n", bc[0], bc[1], bc[2]);
1968 			fz_append_printf(ctx, buf, "%g %g %g %g re\nS\n", b/2, b/2, w-b, h-b);
1969 		}
1970 		if (has_bg)
1971 		{
1972 			fz_append_printf(ctx, buf, "%g %g %g rg\n", hi[0], hi[1], hi[2]);
1973 			fz_append_printf(ctx, buf, "%g %g m %g %g l %g %g l %g %g l %g %g l %g %g l f\n",
1974 				b, b, b, h-b, w-b, h-b, w-b-2, h-b-2, b+2, h-b-2, b+2, b+2);
1975 			fz_append_printf(ctx, buf, "%g %g %g rg\n", sh[0], sh[1], sh[2]);
1976 			fz_append_printf(ctx, buf, "%g %g m %g %g l %g %g l %g %g l %g %g l %g %g l f\n",
1977 				b, b, b+2, b+2, w-b-2, b+2, w-b-2, h-b-2, w-b, h-b, w-b, b);
1978 		}
1979 		if (down)
1980 			fz_append_string(ctx, buf, "1 0 0 1 2 -2 cm\n");
1981 		write_variable_text(ctx, annot, buf, &res, FZ_LANG_UNSET, caption, font, size, color, 1, w, h, b+6, 0.8f, 1.2f, 0, 0, 0);
1982 		fz_append_string(ctx, buf, "Q\n");
1983 
1984 		ap = pdf_new_xobject(ctx, annot->page->doc, bbox, matrix, res, buf);
1985 	}
1986 	fz_always(ctx)
1987 	{
1988 		pdf_drop_obj(ctx, res);
1989 		fz_drop_buffer(ctx, buf);
1990 	}
1991 	fz_catch(ctx)
1992 		fz_rethrow(ctx);
1993 	return ap;
1994 }
1995 
draw_radio_button(fz_context * ctx,pdf_annot * annot,fz_rect bbox,fz_matrix matrix,float w,float h,int yes)1996 static pdf_obj *draw_radio_button(fz_context *ctx, pdf_annot *annot, fz_rect bbox, fz_matrix matrix, float w, float h, int yes)
1997 {
1998 	pdf_obj *ap;
1999 	fz_buffer *buf;
2000 	float b;
2001 
2002 	buf = fz_new_buffer(ctx, 1024);
2003 	fz_try(ctx)
2004 	{
2005 		fz_append_string(ctx, buf, "q\n");
2006 		if (pdf_write_MK_BG_appearance(ctx, annot, buf))
2007 		{
2008 			draw_circle_in_box(ctx, buf, 0, 0, 0, w, h);
2009 			fz_append_string(ctx, buf, "f\n");
2010 		}
2011 		b = pdf_write_border_appearance(ctx, annot, buf);
2012 		if (b > 0 && pdf_write_MK_BC_appearance(ctx, annot, buf))
2013 		{
2014 			draw_circle_in_box(ctx, buf, b, 0, 0, w, h);
2015 			fz_append_string(ctx, buf, "s\n");
2016 		}
2017 		if (yes)
2018 		{
2019 			fz_append_string(ctx, buf, "0 g\n");
2020 			draw_circle(ctx, buf, (w-b*2)/4, (h-b*2)/4, w/2, h/2);
2021 			fz_append_string(ctx, buf, "f\n");
2022 		}
2023 		fz_append_string(ctx, buf, "Q\n");
2024 		ap = pdf_new_xobject(ctx, annot->page->doc, bbox, matrix, NULL, buf);
2025 	}
2026 	fz_always(ctx)
2027 		fz_drop_buffer(ctx, buf);
2028 	fz_catch(ctx)
2029 		fz_rethrow(ctx);
2030 	return ap;
2031 }
2032 
draw_check_button(fz_context * ctx,pdf_annot * annot,fz_rect bbox,fz_matrix matrix,float w,float h,int yes)2033 static pdf_obj *draw_check_button(fz_context *ctx, pdf_annot *annot, fz_rect bbox, fz_matrix matrix, float w, float h, int yes)
2034 {
2035 	float black[3] = { 0, 0, 0 };
2036 	pdf_obj *ap, *res = NULL;
2037 	fz_buffer *buf;
2038 	float b;
2039 
2040 	fz_var(res);
2041 
2042 	buf = fz_new_buffer(ctx, 1024);
2043 	fz_try(ctx)
2044 	{
2045 		fz_append_string(ctx, buf, "q\n");
2046 		if (pdf_write_MK_BG_appearance(ctx, annot, buf))
2047 			fz_append_printf(ctx, buf, "0 0 %g %g re\nf\n", w, h);
2048 		b = pdf_write_border_appearance(ctx, annot, buf);
2049 		if (b > 0 && pdf_write_MK_BC_appearance(ctx, annot, buf))
2050 			fz_append_printf(ctx, buf, "%g %g %g %g re\nS\n", b/2, b/2, w-b, h-b);
2051 		if (yes)
2052 			write_variable_text(ctx, annot, buf, &res, FZ_LANG_UNSET, "3", "ZaDb", h, black, 0, w, h, b+h/10, 0.8f, 1.2f, 0, 0, 0);
2053 		fz_append_string(ctx, buf, "Q\n");
2054 		ap = pdf_new_xobject(ctx, annot->page->doc, bbox, matrix, res, buf);
2055 	}
2056 	fz_always(ctx)
2057 	{
2058 		pdf_drop_obj(ctx, res);
2059 		fz_drop_buffer(ctx, buf);
2060 	}
2061 	fz_catch(ctx)
2062 		fz_rethrow(ctx);
2063 	return ap;
2064 }
2065 
pdf_update_button_appearance(fz_context * ctx,pdf_annot * annot)2066 static void pdf_update_button_appearance(fz_context *ctx, pdf_annot *annot)
2067 {
2068 	int ff = pdf_field_flags(ctx, annot->obj);
2069 	fz_rect rect = pdf_dict_get_rect(ctx, annot->obj, PDF_NAME(Rect));
2070 	fz_matrix matrix;
2071 	fz_rect bbox;
2072 	float w, h, t;
2073 	int r;
2074 
2075 	r = pdf_dict_get_int(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME(MK)), PDF_NAME(R));
2076 	w = rect.x1 - rect.x0;
2077 	h = rect.y1 - rect.y0;
2078 	r = r % 360;
2079 	if (r == 90 || r == 270)
2080 		t = h, h = w, w = t;
2081 	matrix = fz_rotate(r);
2082 	bbox = fz_make_rect(0, 0, w, h);
2083 
2084 
2085 	if (ff & PDF_BTN_FIELD_IS_PUSHBUTTON)
2086 	{
2087 		pdf_obj *ap_n = NULL;
2088 		pdf_obj *ap_d = NULL;
2089 		fz_var(ap_n);
2090 		fz_var(ap_d);
2091 		fz_try(ctx)
2092 		{
2093 			pdf_obj *ap, *MK, *CA, *AC;
2094 			const char *font;
2095 			const char *label;
2096 			float size, color[3];
2097 
2098 			pdf_annot_default_appearance(ctx, annot, &font, &size, color);
2099 
2100 			MK = pdf_dict_get(ctx, annot->obj, PDF_NAME(MK));
2101 			CA = pdf_dict_get(ctx, MK, PDF_NAME(CA));
2102 			AC = pdf_dict_get(ctx, MK, PDF_NAME(AC));
2103 
2104 			label = pdf_to_text_string(ctx, CA);
2105 			ap_n = draw_push_button(ctx, annot, bbox, matrix, w, h, label, font, size, color, 0);
2106 
2107 			label = pdf_to_text_string(ctx, AC ? AC : CA);
2108 			ap_d = draw_push_button(ctx, annot, bbox, matrix, w, h, label, font, size, color, 1);
2109 
2110 			ap = pdf_dict_put_dict(ctx, annot->obj, PDF_NAME(AP), 2);
2111 			pdf_dict_put(ctx, ap, PDF_NAME(N), ap_n);
2112 			pdf_dict_put(ctx, ap, PDF_NAME(D), ap_d);
2113 
2114 			pdf_drop_obj(ctx, annot->ap);
2115 			if (annot->is_hot && annot->is_active)
2116 				annot->ap = pdf_keep_obj(ctx, ap_d);
2117 			else
2118 				annot->ap = pdf_keep_obj(ctx, ap_n);
2119 			annot->has_new_ap = 1;
2120 		}
2121 		fz_always(ctx)
2122 		{
2123 			pdf_drop_obj(ctx, ap_n);
2124 			pdf_drop_obj(ctx, ap_d);
2125 		}
2126 		fz_catch(ctx)
2127 			fz_rethrow(ctx);
2128 	}
2129 	else
2130 	{
2131 		pdf_obj *as_yes = NULL;
2132 		pdf_obj *ap_off = NULL;
2133 		pdf_obj *ap_yes = NULL;
2134 		fz_var(ap_off);
2135 		fz_var(ap_yes);
2136 		fz_var(as_yes);
2137 		fz_try(ctx)
2138 		{
2139 			pdf_obj *ap, *ap_n, *as;
2140 
2141 			if (w > h) w = h;
2142 			if (h > w) h = w;
2143 
2144 			if (ff & PDF_BTN_FIELD_IS_RADIO)
2145 			{
2146 				ap_off = draw_radio_button(ctx, annot, bbox, matrix, w, h, 0);
2147 				ap_yes = draw_radio_button(ctx, annot, bbox, matrix, w, h, 1);
2148 			}
2149 			else
2150 			{
2151 				ap_off = draw_check_button(ctx, annot, bbox, matrix, w, h, 0);
2152 				ap_yes = draw_check_button(ctx, annot, bbox, matrix, w, h, 1);
2153 			}
2154 
2155 			as = pdf_dict_get(ctx, annot->obj, PDF_NAME(AS));
2156 			if (!as)
2157 			{
2158 				pdf_dict_put(ctx, annot->obj, PDF_NAME(AS), PDF_NAME(Off));
2159 				as = PDF_NAME(Off);
2160 			}
2161 
2162 			if (as == PDF_NAME(Off))
2163 				as_yes = pdf_keep_obj(ctx, pdf_button_field_on_state(ctx, annot->obj));
2164 			else
2165 				as_yes = pdf_keep_obj(ctx, as);
2166 
2167 			ap = pdf_dict_put_dict(ctx, annot->obj, PDF_NAME(AP), 2);
2168 			ap_n = pdf_dict_put_dict(ctx, ap, PDF_NAME(N), 2);
2169 			pdf_dict_put(ctx, ap_n, PDF_NAME(Off), ap_off);
2170 			pdf_dict_put(ctx, ap_n, as_yes, ap_yes);
2171 
2172 			pdf_drop_obj(ctx, annot->ap);
2173 			if (as == PDF_NAME(Off))
2174 				annot->ap = pdf_keep_obj(ctx, ap_off);
2175 			else
2176 				annot->ap = pdf_keep_obj(ctx, ap_yes);
2177 			annot->has_new_ap = 1;
2178 		}
2179 		fz_always(ctx)
2180 		{
2181 			pdf_drop_obj(ctx, as_yes);
2182 			pdf_drop_obj(ctx, ap_yes);
2183 			pdf_drop_obj(ctx, ap_off);
2184 		}
2185 		fz_catch(ctx)
2186 		{
2187 			fz_rethrow(ctx);
2188 		}
2189 	}
2190 }
2191 
pdf_update_signature_appearance(fz_context * ctx,pdf_annot * annot,const char * name,const char * dn,const char * date)2192 void pdf_update_signature_appearance(fz_context *ctx, pdf_annot *annot, const char *name, const char *dn, const char *date)
2193 {
2194 	pdf_obj *ap, *new_ap_n, *res_font;
2195 	char tmp[500];
2196 	fz_font *helv = NULL;
2197 	fz_font *zadb = NULL;
2198 	pdf_obj *res = NULL;
2199 	fz_buffer *buf;
2200 	fz_rect rect;
2201 	float w, h, size, name_w;
2202 	fz_text_language lang;
2203 
2204 	fz_var(helv);
2205 	fz_var(zadb);
2206 	fz_var(res);
2207 
2208 	buf = fz_new_buffer(ctx, 1024);
2209 	fz_try(ctx)
2210 	{
2211 		if (name && dn)
2212 		{
2213 			lang = pdf_annot_language(ctx, annot);
2214 
2215 			helv = fz_new_base14_font(ctx, "Helvetica");
2216 			zadb = fz_new_base14_font(ctx, "ZapfDingbats");
2217 
2218 			res = pdf_new_dict(ctx, annot->page->doc, 1);
2219 			res_font = pdf_dict_put_dict(ctx, res, PDF_NAME(Font), 1);
2220 			pdf_dict_put_drop(ctx, res_font, PDF_NAME(ZaDb), pdf_add_simple_font(ctx, annot->page->doc, zadb, 0));
2221 
2222 			rect = pdf_dict_get_rect(ctx, annot->obj, PDF_NAME(Rect));
2223 			w = (rect.x1 - rect.x0) / 2;
2224 			h = (rect.y1 - rect.y0);
2225 
2226 			/* Use flower symbol from ZapfDingbats as sigil */
2227 			fz_append_printf(ctx, buf, "q 1 0.8 0.8 rg BT /ZaDb %g Tf %g %g Td (`) Tj ET Q\n",
2228 					h*1.1f,
2229 					rect.x0 + w - (h*0.4f),
2230 					rect.y0 + h*0.1f);
2231 
2232 			/* Name */
2233 			name_w = measure_string(ctx, FZ_LANG_UNSET, helv, name);
2234 			size = fz_min(fz_min((w - 4) / name_w, h), 24);
2235 			fz_append_string(ctx, buf, "BT\n");
2236 			fz_append_printf(ctx, buf, "%g %g Td\n", rect.x0+2, rect.y1 - size*0.8f - (h-size)/2);
2237 			add_required_fonts(ctx, annot->page->doc, res_font, lang, helv, "Helv", name);
2238 			write_string(ctx, buf, lang, helv, "Helv", size, name, name + strlen(name));
2239 			fz_append_string(ctx, buf, "ET\n");
2240 
2241 			/* Information text */
2242 			size = fz_min(fz_min((w / 12), h / 6), 16);
2243 			fz_append_string(ctx, buf, "BT\n");
2244 			fz_append_printf(ctx, buf, "%g TL\n", size);
2245 			fz_append_printf(ctx, buf, "%g %g Td\n", rect.x0+w+2, rect.y1);
2246 			if (date)
2247 				fz_snprintf(tmp, sizeof tmp, "Digitally signed by %s\nDN: %s\nDate: %s", name, dn, date);
2248 			else
2249 				fz_snprintf(tmp, sizeof tmp, "Digitally signed by %s\nDN: %s", name, dn);
2250 			add_required_fonts(ctx, annot->page->doc, res_font, lang, helv, "Helv", tmp);
2251 			write_string_with_quadding(ctx, buf, lang, "Helv", helv, size, size, tmp, w-4, 0);
2252 			fz_append_string(ctx, buf, "ET\n");
2253 		}
2254 		else
2255 		{
2256 			rect.x0 = rect.y0 = 0;
2257 			rect.x1 = rect.y1 = 100;
2258 			res = pdf_new_dict(ctx, annot->page->doc, 0);
2259 			fz_append_string(ctx, buf, "% DSBlank\n");
2260 		}
2261 
2262 		/* Update the AP/N stream */
2263 		ap = pdf_dict_get(ctx, annot->obj, PDF_NAME(AP));
2264 		if (!ap)
2265 			ap = pdf_dict_put_dict(ctx, annot->obj, PDF_NAME(AP), 1);
2266 		new_ap_n = pdf_new_xobject(ctx, annot->page->doc, rect, fz_identity, res, buf);
2267 		pdf_drop_obj(ctx, annot->ap);
2268 		annot->ap = new_ap_n;
2269 		annot->needs_new_ap = 0;
2270 		annot->has_new_ap = 1;
2271 		pdf_dict_put(ctx, ap, PDF_NAME(N), new_ap_n);
2272 	}
2273 	fz_always(ctx)
2274 	{
2275 		fz_drop_font(ctx, helv);
2276 		fz_drop_font(ctx, zadb);
2277 		pdf_drop_obj(ctx, res);
2278 		fz_drop_buffer(ctx, buf);
2279 	}
2280 	fz_catch(ctx)
2281 	{
2282 		fz_rethrow(ctx);
2283 	}
2284 }
2285 
pdf_update_appearance(fz_context * ctx,pdf_annot * annot)2286 void pdf_update_appearance(fz_context *ctx, pdf_annot *annot)
2287 {
2288 	pdf_obj *subtype;
2289 	pdf_obj *ap, *ap_n, *as, *ft;
2290 
2291 	subtype = pdf_dict_get(ctx, annot->obj, PDF_NAME(Subtype));
2292 	if (subtype == PDF_NAME(Popup))
2293 		return;
2294 	if (subtype == PDF_NAME(Link))
2295 		return;
2296 
2297 	/* Check if the field is dirtied by JS events */
2298 	if (pdf_obj_is_dirty(ctx, annot->obj))
2299 		annot->needs_new_ap = 1;
2300 
2301 	/* Check if the current appearance has been swapped */
2302 	as = pdf_dict_get(ctx, annot->obj, PDF_NAME(AS));
2303 	ap = pdf_dict_get(ctx, annot->obj, PDF_NAME(AP));
2304 	ap_n = pdf_dict_get(ctx, ap, PDF_NAME(N));
2305 	if (annot->is_hot && annot->is_active && subtype == PDF_NAME(Widget))
2306 	{
2307 		pdf_obj *ap_d = pdf_dict_get(ctx, ap, PDF_NAME(D));
2308 		if (ap_d)
2309 			ap_n = ap_d;
2310 	}
2311 	if (!pdf_is_stream(ctx, ap_n))
2312 		ap_n = pdf_dict_get(ctx, ap_n, as);
2313 	if (annot->ap != ap_n)
2314 	{
2315 		pdf_drop_obj(ctx, annot->ap);
2316 		annot->ap = NULL;
2317 		if (pdf_is_stream(ctx, ap_n))
2318 			annot->ap = pdf_keep_obj(ctx, ap_n);
2319 		annot->has_new_ap = 1;
2320 	}
2321 
2322 	ft = pdf_dict_get(ctx, annot->obj, PDF_NAME(FT));
2323 
2324 	/* We cannot synthesise an appearance for a Sig, so don't even try.
2325 	 * Attempting to, will move the object into the new incremental
2326 	 * section, which will invalidate the signature. */
2327 	if ((!annot->ap && !pdf_name_eq(ctx, ft, PDF_NAME(Sig))) || annot->needs_new_ap)
2328 	{
2329 		fz_rect rect, bbox;
2330 		fz_matrix matrix = fz_identity;
2331 		fz_buffer *buf;
2332 		pdf_obj *res = NULL;
2333 		pdf_obj *new_ap_n = NULL;
2334 		fz_var(res);
2335 		fz_var(new_ap_n);
2336 
2337 		annot->needs_new_ap = 0;
2338 
2339 		/* Special case for Btn widgets that need multiple appearance streams. */
2340 		if (pdf_name_eq(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME(Subtype)), PDF_NAME(Widget)))
2341 		{
2342 			if (pdf_name_eq(ctx, pdf_dict_get_inheritable(ctx, annot->obj, PDF_NAME(FT)), PDF_NAME(Btn)))
2343 			{
2344 				pdf_update_button_appearance(ctx, annot);
2345 				pdf_clean_obj(ctx, annot->obj);
2346 				return;
2347 			}
2348 		}
2349 
2350 		buf = fz_new_buffer(ctx, 1024);
2351 		fz_try(ctx)
2352 		{
2353 			rect = pdf_dict_get_rect(ctx, annot->obj, PDF_NAME(Rect));
2354 			pdf_write_appearance(ctx, annot, buf, &rect, &bbox, &matrix, &res);
2355 			pdf_dict_put_rect(ctx, annot->obj, PDF_NAME(Rect), rect);
2356 
2357 			if (!ap_n)
2358 			{
2359 				if (!ap)
2360 				{
2361 					ap = pdf_new_dict(ctx, annot->page->doc, 1);
2362 					pdf_dict_put_drop(ctx, annot->obj, PDF_NAME(AP), ap);
2363 				}
2364 				new_ap_n = pdf_new_xobject(ctx, annot->page->doc, bbox, matrix, res, buf);
2365 				pdf_dict_put(ctx, ap, PDF_NAME(N), new_ap_n);
2366 			}
2367 			else
2368 			{
2369 				new_ap_n = pdf_keep_obj(ctx, ap_n);
2370 				pdf_update_xobject(ctx, annot->page->doc, ap_n, bbox, matrix, res, buf);
2371 			}
2372 
2373 			pdf_drop_obj(ctx, annot->ap);
2374 			annot->ap = NULL;
2375 			annot->ap = pdf_keep_obj(ctx, new_ap_n);
2376 			annot->has_new_ap = 1;
2377 		}
2378 		fz_always(ctx)
2379 		{
2380 			fz_drop_buffer(ctx, buf);
2381 			pdf_drop_obj(ctx, res);
2382 			pdf_drop_obj(ctx, new_ap_n);
2383 		}
2384 		fz_catch(ctx)
2385 		{
2386 			fz_warn(ctx, "cannot create appearance stream");
2387 		}
2388 	}
2389 
2390 	pdf_clean_obj(ctx, annot->obj);
2391 }
2392 
2393 int
pdf_update_annot(fz_context * ctx,pdf_annot * annot)2394 pdf_update_annot(fz_context *ctx, pdf_annot *annot)
2395 {
2396 	int changed;
2397 
2398 	pdf_update_appearance(ctx, annot);
2399 
2400 	changed = annot->has_new_ap;
2401 	annot->has_new_ap = 0;
2402 	return changed;
2403 }
2404