/src/mupdf/source/pdf/pdf-appearance.c
Line | Count | Source |
1 | | // Copyright (C) 2004-2025 Artifex Software, Inc. |
2 | | // |
3 | | // This file is part of MuPDF. |
4 | | // |
5 | | // MuPDF is free software: you can redistribute it and/or modify it under the |
6 | | // terms of the GNU Affero General Public License as published by the Free |
7 | | // Software Foundation, either version 3 of the License, or (at your option) |
8 | | // any later version. |
9 | | // |
10 | | // MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY |
11 | | // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
12 | | // FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more |
13 | | // details. |
14 | | // |
15 | | // You should have received a copy of the GNU Affero General Public License |
16 | | // along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html> |
17 | | // |
18 | | // Alternative licensing terms are available from the licensor. |
19 | | // For commercial licensing, see <https://www.artifex.com/> or contact |
20 | | // Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, |
21 | | // CA 94129, USA, for further information. |
22 | | |
23 | | #include "mupdf/fitz.h" |
24 | | #include "pdf-annot-imp.h" |
25 | | #include "mupdf/ucdn.h" |
26 | | |
27 | | #include <float.h> |
28 | | #include <limits.h> |
29 | | #include <string.h> |
30 | | #include <time.h> |
31 | | |
32 | | #include <stdio.h> |
33 | | |
34 | | #include "annotation-icons.h" |
35 | | |
36 | | /* #define PDF_DEBUG_APPEARANCE_SYNTHESIS */ |
37 | | |
38 | 0 | #define REPLACEMENT 0xB7 |
39 | 0 | #define CIRCLE_MAGIC 0.551915f |
40 | | |
41 | | static fz_point rect_center(const fz_rect rect) |
42 | 0 | { |
43 | 0 | fz_point c; |
44 | 0 | c.x = (rect.x0 + rect.x1) / 2.0f; |
45 | 0 | c.y = (rect.y0 + rect.y1) / 2.0f; |
46 | 0 | return c; |
47 | 0 | } |
48 | | |
49 | | static fz_matrix center_rect_within_rect(const fz_rect tofit, const fz_rect within) |
50 | 0 | { |
51 | 0 | float xscale = (within.x1 - within.x0) / (tofit.x1 - tofit.x0); |
52 | 0 | float yscale = (within.y1 - within.y0) / (tofit.y1 - tofit.y0); |
53 | 0 | float scale = fz_min(xscale, yscale); |
54 | 0 | fz_point tofit_center; |
55 | 0 | fz_point within_center; |
56 | |
|
57 | 0 | within_center = rect_center(within); |
58 | 0 | tofit_center = rect_center(tofit); |
59 | | |
60 | | /* Translate "tofit" to be centered on the origin |
61 | | * Scale "tofit" to a size that fits within "within" |
62 | | * Translate "tofit" to "within's" center |
63 | | * Do all the above in reverse order so that we can use the fz_pre_xx functions */ |
64 | 0 | return fz_pre_translate(fz_pre_scale(fz_translate(within_center.x, within_center.y), scale, -scale), -tofit_center.x, -tofit_center.y); |
65 | 0 | } |
66 | | |
67 | | static void |
68 | | draw_circle(fz_context *ctx, fz_buffer *buf, float rx, float ry, float cx, float cy) |
69 | 0 | { |
70 | 0 | float mx = rx * CIRCLE_MAGIC; |
71 | 0 | float my = ry * CIRCLE_MAGIC; |
72 | 0 | fz_append_printf(ctx, buf, "%g %g m\n", cx, cy+ry); |
73 | 0 | fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n", cx+mx, cy+ry, cx+rx, cy+my, cx+rx, cy); |
74 | 0 | fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n", cx+rx, cy-my, cx+mx, cy-ry, cx, cy-ry); |
75 | 0 | fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n", cx-mx, cy-ry, cx-rx, cy-my, cx-rx, cy); |
76 | 0 | fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n", cx-rx, cy+my, cx-mx, cy+ry, cx, cy+ry); |
77 | 0 | } |
78 | | |
79 | | static void |
80 | | draw_circle_in_box(fz_context *ctx, fz_buffer *buf, float lw, float x0, float y0, float x1, float y1) |
81 | 0 | { |
82 | 0 | float rx = (x1 - x0) / 2 - lw/2; |
83 | 0 | float ry = (y1 - y0) / 2 - lw/2; |
84 | 0 | float cx = x0 + lw/2 + rx; |
85 | 0 | float cy = y0 + lw/2 + ry; |
86 | 0 | draw_circle(ctx, buf, rx, ry, cx, cy); |
87 | 0 | } |
88 | | |
89 | | static void |
90 | | draw_arc_seg(fz_context *ctx, fz_buffer *buf, float r, float xc, float yc, float th0, float th1, int move) |
91 | 0 | { |
92 | 0 | float x1 = xc + r * cosf(th0); |
93 | 0 | float y1 = yc + r * sinf(th0); |
94 | 0 | float x4 = xc + r * cosf(th1); |
95 | 0 | float y4 = yc + r * sinf(th1); |
96 | |
|
97 | 0 | float ax = x1 - xc; |
98 | 0 | float ay = y1 - yc; |
99 | 0 | float bx = x4 - xc; |
100 | 0 | float by = y4 - yc; |
101 | 0 | float q1 = ax * ax + ay * ay; |
102 | 0 | float q2 = q1 + ax * bx + ay * by; |
103 | 0 | float k2 = (4.0f/3.0f) * (sqrtf(2 * q1 * q2) - q2) / (ax * by - ay * bx); |
104 | |
|
105 | 0 | float x2 = xc + ax - k2 * ay; |
106 | 0 | float y2 = yc + ay + k2 * ax; |
107 | 0 | float x3 = xc + bx + k2 * by; |
108 | 0 | float y3 = yc + by - k2 * bx; |
109 | |
|
110 | 0 | if (move) |
111 | 0 | fz_append_printf(ctx, buf, "%g %g m\n", x1, y1); |
112 | 0 | fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n", x2, y2, x3, y3, x4, y4); |
113 | 0 | } |
114 | | |
115 | | static void |
116 | | draw_arc(fz_context *ctx, fz_buffer *buf, float r, float xc, float yc, float th0, float th1, int move) |
117 | 0 | { |
118 | 0 | float d = th0 - th1; |
119 | 0 | if (d > FZ_PI / 4) |
120 | 0 | { |
121 | 0 | draw_arc(ctx, buf, r, xc, yc, th0, th0 - d / 2, move); |
122 | 0 | draw_arc(ctx, buf, r, xc, yc, th0 - d / 2, th1, 0); |
123 | 0 | } |
124 | 0 | else |
125 | 0 | { |
126 | 0 | draw_arc_seg(ctx, buf, r, xc, yc, th0, th1, move); |
127 | 0 | } |
128 | 0 | } |
129 | | |
130 | | static void |
131 | | draw_arc_tail(fz_context *ctx, fz_buffer *buf, float r, float xc, float yc, float th0, float th1, int fill) |
132 | 0 | { |
133 | 0 | draw_arc_seg(ctx, buf, r, xc, yc, th0, th1, 0); |
134 | 0 | if (fill) |
135 | 0 | draw_arc_seg(ctx, buf, r, xc, yc, th1, th0, 0); |
136 | 0 | } |
137 | | |
138 | | static void |
139 | | pdf_write_opacity_blend_mode(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, pdf_obj **res, int bm) |
140 | 0 | { |
141 | 0 | pdf_obj *res_egs, *res_egs_h; |
142 | 0 | float opacity = pdf_annot_opacity(ctx, annot); |
143 | |
|
144 | 0 | if (bm == FZ_BLEND_NORMAL && opacity == 1) |
145 | 0 | return; |
146 | | |
147 | | /* /Resources << /ExtGState << /H << /Type/ExtGState /BM/Multiply /CA %g /ca %g >> >> >> */ |
148 | | |
149 | 0 | if (!*res) |
150 | 0 | *res = pdf_new_dict(ctx, annot->page->doc, 1); |
151 | |
|
152 | 0 | res_egs = pdf_dict_put_dict(ctx, *res, PDF_NAME(ExtGState), 1); |
153 | 0 | res_egs_h = pdf_dict_put_dict(ctx, res_egs, PDF_NAME(H), 2); |
154 | 0 | pdf_dict_put(ctx, res_egs_h, PDF_NAME(Type), PDF_NAME(ExtGState)); |
155 | |
|
156 | 0 | if (bm == FZ_BLEND_MULTIPLY) |
157 | 0 | { |
158 | 0 | pdf_dict_put(ctx, res_egs_h, PDF_NAME(BM), PDF_NAME(Multiply)); |
159 | 0 | } |
160 | |
|
161 | 0 | if (opacity < 1) |
162 | 0 | { |
163 | 0 | pdf_dict_put_real(ctx, res_egs_h, PDF_NAME(CA), opacity); |
164 | 0 | pdf_dict_put_real(ctx, res_egs_h, PDF_NAME(ca), opacity); |
165 | 0 | } |
166 | |
|
167 | 0 | fz_append_printf(ctx, buf, "/H gs\n"); |
168 | 0 | } |
169 | | |
170 | | static void |
171 | | pdf_write_opacity(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, pdf_obj **res) |
172 | 0 | { |
173 | 0 | pdf_write_opacity_blend_mode(ctx, annot, buf, res, FZ_BLEND_NORMAL); |
174 | 0 | } |
175 | | |
176 | | static void |
177 | | pdf_write_dash_pattern(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, pdf_obj **res) |
178 | 0 | { |
179 | 0 | int count = pdf_annot_border_dash_count(ctx, annot); |
180 | 0 | int i; |
181 | |
|
182 | 0 | if (count == 0) |
183 | 0 | return; |
184 | | |
185 | 0 | fz_append_printf(ctx, buf, "["); |
186 | 0 | for (i = 0; i < count; ++i) |
187 | 0 | { |
188 | 0 | float length = pdf_annot_border_dash_item(ctx, annot, i); |
189 | 0 | if (i == 0) |
190 | 0 | fz_append_printf(ctx, buf, "%g", length); |
191 | 0 | else |
192 | 0 | fz_append_printf(ctx, buf, " %g", length); |
193 | 0 | } |
194 | 0 | fz_append_printf(ctx, buf, "]0 d\n"); |
195 | 0 | } |
196 | | |
197 | | static float pdf_write_border_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf) |
198 | 0 | { |
199 | 0 | float w = pdf_annot_border_width(ctx, annot); |
200 | 0 | fz_append_printf(ctx, buf, "%g w\n", w); |
201 | 0 | return w; |
202 | 0 | } |
203 | | |
204 | | static int |
205 | | write_color(fz_context *ctx, fz_buffer *buf, int n, float *color, int stroke) |
206 | 0 | { |
207 | 0 | if (n == 4) |
208 | 0 | fz_append_printf(ctx, buf, "%g %g %g %g %c\n", color[0], color[1], color[2], color[3], stroke ? 'K' : 'k'); |
209 | 0 | else if (n == 3) |
210 | 0 | fz_append_printf(ctx, buf, "%g %g %g %s\n", color[0], color[1], color[2], stroke ? "RG" : "rg"); |
211 | 0 | else if (n == 1) |
212 | 0 | fz_append_printf(ctx, buf, "%g %c\n", color[0], stroke ? 'G' : 'g'); |
213 | 0 | else |
214 | 0 | return 0; |
215 | 0 | return 1; |
216 | 0 | } |
217 | | |
218 | | static void |
219 | | write_color0(fz_context *ctx, fz_buffer *buf, int n, float *color, int stroke) |
220 | 0 | { |
221 | 0 | if (n == 0) |
222 | 0 | fz_append_printf(ctx, buf, "0 %c\n", stroke ? 'G' : 'g'); |
223 | 0 | else |
224 | 0 | write_color(ctx, buf, n, color, stroke); |
225 | 0 | } |
226 | | |
227 | | |
228 | | static int pdf_write_stroke_color_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf) |
229 | 0 | { |
230 | 0 | float color[4]; |
231 | 0 | int n; |
232 | 0 | pdf_annot_color(ctx, annot, &n, color); |
233 | 0 | return write_color(ctx, buf, n, color, 1); |
234 | 0 | } |
235 | | |
236 | | static int pdf_is_dark_fill_color(fz_context *ctx, pdf_annot *annot) |
237 | 0 | { |
238 | 0 | float color[4], gray; |
239 | 0 | int n; |
240 | 0 | pdf_annot_color(ctx, annot, &n, color); |
241 | 0 | switch (n) |
242 | 0 | { |
243 | 0 | default: |
244 | 0 | gray = 1; |
245 | 0 | break; |
246 | 0 | case 1: |
247 | 0 | gray = color[0]; |
248 | 0 | break; |
249 | 0 | case 3: |
250 | 0 | gray = color[0] * 0.3f + color[1] * 0.59f + color[2] * 0.11f; |
251 | 0 | break; |
252 | 0 | case 4: |
253 | 0 | gray = color[0] * 0.3f + color[1] * 0.59f + color[2] * 0.11f + color[3]; |
254 | 0 | gray = 1 - fz_min(gray, 1); |
255 | 0 | break; |
256 | 0 | } |
257 | 0 | return gray < 0.25f; |
258 | 0 | } |
259 | | |
260 | | static int pdf_write_fill_color_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf) |
261 | 0 | { |
262 | 0 | float color[4]; |
263 | 0 | int n; |
264 | 0 | pdf_annot_color(ctx, annot, &n, color); |
265 | 0 | return write_color(ctx, buf, n, color, 0); |
266 | 0 | } |
267 | | |
268 | | static int pdf_write_interior_fill_color_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf) |
269 | 0 | { |
270 | 0 | float color[4]; |
271 | 0 | int n; |
272 | 0 | pdf_annot_interior_color(ctx, annot, &n, color); |
273 | 0 | return write_color(ctx, buf, n, color, 0); |
274 | 0 | } |
275 | | |
276 | | static int pdf_write_MK_BG_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf) |
277 | 0 | { |
278 | 0 | float color[4]; |
279 | 0 | int n; |
280 | 0 | pdf_annot_MK_BG(ctx, annot, &n, color); |
281 | 0 | return write_color(ctx, buf, n, color, 0); |
282 | 0 | } |
283 | | |
284 | | static int pdf_write_MK_BC_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf) |
285 | 0 | { |
286 | 0 | float color[4]; |
287 | 0 | int n; |
288 | 0 | pdf_annot_MK_BC(ctx, annot, &n, color); |
289 | 0 | return write_color(ctx, buf, n, color, 1); |
290 | 0 | } |
291 | | |
292 | | static void maybe_stroke_and_fill(fz_context *ctx, fz_buffer *buf, int sc, int ic) |
293 | 0 | { |
294 | 0 | if (sc) |
295 | 0 | fz_append_string(ctx, buf, ic ? "b\n" : "S\n"); |
296 | 0 | else |
297 | 0 | fz_append_string(ctx, buf, ic ? "f\n" : "n\n"); |
298 | 0 | } |
299 | | |
300 | | static void maybe_stroke(fz_context *ctx, fz_buffer *buf, int sc) |
301 | 0 | { |
302 | 0 | fz_append_string(ctx, buf, sc ? "S\n" : "n\n"); |
303 | 0 | } |
304 | | |
305 | | static fz_point rotate_vector(float angle, float x, float y) |
306 | 0 | { |
307 | 0 | float ca = cosf(angle); |
308 | 0 | float sa = sinf(angle); |
309 | 0 | return fz_make_point(x*ca - y*sa, x*sa + y*ca); |
310 | 0 | } |
311 | | |
312 | | 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, int close) |
313 | 0 | { |
314 | 0 | float r = fz_max(1, w); |
315 | 0 | float angle = atan2f(dy, dx); |
316 | 0 | fz_point v, a, b; |
317 | |
|
318 | 0 | v = rotate_vector(angle, 8.8f*r, 4.5f*r); |
319 | 0 | a = fz_make_point(x + v.x, y + v.y); |
320 | 0 | v = rotate_vector(angle, 8.8f*r, -4.5f*r); |
321 | 0 | b = fz_make_point(x + v.x, y + v.y); |
322 | |
|
323 | 0 | *rect = fz_include_point_in_rect(*rect, a); |
324 | 0 | *rect = fz_include_point_in_rect(*rect, b); |
325 | 0 | *rect = fz_expand_rect(*rect, w); |
326 | |
|
327 | 0 | fz_append_printf(ctx, buf, "%g %g m\n", a.x, a.y); |
328 | 0 | fz_append_printf(ctx, buf, "%g %g l\n", x, y); |
329 | 0 | fz_append_printf(ctx, buf, "%g %g l\n", b.x, b.y); |
330 | 0 | if (close) |
331 | 0 | fz_append_printf(ctx, buf, "h\n"); |
332 | 0 | } |
333 | | |
334 | | static void include_cap(fz_rect *rect, float x, float y, float r) |
335 | 0 | { |
336 | 0 | rect->x0 = fz_min(rect->x0, x-r); |
337 | 0 | rect->y0 = fz_min(rect->y0, y-r); |
338 | 0 | rect->x1 = fz_max(rect->x1, x+r); |
339 | 0 | rect->y1 = fz_max(rect->y1, y+r); |
340 | 0 | } |
341 | | |
342 | | static void |
343 | | pdf_write_line_cap_appearance(fz_context *ctx, fz_buffer *buf, fz_rect *rect, |
344 | | float x, float y, float dx, float dy, float w, |
345 | | int sc, int ic, pdf_obj *cap) |
346 | 0 | { |
347 | 0 | float l = sqrtf(dx*dx + dy*dy); |
348 | 0 | dx = dx / l; |
349 | 0 | dy = dy / l; |
350 | |
|
351 | 0 | if (cap == PDF_NAME(Square)) |
352 | 0 | { |
353 | 0 | float r = fz_max(3.0f, w * 3.0f); |
354 | 0 | fz_append_printf(ctx, buf, "%g %g %g %g re\n", x-r, y-r, r*2, r*2); |
355 | 0 | maybe_stroke_and_fill(ctx, buf, sc, ic); |
356 | 0 | include_cap(rect, x, y, r + w/2); |
357 | 0 | } |
358 | 0 | else if (cap == PDF_NAME(Circle)) |
359 | 0 | { |
360 | 0 | float r = fz_max(3.0f, w * 3.0f); |
361 | 0 | float m = r * CIRCLE_MAGIC; |
362 | 0 | fz_append_printf(ctx, buf, "%g %g m\n", x, y+r); |
363 | 0 | fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n", x+m, y+r, x+r, y+m, x+r, y); |
364 | 0 | fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n", x+r, y-m, x+m, y-r, x, y-r); |
365 | 0 | fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n", x-m, y-r, x-r, y-m, x-r, y); |
366 | 0 | fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n", x-r, y+m, x-m, y+r, x, y+r); |
367 | 0 | maybe_stroke_and_fill(ctx, buf, sc, ic); |
368 | 0 | include_cap(rect, x, y, r + w/2); |
369 | 0 | } |
370 | 0 | else if (cap == PDF_NAME(Diamond)) |
371 | 0 | { |
372 | 0 | float r = fz_max(3.0f, w * 3.0f); |
373 | 0 | fz_append_printf(ctx, buf, "%g %g m\n", x, y+r); |
374 | 0 | fz_append_printf(ctx, buf, "%g %g l\n", x+r, y); |
375 | 0 | fz_append_printf(ctx, buf, "%g %g l\n", x, y-r); |
376 | 0 | fz_append_printf(ctx, buf, "%g %g l\n", x-r, y); |
377 | 0 | fz_append_printf(ctx, buf, "h\n"); |
378 | 0 | maybe_stroke_and_fill(ctx, buf, sc, ic); |
379 | 0 | include_cap(rect, x, y, r + w/sqrtf(2)); |
380 | 0 | } |
381 | 0 | else if (cap == PDF_NAME(OpenArrow)) |
382 | 0 | { |
383 | 0 | pdf_write_arrow_appearance(ctx, buf, rect, x, y, dx, dy, w, 0); |
384 | 0 | maybe_stroke(ctx, buf, sc); |
385 | 0 | } |
386 | 0 | else if (cap == PDF_NAME(ClosedArrow)) |
387 | 0 | { |
388 | 0 | pdf_write_arrow_appearance(ctx, buf, rect, x, y, dx, dy, w, 1); |
389 | 0 | maybe_stroke_and_fill(ctx, buf, sc, ic); |
390 | 0 | } |
391 | | /* PDF 1.5 */ |
392 | 0 | else if (cap == PDF_NAME(Butt)) |
393 | 0 | { |
394 | 0 | float r = fz_max(3, w * 3); |
395 | 0 | fz_point a = { x-dy*r, y+dx*r }; |
396 | 0 | fz_point b = { x+dy*r, y-dx*r }; |
397 | 0 | fz_append_printf(ctx, buf, "%g %g m\n", a.x, a.y); |
398 | 0 | fz_append_printf(ctx, buf, "%g %g l\n", b.x, b.y); |
399 | 0 | maybe_stroke(ctx, buf, sc); |
400 | 0 | *rect = fz_include_point_in_rect(*rect, a); |
401 | 0 | *rect = fz_include_point_in_rect(*rect, b); |
402 | 0 | *rect = fz_expand_rect(*rect, w); |
403 | 0 | } |
404 | 0 | else if (cap == PDF_NAME(ROpenArrow)) |
405 | 0 | { |
406 | 0 | pdf_write_arrow_appearance(ctx, buf, rect, x, y, -dx, -dy, w, 0); |
407 | 0 | maybe_stroke(ctx, buf, sc); |
408 | 0 | } |
409 | 0 | else if (cap == PDF_NAME(RClosedArrow)) |
410 | 0 | { |
411 | 0 | pdf_write_arrow_appearance(ctx, buf, rect, x, y, -dx, -dy, w, 1); |
412 | 0 | maybe_stroke_and_fill(ctx, buf, sc, ic); |
413 | 0 | } |
414 | | /* PDF 1.6 */ |
415 | 0 | else if (cap == PDF_NAME(Slash)) |
416 | 0 | { |
417 | 0 | float r = fz_max(5, w * 5); |
418 | 0 | float angle = atan2f(dy, dx) - (30 * FZ_PI / 180); |
419 | 0 | fz_point a, b, v; |
420 | 0 | v = rotate_vector(angle, 0, r); |
421 | 0 | a = fz_make_point(x + v.x, y + v.y); |
422 | 0 | v = rotate_vector(angle, 0, -r); |
423 | 0 | b = fz_make_point(x + v.x, y + v.y); |
424 | 0 | fz_append_printf(ctx, buf, "%g %g m\n", a.x, a.y); |
425 | 0 | fz_append_printf(ctx, buf, "%g %g l\n", b.x, b.y); |
426 | 0 | maybe_stroke(ctx, buf, sc); |
427 | 0 | *rect = fz_include_point_in_rect(*rect, a); |
428 | 0 | *rect = fz_include_point_in_rect(*rect, b); |
429 | 0 | *rect = fz_expand_rect(*rect, w); |
430 | 0 | } |
431 | 0 | } |
432 | | |
433 | | static float pdf_write_line_caption(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, pdf_obj **res, fz_point a, fz_point b); |
434 | | |
435 | | static void |
436 | | pdf_write_line_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, pdf_obj **res) |
437 | 0 | { |
438 | 0 | pdf_obj *line, *le; |
439 | 0 | float w; |
440 | 0 | fz_point a, b, aa, ab, ba, bb; |
441 | 0 | float dx, dy, line_length, ll, lle, llo; |
442 | 0 | int sc; |
443 | 0 | int ic; |
444 | | |
445 | | // The start and end point of the actual line (a, b) |
446 | | // The start and end points of the leader line (aa, ab, ba, bb) |
447 | |
|
448 | 0 | pdf_write_opacity(ctx, annot, buf, res); |
449 | 0 | pdf_write_dash_pattern(ctx, annot, buf, res); |
450 | 0 | w = pdf_write_border_appearance(ctx, annot, buf); |
451 | 0 | sc = pdf_write_stroke_color_appearance(ctx, annot, buf); |
452 | 0 | ic = pdf_write_interior_fill_color_appearance(ctx, annot, buf); |
453 | |
|
454 | 0 | line = pdf_dict_get(ctx, annot->obj, PDF_NAME(L)); |
455 | 0 | a.x = pdf_array_get_real(ctx, line, 0); |
456 | 0 | a.y = pdf_array_get_real(ctx, line, 1); |
457 | 0 | b.x = pdf_array_get_real(ctx, line, 2); |
458 | 0 | b.y = pdf_array_get_real(ctx, line, 3); |
459 | | |
460 | | /* vector of line */ |
461 | 0 | dx = b.x - a.x; |
462 | 0 | dy = b.y - a.y; |
463 | 0 | line_length = hypotf(dx, dy); |
464 | 0 | dx /= line_length; |
465 | 0 | dy /= line_length; |
466 | |
|
467 | 0 | rect->x0 = fz_min(a.x, b.x); |
468 | 0 | rect->y0 = fz_min(a.y, b.y); |
469 | 0 | rect->x1 = fz_max(a.x, b.x); |
470 | 0 | rect->y1 = fz_max(a.y, b.y); |
471 | |
|
472 | 0 | ll = pdf_dict_get_real(ctx, annot->obj, PDF_NAME(LL)); |
473 | |
|
474 | 0 | if (ll != 0) |
475 | 0 | { |
476 | 0 | lle = pdf_dict_get_real(ctx, annot->obj, PDF_NAME(LLE)); |
477 | 0 | llo = pdf_dict_get_real(ctx, annot->obj, PDF_NAME(LLO)); |
478 | 0 | if (ll < 0) { |
479 | 0 | lle = -lle; |
480 | 0 | llo = -llo; |
481 | 0 | } |
482 | |
|
483 | 0 | aa = fz_make_point(a.x - dy * (llo), a.y + dx * (llo)); |
484 | 0 | ba = fz_make_point(b.x - dy * (llo), b.y + dx * (llo)); |
485 | 0 | ab = fz_make_point(a.x - dy * (llo + ll + lle), a.y + dx * (llo + ll + lle)); |
486 | 0 | bb = fz_make_point(b.x - dy * (llo + ll + lle), b.y + dx * (llo + ll + lle)); |
487 | 0 | a = fz_make_point(a.x - dy * (llo + ll), a.y + dx * (llo + ll)); |
488 | 0 | b = fz_make_point(b.x - dy * (llo + ll), b.y + dx * (llo + ll)); |
489 | |
|
490 | 0 | fz_append_printf(ctx, buf, "%g %g m\n%g %g l\n", aa.x, aa.y, ab.x, ab.y); |
491 | 0 | fz_append_printf(ctx, buf, "%g %g m\n%g %g l\n", ba.x, ba.y, bb.x, bb.y); |
492 | |
|
493 | 0 | *rect = fz_include_point_in_rect(*rect, a); |
494 | 0 | *rect = fz_include_point_in_rect(*rect, b); |
495 | 0 | *rect = fz_include_point_in_rect(*rect, ab); |
496 | 0 | *rect = fz_include_point_in_rect(*rect, bb); |
497 | 0 | } |
498 | |
|
499 | 0 | if (pdf_dict_get_bool(ctx, annot->obj, PDF_NAME(Cap))) |
500 | 0 | { |
501 | 0 | float gap = pdf_write_line_caption(ctx, annot, buf, rect, res, a, b); |
502 | 0 | if (gap > 0) |
503 | 0 | { |
504 | 0 | fz_point m = fz_make_point((a.x + b.x) / 2, (a.y + b.y) / 2); |
505 | 0 | fz_point ca = fz_make_point(m.x - dx * gap, m.y - dy * gap); |
506 | 0 | fz_point cb = fz_make_point(m.x + dx * gap, m.y + dy * gap); |
507 | 0 | fz_append_printf(ctx, buf, "%g %g m\n%g %g l\n", a.x, a.y, ca.x, ca.y); |
508 | 0 | fz_append_printf(ctx, buf, "%g %g m\n%g %g l\n", cb.x, cb.y, b.x, b.y); |
509 | 0 | } |
510 | 0 | else |
511 | 0 | { |
512 | 0 | fz_append_printf(ctx, buf, "%g %g m\n%g %g l\n", a.x, a.y, b.x, b.y); |
513 | 0 | } |
514 | 0 | } |
515 | 0 | else |
516 | 0 | { |
517 | 0 | fz_append_printf(ctx, buf, "%g %g m\n%g %g l\n", a.x, a.y, b.x, b.y); |
518 | 0 | } |
519 | |
|
520 | 0 | maybe_stroke(ctx, buf, sc); |
521 | |
|
522 | 0 | le = pdf_dict_get(ctx, annot->obj, PDF_NAME(LE)); |
523 | 0 | if (pdf_array_len(ctx, le) == 2) |
524 | 0 | { |
525 | 0 | dx = b.x - a.x; |
526 | 0 | dy = b.y - a.y; |
527 | 0 | pdf_write_line_cap_appearance(ctx, buf, rect, a.x, a.y, dx, dy, w, sc, ic, pdf_array_get(ctx, le, 0)); |
528 | 0 | pdf_write_line_cap_appearance(ctx, buf, rect, b.x, b.y, -dx, -dy, w, sc, ic, pdf_array_get(ctx, le, 1)); |
529 | 0 | } |
530 | 0 | *rect = fz_expand_rect(*rect, fz_max(1, w)); |
531 | 0 | } |
532 | | |
533 | | /* The rect diff is NOT an fz_rect. It's differences between |
534 | | * 2 rects. We return it as a rect for convenience. */ |
535 | | fz_rect |
536 | | pdf_annot_rect_diff(fz_context *ctx, pdf_annot *annot) |
537 | 0 | { |
538 | 0 | pdf_obj *rd_obj = pdf_dict_get(ctx, annot->obj, PDF_NAME(RD)); |
539 | 0 | fz_rect rd; |
540 | |
|
541 | 0 | if (!pdf_is_array(ctx, rd_obj)) |
542 | 0 | return fz_make_rect(0, 0, 0, 0); |
543 | | |
544 | 0 | rd.x0 = pdf_array_get_real(ctx, rd_obj, 0); |
545 | 0 | rd.y0 = pdf_array_get_real(ctx, rd_obj, 1); |
546 | 0 | rd.x1 = pdf_array_get_real(ctx, rd_obj, 2); |
547 | 0 | rd.y1 = pdf_array_get_real(ctx, rd_obj, 3); |
548 | |
|
549 | 0 | return rd; |
550 | 0 | } |
551 | | |
552 | | static float |
553 | | cloud_intensity(fz_context *ctx, pdf_annot *annot) |
554 | 0 | { |
555 | 0 | if (pdf_annot_border_effect(ctx, annot) == PDF_BORDER_EFFECT_CLOUDY) |
556 | 0 | return pdf_annot_border_effect_intensity(ctx, annot); |
557 | 0 | return 0; |
558 | 0 | } |
559 | | |
560 | | struct cloud_list { |
561 | | // store first 2 and latest 3 points |
562 | | fz_point data[5]; |
563 | | int len; |
564 | | int first; |
565 | | int fill; |
566 | | float spacing, radius, phase; |
567 | | }; |
568 | | |
569 | | static float intersect_cloud(fz_point p0, fz_point p1, float r, int sel) |
570 | 0 | { |
571 | 0 | float dx = p1.x - p0.x; |
572 | 0 | float dy = p1.y - p0.y; |
573 | 0 | float d = sqrtf(dx * dx + dy * dy); |
574 | 0 | float x2, y2, x3, y3; |
575 | 0 | float a, h; |
576 | |
|
577 | 0 | if (d >= r + r) return 0; |
578 | 0 | if (d <= 0) return 0; |
579 | | |
580 | 0 | a = d / 2; |
581 | 0 | h = sqrtf(r * r - a * a); |
582 | |
|
583 | 0 | x2 = (p0.x + p1.x) / 2; |
584 | 0 | y2 = (p0.y + p1.y) / 2; |
585 | |
|
586 | 0 | x3 = x2 - h * (p1.y - p0.y) / d; |
587 | 0 | y3 = y2 + h * (p1.x - p0.x) / d; |
588 | |
|
589 | 0 | if (sel == 0) |
590 | 0 | return atan2(y3 - p1.y, x3 - p1.x); |
591 | 0 | else |
592 | 0 | return atan2(y3 - p0.y, x3 - p0.x); |
593 | 0 | } |
594 | | |
595 | | static void start_cloud(fz_context *ctx, struct cloud_list *list, fz_buffer *buf, float border, float intensity, int fill) |
596 | 0 | { |
597 | | // Constants measured from Acrobat Reader |
598 | 0 | list->spacing = intensity * 6.666667f + border * 0.8333333f; |
599 | 0 | list->radius = intensity * 4.0f + border * 0.5f; |
600 | 0 | list->phase = 0; |
601 | 0 | list->len = 0; |
602 | 0 | list->first = 1; |
603 | 0 | list->fill = fill; |
604 | 0 | fz_append_string(ctx, buf, "2 j\n"); // bevel join |
605 | 0 | } |
606 | | |
607 | | static void emit_cloud(fz_context *ctx, struct cloud_list *list, fz_buffer *buf, fz_point a, fz_point b, fz_point c) |
608 | 0 | { |
609 | 0 | float th0 = intersect_cloud(a, b, list->radius, 0); |
610 | 0 | float th1 = intersect_cloud(b, c, list->radius, 1); |
611 | 0 | while (th1 > th0) |
612 | 0 | th1 -= FZ_PI * 2; |
613 | 0 | draw_arc(ctx, buf, list->radius, b.x, b.y, th0, th1, list->first || !list->fill); |
614 | 0 | draw_arc_tail(ctx, buf, list->radius, b.x, b.y, th1, th1 - FZ_PI / 8, list->fill); |
615 | 0 | list->first = 0; |
616 | 0 | } |
617 | | |
618 | | static void add_cloud(fz_context *ctx, struct cloud_list *list, fz_buffer *buf, float x, float y) |
619 | 0 | { |
620 | 0 | if (list->len < 5) |
621 | 0 | { |
622 | 0 | list->data[list->len].x = x; |
623 | 0 | list->data[list->len].y = y; |
624 | 0 | list->len++; |
625 | 0 | } |
626 | 0 | else |
627 | 0 | { |
628 | 0 | list->data[2] = list->data[3]; |
629 | 0 | list->data[3] = list->data[4]; |
630 | 0 | list->data[4].x = x; |
631 | 0 | list->data[4].y = y; |
632 | 0 | } |
633 | 0 | if (list->len >= 3) |
634 | 0 | { |
635 | 0 | emit_cloud(ctx, list, buf, list->data[list->len-3], list->data[list->len-2], list->data[list->len-1]); |
636 | 0 | } |
637 | 0 | } |
638 | | |
639 | | static void add_cloud_line(fz_context *ctx, struct cloud_list *list, fz_buffer *buf, float x0, float y0, float x1, float y1) |
640 | 0 | { |
641 | 0 | float dx = x1 - x0; |
642 | 0 | float dy = y1 - y0; |
643 | 0 | float total = hypotf(dx, dy); |
644 | 0 | float used = 0; |
645 | 0 | float t; |
646 | |
|
647 | 0 | if (list->phase == 0) |
648 | 0 | add_cloud(ctx, list, buf, x0, y0); |
649 | |
|
650 | 0 | while (total - used > list->spacing - list->phase) |
651 | 0 | { |
652 | 0 | used += list->spacing - list->phase; |
653 | 0 | t = used / total; |
654 | 0 | add_cloud(ctx, list, buf, x0 + dx * t, y0 + dy * t); |
655 | 0 | list->phase = 0; |
656 | 0 | } |
657 | |
|
658 | 0 | list->phase += total - used; |
659 | 0 | } |
660 | | |
661 | | static void add_cloud_circle(fz_context *ctx, struct cloud_list *list, fz_buffer *buf, float x0, float y0, float x1, float y1) |
662 | 0 | { |
663 | 0 | float cx = (x0 + x1) / 2; |
664 | 0 | float cy = (y0 + y1) / 2; |
665 | 0 | float rx = (x1 - x0) / 2; |
666 | 0 | float ry = (y1 - y0) / 2; |
667 | 0 | float da = -FZ_PI * 2 / 32; |
668 | 0 | int i; |
669 | 0 | for (i = 1; i <= 32; ++i) { |
670 | 0 | float ax = cx + cosf((i-1) * da) * rx; |
671 | 0 | float ay = cy + sinf((i-1) * da) * ry; |
672 | 0 | float bx = cx + cosf(i * da) * rx; |
673 | 0 | float by = cy + sinf(i * da) * ry; |
674 | 0 | add_cloud_line(ctx, list, buf, ax, ay, bx, by); |
675 | 0 | } |
676 | 0 | } |
677 | | |
678 | | static void end_cloud(fz_context *ctx, struct cloud_list *list, fz_buffer *buf) |
679 | 0 | { |
680 | | // Join up with last and first circles. |
681 | 0 | switch (list->len) |
682 | 0 | { |
683 | 0 | case 0: |
684 | 0 | break; |
685 | 0 | case 1: |
686 | 0 | draw_circle(ctx, buf, list->radius, list->radius, list->data[0].x, list->data[0].y); |
687 | 0 | break; |
688 | 0 | case 2: |
689 | 0 | emit_cloud(ctx, list, buf, list->data[0], list->data[1], list->data[0]); |
690 | 0 | emit_cloud(ctx, list, buf, list->data[1], list->data[0], list->data[1]); |
691 | 0 | break; |
692 | 0 | case 3: |
693 | 0 | emit_cloud(ctx, list, buf, list->data[1], list->data[2], list->data[0]); |
694 | 0 | emit_cloud(ctx, list, buf, list->data[2], list->data[0], list->data[1]); |
695 | 0 | break; |
696 | 0 | case 4: |
697 | 0 | emit_cloud(ctx, list, buf, list->data[2], list->data[3], list->data[0]); |
698 | 0 | emit_cloud(ctx, list, buf, list->data[3], list->data[0], list->data[1]); |
699 | 0 | break; |
700 | 0 | case 5: |
701 | 0 | emit_cloud(ctx, list, buf, list->data[3], list->data[4], list->data[0]); |
702 | 0 | emit_cloud(ctx, list, buf, list->data[4], list->data[0], list->data[1]); |
703 | 0 | break; |
704 | 0 | } |
705 | 0 | } |
706 | | |
707 | | static void |
708 | | pdf_write_square_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, pdf_obj **res) |
709 | 0 | { |
710 | 0 | struct cloud_list cloud_list; |
711 | 0 | fz_rect orect, rd; |
712 | 0 | float x, y, w, h; |
713 | 0 | float lw; |
714 | 0 | int sc; |
715 | 0 | int ic; |
716 | 0 | float cloud; |
717 | 0 | float exp; |
718 | |
|
719 | 0 | pdf_write_opacity(ctx, annot, buf, res); |
720 | 0 | pdf_write_dash_pattern(ctx, annot, buf, res); |
721 | 0 | lw = pdf_write_border_appearance(ctx, annot, buf); |
722 | 0 | sc = pdf_write_stroke_color_appearance(ctx, annot, buf); |
723 | 0 | ic = pdf_write_interior_fill_color_appearance(ctx, annot, buf); |
724 | 0 | orect = pdf_dict_get_rect(ctx, annot->obj, PDF_NAME(Rect)); |
725 | 0 | rd = pdf_annot_rect_diff(ctx, annot); |
726 | | |
727 | | /* We have various rules that we need to follow here: |
728 | | * 1) No part of what we draw should extend outside of 'Rect'. |
729 | | * 2) The 'centre' of the border should be on 'Rect+RD'. |
730 | | * 3) RD and linewidth will therefore have problems if they are too large. |
731 | | * We do our best to cope with all of these. |
732 | | */ |
733 | |
|
734 | 0 | exp = lw/2; |
735 | 0 | if (rd.x0 < exp) |
736 | 0 | rd.x0 = exp; |
737 | 0 | if (rd.x1 < exp) |
738 | 0 | rd.x1 = exp; |
739 | 0 | if (rd.y0 < exp) |
740 | 0 | rd.y0 = exp; |
741 | 0 | if (rd.y1 < exp) |
742 | 0 | rd.y1 = exp; |
743 | |
|
744 | 0 | x = orect.x0 + rd.x0; |
745 | 0 | y = orect.y0 + rd.y0; |
746 | 0 | w = orect.x1 - orect.x0 - rd.x0 - rd.x1; |
747 | 0 | h = orect.y1 - orect.y0 - rd.y0 - rd.y1; |
748 | |
|
749 | 0 | if (w < 1) w = 1; |
750 | 0 | if (h < 1) h = 1; |
751 | |
|
752 | 0 | cloud = cloud_intensity(ctx, annot); |
753 | 0 | if (cloud > 0) |
754 | 0 | { |
755 | 0 | start_cloud(ctx, &cloud_list, buf, lw, cloud, ic); |
756 | |
|
757 | 0 | add_cloud_line(ctx, &cloud_list, buf, x, y, x, y+h); |
758 | 0 | cloud_list.phase = 0; |
759 | 0 | add_cloud_line(ctx, &cloud_list, buf, x, y+h, x+w, y+h); |
760 | 0 | cloud_list.phase = 0; |
761 | 0 | add_cloud_line(ctx, &cloud_list, buf, x+w, y+h, x+w, y); |
762 | 0 | cloud_list.phase = 0; |
763 | 0 | add_cloud_line(ctx, &cloud_list, buf, x+w, y, x, y); |
764 | |
|
765 | 0 | end_cloud(ctx, &cloud_list, buf); |
766 | 0 | exp += cloud_list.radius; |
767 | |
|
768 | 0 | if (rd.x0 < exp) |
769 | 0 | rd.x0 = exp; |
770 | 0 | if (rd.x1 < exp) |
771 | 0 | rd.x1 = exp; |
772 | 0 | if (rd.y0 < exp) |
773 | 0 | rd.y0 = exp; |
774 | 0 | if (rd.y1 < exp) |
775 | 0 | rd.y1 = exp; |
776 | 0 | } |
777 | 0 | else |
778 | 0 | { |
779 | 0 | fz_append_printf(ctx, buf, "%g %g %g %g re\n", x, y, w, h); |
780 | 0 | } |
781 | 0 | maybe_stroke_and_fill(ctx, buf, sc, ic); |
782 | |
|
783 | 0 | pdf_dict_put_rect(ctx, annot->obj, PDF_NAME(RD), rd); |
784 | 0 | rect->x0 = x - rd.x0; |
785 | 0 | rect->y0 = y - rd.y0; |
786 | 0 | rect->x1 = x + w + rd.x1; |
787 | 0 | rect->y1 = y + h + rd.y1; |
788 | 0 | } |
789 | | |
790 | | static void |
791 | | pdf_write_circle_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, pdf_obj **res) |
792 | 0 | { |
793 | 0 | struct cloud_list cloud_list; |
794 | 0 | fz_rect orect, rd; |
795 | 0 | float x, y, w, h; |
796 | 0 | float lw; |
797 | 0 | int sc; |
798 | 0 | int ic; |
799 | 0 | float cloud; |
800 | 0 | float exp; |
801 | |
|
802 | 0 | pdf_write_opacity(ctx, annot, buf, res); |
803 | 0 | pdf_write_dash_pattern(ctx, annot, buf, res); |
804 | 0 | lw = pdf_write_border_appearance(ctx, annot, buf); |
805 | 0 | sc = pdf_write_stroke_color_appearance(ctx, annot, buf); |
806 | 0 | ic = pdf_write_interior_fill_color_appearance(ctx, annot, buf); |
807 | 0 | orect = pdf_dict_get_rect(ctx, annot->obj, PDF_NAME(Rect)); |
808 | 0 | rd = pdf_annot_rect_diff(ctx, annot); |
809 | | |
810 | | /* We have various rules that we need to follow here: |
811 | | * 1) No part of what we draw should extend outside of 'Rect'. |
812 | | * 2) The 'centre' of the border should be on 'Rect+RD'. |
813 | | * 3) RD and linewidth will therefore have problems if they are too large. |
814 | | * We do our best to cope with all of these. |
815 | | */ |
816 | |
|
817 | 0 | exp = lw/2; |
818 | 0 | if (rd.x0 < exp) |
819 | 0 | rd.x0 = exp; |
820 | 0 | if (rd.x1 < exp) |
821 | 0 | rd.x1 = exp; |
822 | 0 | if (rd.y0 < exp) |
823 | 0 | rd.y0 = exp; |
824 | 0 | if (rd.y1 < exp) |
825 | 0 | rd.y1 = exp; |
826 | |
|
827 | 0 | x = orect.x0 + rd.x0; |
828 | 0 | y = orect.y0 + rd.y0; |
829 | 0 | w = orect.x1 - orect.x0 - rd.x0 - rd.x1; |
830 | 0 | h = orect.y1 - orect.y0 - rd.y0 - rd.y1; |
831 | |
|
832 | 0 | if (w < 1) w = 1; |
833 | 0 | if (h < 1) h = 1; |
834 | |
|
835 | 0 | cloud = cloud_intensity(ctx, annot); |
836 | 0 | if (cloud > 0) |
837 | 0 | { |
838 | 0 | start_cloud(ctx, &cloud_list, buf, lw, cloud, ic); |
839 | 0 | add_cloud_circle(ctx, &cloud_list, buf, x, y, x+w, y+h); |
840 | 0 | end_cloud(ctx, &cloud_list, buf); |
841 | 0 | exp += cloud_list.radius; |
842 | |
|
843 | 0 | if (rd.x0 < exp) |
844 | 0 | rd.x0 = exp; |
845 | 0 | if (rd.x1 < exp) |
846 | 0 | rd.x1 = exp; |
847 | 0 | if (rd.y0 < exp) |
848 | 0 | rd.y0 = exp; |
849 | 0 | if (rd.y1 < exp) |
850 | 0 | rd.y1 = exp; |
851 | 0 | } |
852 | 0 | else |
853 | 0 | { |
854 | 0 | draw_circle(ctx, buf, w / 2, h / 2, x + w / 2, y + h / 2); |
855 | 0 | } |
856 | 0 | maybe_stroke_and_fill(ctx, buf, sc, ic); |
857 | |
|
858 | 0 | pdf_dict_put_rect(ctx, annot->obj, PDF_NAME(RD), rd); |
859 | 0 | rect->x0 = x - rd.x0; |
860 | 0 | rect->y0 = y - rd.x1; |
861 | 0 | rect->x1 = x + w + rd.x1; |
862 | 0 | rect->y1 = y + h + rd.y1; |
863 | 0 | } |
864 | | |
865 | | /* |
866 | | * This doesn't quite match Acrobat, but I haven't really got a clue |
867 | | * what magic algorithm Acrobat is using. It'll match for all 'simple' |
868 | | * polygons, and makes a reasonable stab for complex ones. |
869 | | * |
870 | | * Find the y position of the centre of the polygon. Sum the |
871 | | * area * winding_number across that line. (Effectively an integration?) |
872 | | * Then pick cw or ccw according to what will be leave the largest |
873 | | * area correct. |
874 | | */ |
875 | | static int |
876 | | polygon_winding(fz_context *ctx, pdf_obj *v, int n) |
877 | 0 | { |
878 | 0 | int i; |
879 | 0 | float mid_y = 0; |
880 | 0 | float min_x = 0; |
881 | 0 | fz_point q, r; |
882 | 0 | float area = 0; |
883 | | |
884 | | /* Find the centre for y, and min x. */ |
885 | 0 | for (i = 0; i < n; i++) |
886 | 0 | { |
887 | 0 | float x = pdf_array_get_real(ctx, v, 2*i); |
888 | 0 | if (x < min_x || i == 0) |
889 | 0 | min_x = x; |
890 | 0 | mid_y += pdf_array_get_real(ctx, v, 2*i+1); |
891 | 0 | } |
892 | 0 | mid_y /= n; |
893 | | |
894 | | /* Now run through finding the weighted area across that middle line. */ |
895 | 0 | q.x = pdf_array_get_real(ctx, v, 0); |
896 | 0 | q.y = pdf_array_get_real(ctx, v, 1); |
897 | 0 | for (i = 1; i < n; i++) |
898 | 0 | { |
899 | 0 | r.x = pdf_array_get_real(ctx, v, 2*i); |
900 | 0 | r.y = pdf_array_get_real(ctx, v, 2*i+1); |
901 | 0 | if ((r.y > mid_y && mid_y > q.y) || (r.y < mid_y && mid_y < q.y)) |
902 | 0 | { |
903 | 0 | int w = (r.y > mid_y) ? 1 : -1; |
904 | 0 | float x = r.x + (q.x - r.x) * (r.y - mid_y) / (r.y - q.y); |
905 | |
|
906 | 0 | area += (x - min_x) * w; |
907 | 0 | } |
908 | 0 | q = r; |
909 | 0 | } |
910 | |
|
911 | 0 | return area < 0; |
912 | 0 | } |
913 | | |
914 | | static void |
915 | | pdf_write_polygon_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, pdf_obj **res, int close) |
916 | 0 | { |
917 | 0 | struct cloud_list cloud_list; |
918 | 0 | float cloud = 0; |
919 | 0 | pdf_obj *verts, *le; |
920 | 0 | fz_point p, first = {0,0}, last = {0,0}; |
921 | 0 | int i, n; |
922 | 0 | float lw; |
923 | 0 | int sc, ic; |
924 | 0 | int i0, i1, is; |
925 | 0 | float exp = 0; |
926 | |
|
927 | 0 | pdf_write_opacity(ctx, annot, buf, res); |
928 | 0 | pdf_write_dash_pattern(ctx, annot, buf, res); |
929 | 0 | lw = pdf_write_border_appearance(ctx, annot, buf); |
930 | 0 | sc = pdf_write_stroke_color_appearance(ctx, annot, buf); |
931 | 0 | ic = pdf_write_interior_fill_color_appearance(ctx, annot, buf); |
932 | |
|
933 | 0 | *rect = fz_empty_rect; |
934 | |
|
935 | 0 | if (close) |
936 | 0 | cloud = cloud_intensity(ctx, annot); |
937 | |
|
938 | 0 | verts = pdf_dict_get(ctx, annot->obj, PDF_NAME(Vertices)); |
939 | 0 | n = pdf_array_len(ctx, verts) / 2; |
940 | 0 | if (n > 0) |
941 | 0 | { |
942 | 0 | if (polygon_winding(ctx, verts, n)) |
943 | 0 | i0 = 0, i1 = n, is = 1; |
944 | 0 | else |
945 | 0 | i0 = n-1, i1 = -1, is = -1; |
946 | |
|
947 | 0 | if (cloud > 0) |
948 | 0 | { |
949 | 0 | start_cloud(ctx, &cloud_list, buf, lw, cloud, ic); |
950 | 0 | } |
951 | |
|
952 | 0 | for (i = i0; i != i1; i += is) |
953 | 0 | { |
954 | 0 | p.x = pdf_array_get_real(ctx, verts, i*2+0); |
955 | 0 | p.y = pdf_array_get_real(ctx, verts, i*2+1); |
956 | 0 | if (i == i0) |
957 | 0 | { |
958 | 0 | rect->x0 = rect->x1 = p.x; |
959 | 0 | rect->y0 = rect->y1 = p.y; |
960 | 0 | } |
961 | 0 | else |
962 | 0 | { |
963 | 0 | *rect = fz_include_point_in_rect(*rect, p); |
964 | 0 | } |
965 | 0 | if (cloud > 0) |
966 | 0 | { |
967 | 0 | if (i == i0) |
968 | 0 | first = p; |
969 | 0 | else |
970 | 0 | add_cloud_line(ctx, &cloud_list, buf, last.x, last.y, p.x, p.y); |
971 | 0 | last = p; |
972 | 0 | } |
973 | 0 | else |
974 | 0 | { |
975 | 0 | if (i == i0) |
976 | 0 | fz_append_printf(ctx, buf, "%g %g m\n", p.x, p.y); |
977 | 0 | else |
978 | 0 | fz_append_printf(ctx, buf, "%g %g l\n", p.x, p.y); |
979 | 0 | } |
980 | 0 | } |
981 | |
|
982 | 0 | if (cloud > 0) |
983 | 0 | { |
984 | 0 | add_cloud_line(ctx, &cloud_list, buf, last.x, last.y, first.x, first.y); |
985 | 0 | end_cloud(ctx, &cloud_list, buf); |
986 | 0 | } |
987 | 0 | else |
988 | 0 | { |
989 | 0 | if (close) |
990 | 0 | fz_append_string(ctx, buf, "h\n"); |
991 | 0 | } |
992 | |
|
993 | 0 | if (close) |
994 | 0 | maybe_stroke_and_fill(ctx, buf, sc, ic); |
995 | 0 | else |
996 | 0 | maybe_stroke(ctx, buf, sc); |
997 | |
|
998 | 0 | exp = lw; |
999 | 0 | if (cloud > 0) |
1000 | 0 | exp += cloud_list.radius; |
1001 | |
|
1002 | 0 | *rect = fz_expand_rect(*rect, exp); |
1003 | 0 | } |
1004 | |
|
1005 | 0 | le = pdf_dict_get(ctx, annot->obj, PDF_NAME(LE)); |
1006 | 0 | if (!close && n >= 2 && pdf_array_len(ctx, le) == 2) |
1007 | 0 | { |
1008 | 0 | float dx, dy; |
1009 | 0 | fz_point a, b; |
1010 | |
|
1011 | 0 | a.x = pdf_array_get_real(ctx, verts, 0*2+0); |
1012 | 0 | a.y = pdf_array_get_real(ctx, verts, 0*2+1); |
1013 | 0 | b.x = pdf_array_get_real(ctx, verts, 1*2+0); |
1014 | 0 | b.y = pdf_array_get_real(ctx, verts, 1*2+1); |
1015 | |
|
1016 | 0 | dx = b.x - a.x; |
1017 | 0 | dy = b.y - a.y; |
1018 | |
|
1019 | 0 | pdf_write_line_cap_appearance(ctx, buf, rect, a.x, a.y, dx, dy, lw, sc, ic, pdf_array_get(ctx, le, 0)); |
1020 | |
|
1021 | 0 | a.x = pdf_array_get_real(ctx, verts, (n-1)*2+0); |
1022 | 0 | a.y = pdf_array_get_real(ctx, verts, (n-1)*2+1); |
1023 | 0 | b.x = pdf_array_get_real(ctx, verts, (n-2)*2+0); |
1024 | 0 | b.y = pdf_array_get_real(ctx, verts, (n-2)*2+1); |
1025 | |
|
1026 | 0 | dx = b.x - a.x; |
1027 | 0 | dy = b.y - a.y; |
1028 | |
|
1029 | 0 | pdf_write_line_cap_appearance(ctx, buf, rect, a.x, a.y, dx, dy, lw, sc, ic, pdf_array_get(ctx, le, 1)); |
1030 | 0 | } |
1031 | |
|
1032 | 0 | if (exp == 0) |
1033 | 0 | pdf_dict_del(ctx, annot->obj, PDF_NAME(RD)); |
1034 | 0 | else |
1035 | 0 | pdf_dict_put_rect(ctx, annot->obj, PDF_NAME(RD), fz_make_rect(exp, exp, exp, exp)); |
1036 | 0 | } |
1037 | | |
1038 | | static void |
1039 | | pdf_write_ink_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, pdf_obj **res) |
1040 | 0 | { |
1041 | 0 | pdf_obj *ink_list, *stroke; |
1042 | 0 | int i, n, k, m; |
1043 | 0 | float lw; |
1044 | 0 | fz_point p; |
1045 | 0 | int sc; |
1046 | |
|
1047 | 0 | pdf_write_opacity(ctx, annot, buf, res); |
1048 | 0 | pdf_write_dash_pattern(ctx, annot, buf, res); |
1049 | 0 | lw = pdf_write_border_appearance(ctx, annot, buf); |
1050 | 0 | sc = pdf_write_stroke_color_appearance(ctx, annot, buf); |
1051 | |
|
1052 | 0 | *rect = fz_empty_rect; |
1053 | |
|
1054 | 0 | fz_append_printf(ctx, buf, "1 J\n1 j\n"); |
1055 | |
|
1056 | 0 | ink_list = pdf_dict_get(ctx, annot->obj, PDF_NAME(InkList)); |
1057 | 0 | n = pdf_array_len(ctx, ink_list); |
1058 | 0 | for (i = 0; i < n; ++i) |
1059 | 0 | { |
1060 | 0 | stroke = pdf_array_get(ctx, ink_list, i); |
1061 | 0 | m = pdf_array_len(ctx, stroke) / 2; |
1062 | 0 | for (k = 0; k < m; ++k) |
1063 | 0 | { |
1064 | 0 | p.x = pdf_array_get_real(ctx, stroke, k*2+0); |
1065 | 0 | p.y = pdf_array_get_real(ctx, stroke, k*2+1); |
1066 | 0 | if (i == 0 && k == 0) |
1067 | 0 | { |
1068 | 0 | rect->x0 = rect->x1 = p.x; |
1069 | 0 | rect->y0 = rect->y1 = p.y; |
1070 | 0 | } |
1071 | 0 | else |
1072 | 0 | *rect = fz_include_point_in_rect(*rect, p); |
1073 | 0 | fz_append_printf(ctx, buf, "%g %g %c\n", p.x, p.y, k == 0 ? 'm' : 'l'); |
1074 | 0 | } |
1075 | |
|
1076 | 0 | if (m == 1) |
1077 | 0 | fz_append_printf(ctx, buf, "%g %g %c\n", p.x, p.y, 'l'); |
1078 | 0 | } |
1079 | 0 | maybe_stroke(ctx, buf, sc); |
1080 | | |
1081 | | /* Account for line width and add an extra 6 points of whitespace padding. |
1082 | | * In case the annotation is a dot or a perfectly horizontal/vertical line, |
1083 | | * we need some extra size to allow selecting it easily. |
1084 | | */ |
1085 | 0 | *rect = fz_expand_rect(*rect, lw + 6); |
1086 | |
|
1087 | 0 | pdf_dict_put_rect(ctx, annot->obj, PDF_NAME(RD), fz_make_rect(lw + 6, lw + 6, lw + 6, lw + 6)); |
1088 | 0 | } |
1089 | | |
1090 | | /* Contrary to the specification, the points within a QuadPoint are NOT |
1091 | | * ordered in a counter-clockwise fashion starting with the lower left. |
1092 | | * Experiments with Adobe's implementation indicates a cross-wise |
1093 | | * ordering is intended: ul, ur, ll, lr. |
1094 | | */ |
1095 | | enum { UL, UR, LL, LR }; |
1096 | | |
1097 | | static float |
1098 | | extract_quad(fz_context *ctx, fz_point *quad, pdf_obj *obj, int i) |
1099 | 0 | { |
1100 | 0 | float dx, dy; |
1101 | 0 | quad[0].x = pdf_array_get_real(ctx, obj, i+0); |
1102 | 0 | quad[0].y = pdf_array_get_real(ctx, obj, i+1); |
1103 | 0 | quad[1].x = pdf_array_get_real(ctx, obj, i+2); |
1104 | 0 | quad[1].y = pdf_array_get_real(ctx, obj, i+3); |
1105 | 0 | quad[2].x = pdf_array_get_real(ctx, obj, i+4); |
1106 | 0 | quad[2].y = pdf_array_get_real(ctx, obj, i+5); |
1107 | 0 | quad[3].x = pdf_array_get_real(ctx, obj, i+6); |
1108 | 0 | quad[3].y = pdf_array_get_real(ctx, obj, i+7); |
1109 | 0 | dx = quad[UL].x - quad[LL].x; |
1110 | 0 | dy = quad[UL].y - quad[LL].y; |
1111 | 0 | return sqrtf(dx * dx + dy * dy); |
1112 | 0 | } |
1113 | | |
1114 | | static void |
1115 | | union_quad(fz_rect *rect, const fz_point quad[4], float lw) |
1116 | 0 | { |
1117 | 0 | fz_rect qbox; |
1118 | 0 | qbox.x0 = fz_min(fz_min(quad[0].x, quad[1].x), fz_min(quad[2].x, quad[3].x)); |
1119 | 0 | qbox.y0 = fz_min(fz_min(quad[0].y, quad[1].y), fz_min(quad[2].y, quad[3].y)); |
1120 | 0 | qbox.x1 = fz_max(fz_max(quad[0].x, quad[1].x), fz_max(quad[2].x, quad[3].x)); |
1121 | 0 | qbox.y1 = fz_max(fz_max(quad[0].y, quad[1].y), fz_max(quad[2].y, quad[3].y)); |
1122 | 0 | *rect = fz_union_rect(*rect, fz_expand_rect(qbox, lw)); |
1123 | 0 | } |
1124 | | |
1125 | | static fz_point |
1126 | | lerp_point(fz_point a, fz_point b, float t) |
1127 | 0 | { |
1128 | 0 | return fz_make_point(a.x + t * (b.x - a.x), a.y + t * (b.y - a.y)); |
1129 | 0 | } |
1130 | | |
1131 | | static void |
1132 | | pdf_write_highlight_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, pdf_obj **res) |
1133 | 0 | { |
1134 | 0 | pdf_obj *qp; |
1135 | 0 | fz_point quad[4], mquad[4], v; |
1136 | 0 | float h, m, dx, dy, vn; |
1137 | 0 | int i, n; |
1138 | |
|
1139 | 0 | *rect = fz_empty_rect; |
1140 | |
|
1141 | 0 | pdf_write_opacity_blend_mode(ctx, annot, buf, res, FZ_BLEND_MULTIPLY); |
1142 | 0 | pdf_write_fill_color_appearance(ctx, annot, buf); |
1143 | |
|
1144 | 0 | qp = pdf_dict_get(ctx, annot->obj, PDF_NAME(QuadPoints)); |
1145 | 0 | n = pdf_array_len(ctx, qp); |
1146 | 0 | if (n > 0) |
1147 | 0 | { |
1148 | 0 | for (i = 0; i < n; i += 8) |
1149 | 0 | { |
1150 | 0 | h = extract_quad(ctx, quad, qp, i); |
1151 | 0 | m = h / 4.2425f; /* magic number that matches adobe's appearance */ |
1152 | 0 | dx = quad[LR].x - quad[LL].x; |
1153 | 0 | dy = quad[LR].y - quad[LL].y; |
1154 | 0 | vn = sqrtf(dx * dx + dy * dy); |
1155 | 0 | v = fz_make_point(dx * m / vn, dy * m / vn); |
1156 | |
|
1157 | 0 | mquad[LL].x = quad[LL].x - v.x - v.y; |
1158 | 0 | mquad[LL].y = quad[LL].y - v.y + v.x; |
1159 | 0 | mquad[UL].x = quad[UL].x - v.x + v.y; |
1160 | 0 | mquad[UL].y = quad[UL].y - v.y - v.x; |
1161 | 0 | mquad[LR].x = quad[LR].x + v.x - v.y; |
1162 | 0 | mquad[LR].y = quad[LR].y + v.y + v.x; |
1163 | 0 | mquad[UR].x = quad[UR].x + v.x + v.y; |
1164 | 0 | mquad[UR].y = quad[UR].y + v.y - v.x; |
1165 | |
|
1166 | 0 | fz_append_printf(ctx, buf, "%g %g m\n", quad[LL].x, quad[LL].y); |
1167 | 0 | fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n", |
1168 | 0 | mquad[LL].x, mquad[LL].y, |
1169 | 0 | mquad[UL].x, mquad[UL].y, |
1170 | 0 | quad[UL].x, quad[UL].y); |
1171 | 0 | fz_append_printf(ctx, buf, "%g %g l\n", quad[UR].x, quad[UR].y); |
1172 | 0 | fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n", |
1173 | 0 | mquad[UR].x, mquad[UR].y, |
1174 | 0 | mquad[LR].x, mquad[LR].y, |
1175 | 0 | quad[LR].x, quad[LR].y); |
1176 | 0 | fz_append_printf(ctx, buf, "f\n"); |
1177 | |
|
1178 | 0 | union_quad(rect, quad, h/16); |
1179 | 0 | union_quad(rect, mquad, 0); |
1180 | 0 | } |
1181 | 0 | } |
1182 | 0 | } |
1183 | | |
1184 | | static void |
1185 | | pdf_write_underline_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, pdf_obj **res) |
1186 | 0 | { |
1187 | 0 | fz_point quad[4], a, b; |
1188 | 0 | float h; |
1189 | 0 | pdf_obj *qp; |
1190 | 0 | int i, n; |
1191 | |
|
1192 | 0 | *rect = fz_empty_rect; |
1193 | |
|
1194 | 0 | pdf_write_opacity(ctx, annot, buf, res); |
1195 | 0 | pdf_write_stroke_color_appearance(ctx, annot, buf); |
1196 | |
|
1197 | 0 | qp = pdf_dict_get(ctx, annot->obj, PDF_NAME(QuadPoints)); |
1198 | 0 | n = pdf_array_len(ctx, qp); |
1199 | 0 | if (n > 0) |
1200 | 0 | { |
1201 | 0 | for (i = 0; i < n; i += 8) |
1202 | 0 | { |
1203 | | /* Acrobat draws the line at 1/7 of the box width from the bottom |
1204 | | * of the box and 1/16 thick of the box width. */ |
1205 | |
|
1206 | 0 | h = extract_quad(ctx, quad, qp, i); |
1207 | 0 | a = lerp_point(quad[LL], quad[UL], 1/7.0f); |
1208 | 0 | b = lerp_point(quad[LR], quad[UR], 1/7.0f); |
1209 | |
|
1210 | 0 | fz_append_printf(ctx, buf, "%g w\n", h/16); |
1211 | 0 | fz_append_printf(ctx, buf, "%g %g m\n", a.x, a.y); |
1212 | 0 | fz_append_printf(ctx, buf, "%g %g l\n", b.x, b.y); |
1213 | 0 | fz_append_printf(ctx, buf, "S\n"); |
1214 | |
|
1215 | 0 | union_quad(rect, quad, h/16); |
1216 | 0 | } |
1217 | 0 | } |
1218 | 0 | } |
1219 | | |
1220 | | static void |
1221 | | pdf_write_strike_out_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, pdf_obj **res) |
1222 | 0 | { |
1223 | 0 | fz_point quad[4], a, b; |
1224 | 0 | float h; |
1225 | 0 | pdf_obj *qp; |
1226 | 0 | int i, n; |
1227 | |
|
1228 | 0 | pdf_write_opacity(ctx, annot, buf, res); |
1229 | 0 | pdf_write_stroke_color_appearance(ctx, annot, buf); |
1230 | |
|
1231 | 0 | qp = pdf_dict_get(ctx, annot->obj, PDF_NAME(QuadPoints)); |
1232 | 0 | n = pdf_array_len(ctx, qp); |
1233 | 0 | if (n > 0) |
1234 | 0 | { |
1235 | 0 | *rect = fz_empty_rect; |
1236 | 0 | for (i = 0; i < n; i += 8) |
1237 | 0 | { |
1238 | | /* Acrobat draws the line at 3/7 of the box width from the bottom |
1239 | | * of the box and 1/16 thick of the box width. */ |
1240 | |
|
1241 | 0 | h = extract_quad(ctx, quad, qp, i); |
1242 | 0 | a = lerp_point(quad[LL], quad[UL], 3/7.0f); |
1243 | 0 | b = lerp_point(quad[LR], quad[UR], 3/7.0f); |
1244 | |
|
1245 | 0 | fz_append_printf(ctx, buf, "%g w\n", h/16); |
1246 | 0 | fz_append_printf(ctx, buf, "%g %g m\n", a.x, a.y); |
1247 | 0 | fz_append_printf(ctx, buf, "%g %g l\n", b.x, b.y); |
1248 | 0 | fz_append_printf(ctx, buf, "S\n"); |
1249 | |
|
1250 | 0 | union_quad(rect, quad, h/16); |
1251 | 0 | } |
1252 | 0 | } |
1253 | 0 | } |
1254 | | |
1255 | | static void |
1256 | | pdf_write_squiggly_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, pdf_obj **res) |
1257 | 0 | { |
1258 | 0 | fz_point quad[4], a, b, c, v; |
1259 | 0 | float h, x, w; |
1260 | 0 | pdf_obj *qp; |
1261 | 0 | int i, n; |
1262 | |
|
1263 | 0 | *rect = fz_empty_rect; |
1264 | |
|
1265 | 0 | pdf_write_opacity(ctx, annot, buf, res); |
1266 | 0 | pdf_write_stroke_color_appearance(ctx, annot, buf); |
1267 | |
|
1268 | 0 | qp = pdf_dict_get(ctx, annot->obj, PDF_NAME(QuadPoints)); |
1269 | 0 | n = pdf_array_len(ctx, qp); |
1270 | 0 | if (n > 0) |
1271 | 0 | { |
1272 | 0 | for (i = 0; i < n; i += 8) |
1273 | 0 | { |
1274 | 0 | int up = 1; |
1275 | 0 | h = extract_quad(ctx, quad, qp, i); |
1276 | 0 | v = fz_make_point(quad[LR].x - quad[LL].x, quad[LR].y - quad[LL].y); |
1277 | 0 | w = sqrtf(v.x * v.x + v.y * v.y); |
1278 | 0 | x = 0; |
1279 | |
|
1280 | 0 | fz_append_printf(ctx, buf, "%g w\n", h/16); |
1281 | 0 | fz_append_printf(ctx, buf, "%g %g m\n", quad[LL].x, quad[LL].y); |
1282 | 0 | while (x < w) |
1283 | 0 | { |
1284 | 0 | x += h/7; |
1285 | 0 | a = lerp_point(quad[LL], quad[LR], x/w); |
1286 | 0 | if (up) |
1287 | 0 | { |
1288 | 0 | b = lerp_point(quad[UL], quad[UR], x/w); |
1289 | 0 | c = lerp_point(a, b, 1/7.0f); |
1290 | 0 | fz_append_printf(ctx, buf, "%g %g l\n", c.x, c.y); |
1291 | 0 | } |
1292 | 0 | else |
1293 | 0 | fz_append_printf(ctx, buf, "%g %g l\n", a.x, a.y); |
1294 | 0 | up = !up; |
1295 | 0 | } |
1296 | 0 | fz_append_printf(ctx, buf, "S\n"); |
1297 | |
|
1298 | 0 | union_quad(rect, quad, h/16); |
1299 | 0 | } |
1300 | 0 | } |
1301 | 0 | } |
1302 | | |
1303 | | static void |
1304 | | pdf_write_redact_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, pdf_obj **res) |
1305 | 0 | { |
1306 | 0 | fz_point quad[4]; |
1307 | 0 | pdf_obj *qp; |
1308 | 0 | int i, n; |
1309 | |
|
1310 | 0 | pdf_write_opacity(ctx, annot, buf, res); |
1311 | |
|
1312 | 0 | fz_append_printf(ctx, buf, "1 0 0 RG\n"); |
1313 | |
|
1314 | 0 | qp = pdf_dict_get(ctx, annot->obj, PDF_NAME(QuadPoints)); |
1315 | 0 | n = pdf_array_len(ctx, qp); |
1316 | 0 | if (n > 0) |
1317 | 0 | { |
1318 | 0 | *rect = fz_empty_rect; |
1319 | 0 | for (i = 0; i < n; i += 8) |
1320 | 0 | { |
1321 | 0 | extract_quad(ctx, quad, qp, i); |
1322 | 0 | fz_append_printf(ctx, buf, "%g %g m\n", quad[LL].x, quad[LL].y); |
1323 | 0 | fz_append_printf(ctx, buf, "%g %g l\n", quad[LR].x, quad[LR].y); |
1324 | 0 | fz_append_printf(ctx, buf, "%g %g l\n", quad[UR].x, quad[UR].y); |
1325 | 0 | fz_append_printf(ctx, buf, "%g %g l\n", quad[UL].x, quad[UL].y); |
1326 | 0 | fz_append_printf(ctx, buf, "s\n"); |
1327 | 0 | union_quad(rect, quad, 1); |
1328 | 0 | } |
1329 | 0 | } |
1330 | 0 | else |
1331 | 0 | { |
1332 | 0 | fz_append_printf(ctx, buf, "%g %g m\n", rect->x0+1, rect->y0+1); |
1333 | 0 | fz_append_printf(ctx, buf, "%g %g l\n", rect->x1-1, rect->y0+1); |
1334 | 0 | fz_append_printf(ctx, buf, "%g %g l\n", rect->x1-1, rect->y1-1); |
1335 | 0 | fz_append_printf(ctx, buf, "%g %g l\n", rect->x0+1, rect->y1-1); |
1336 | 0 | fz_append_printf(ctx, buf, "s\n"); |
1337 | 0 | } |
1338 | 0 | } |
1339 | | |
1340 | | static void |
1341 | | pdf_write_caret_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, fz_rect *bbox, pdf_obj **res) |
1342 | 0 | { |
1343 | 0 | float xc = (rect->x0 + rect->x1) / 2; |
1344 | 0 | float yc = (rect->y0 + rect->y1) / 2; |
1345 | |
|
1346 | 0 | pdf_write_opacity(ctx, annot, buf, res); |
1347 | 0 | pdf_write_fill_color_appearance(ctx, annot, buf); |
1348 | |
|
1349 | 0 | fz_append_string(ctx, buf, "0 0 m\n"); |
1350 | 0 | fz_append_string(ctx, buf, "10 0 10 7 10 14 c\n"); |
1351 | 0 | fz_append_string(ctx, buf, "10 7 10 0 20 0 c\n"); |
1352 | 0 | fz_append_string(ctx, buf, "f\n"); |
1353 | |
|
1354 | 0 | *rect = fz_make_rect(xc - 10, yc - 7, xc + 10, yc + 7); |
1355 | 0 | *bbox = fz_make_rect(0, 0, 20, 14); |
1356 | 0 | } |
1357 | | |
1358 | | static void |
1359 | | pdf_write_icon_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, fz_rect *bbox, pdf_obj **res) |
1360 | 0 | { |
1361 | 0 | const char *name; |
1362 | 0 | float w = fz_max(10, fz_min(20, fz_min(rect->y1 - rect->y0, rect->x1 - rect->x0))); |
1363 | 0 | float x = rect->x0; |
1364 | 0 | float y = rect->y1 - w; |
1365 | |
|
1366 | 0 | pdf_write_opacity(ctx, annot, buf, res); |
1367 | |
|
1368 | 0 | if (!pdf_write_fill_color_appearance(ctx, annot, buf)) |
1369 | 0 | fz_append_string(ctx, buf, "1 g\n"); |
1370 | |
|
1371 | 0 | fz_append_string(ctx, buf, "1 w\n0.5 0.5 15 15 re\nb\n"); |
1372 | 0 | fz_append_string(ctx, buf, "1 0 0 -1 4 12 cm\n"); |
1373 | |
|
1374 | 0 | if (pdf_is_dark_fill_color(ctx, annot)) |
1375 | 0 | fz_append_string(ctx, buf, "1 g\n"); |
1376 | 0 | else |
1377 | 0 | fz_append_string(ctx, buf, "0 g\n"); |
1378 | |
|
1379 | 0 | name = pdf_annot_icon_name(ctx, annot); |
1380 | | |
1381 | | /* Text names */ |
1382 | 0 | if (!strcmp(name, "Comment")) |
1383 | 0 | fz_append_string(ctx, buf, icon_comment); |
1384 | 0 | else if (!strcmp(name, "Key")) |
1385 | 0 | fz_append_string(ctx, buf, icon_key); |
1386 | 0 | else if (!strcmp(name, "Note")) |
1387 | 0 | fz_append_string(ctx, buf, icon_note); |
1388 | 0 | else if (!strcmp(name, "Help")) |
1389 | 0 | fz_append_string(ctx, buf, icon_help); |
1390 | 0 | else if (!strcmp(name, "NewParagraph")) |
1391 | 0 | fz_append_string(ctx, buf, icon_new_paragraph); |
1392 | 0 | else if (!strcmp(name, "Paragraph")) |
1393 | 0 | fz_append_string(ctx, buf, icon_paragraph); |
1394 | 0 | else if (!strcmp(name, "Insert")) |
1395 | 0 | fz_append_string(ctx, buf, icon_insert); |
1396 | | |
1397 | | /* FileAttachment names */ |
1398 | 0 | else if (!strcmp(name, "Graph")) |
1399 | 0 | fz_append_string(ctx, buf, icon_graph); |
1400 | 0 | else if (!strcmp(name, "PushPin")) |
1401 | 0 | fz_append_string(ctx, buf, icon_push_pin); |
1402 | 0 | else if (!strcmp(name, "Paperclip")) |
1403 | 0 | fz_append_string(ctx, buf, icon_paperclip); |
1404 | 0 | else if (!strcmp(name, "Tag")) |
1405 | 0 | fz_append_string(ctx, buf, icon_tag); |
1406 | | |
1407 | | /* Sound names */ |
1408 | 0 | else if (!strcmp(name, "Speaker")) |
1409 | 0 | fz_append_string(ctx, buf, icon_speaker); |
1410 | 0 | else if (!strcmp(name, "Mic")) |
1411 | 0 | fz_append_string(ctx, buf, icon_mic); |
1412 | | |
1413 | | /* Unknown */ |
1414 | 0 | else |
1415 | 0 | fz_append_string(ctx, buf, icon_star); |
1416 | |
|
1417 | 0 | *rect = fz_make_rect(x, y, x + w, y + w); |
1418 | 0 | *bbox = fz_make_rect(0, 0, 16, 16); |
1419 | 0 | } |
1420 | | |
1421 | | static float |
1422 | | measure_stamp_string(fz_context *ctx, fz_font *font, const char *text) |
1423 | 0 | { |
1424 | 0 | float w = 0; |
1425 | 0 | while (*text) |
1426 | 0 | { |
1427 | 0 | int c, g; |
1428 | 0 | text += fz_chartorune(&c, text); |
1429 | 0 | if (fz_windows_1252_from_unicode(c) < 0) |
1430 | 0 | c = REPLACEMENT; |
1431 | 0 | g = fz_encode_character(ctx, font, c); |
1432 | 0 | w += fz_advance_glyph(ctx, font, g, 0); |
1433 | 0 | } |
1434 | 0 | return w; |
1435 | 0 | } |
1436 | | |
1437 | | static void |
1438 | | write_stamp_string(fz_context *ctx, fz_buffer *buf, fz_font *font, const char *text) |
1439 | 0 | { |
1440 | 0 | fz_append_byte(ctx, buf, '('); |
1441 | 0 | while (*text) |
1442 | 0 | { |
1443 | 0 | int c; |
1444 | 0 | text += fz_chartorune(&c, text); |
1445 | 0 | c = fz_windows_1252_from_unicode(c); |
1446 | 0 | if (c < 0) c = REPLACEMENT; |
1447 | 0 | if (c == '(' || c == ')' || c == '\\') |
1448 | 0 | fz_append_byte(ctx, buf, '\\'); |
1449 | 0 | fz_append_byte(ctx, buf, c); |
1450 | 0 | } |
1451 | 0 | fz_append_byte(ctx, buf, ')'); |
1452 | 0 | } |
1453 | | |
1454 | | static void |
1455 | | write_stamp(fz_context *ctx, fz_buffer *buf, fz_font *font, const char *text, float y, float h) |
1456 | 0 | { |
1457 | 0 | float tw = measure_stamp_string(ctx, font, text) * h; |
1458 | 0 | fz_append_string(ctx, buf, "BT\n"); |
1459 | 0 | fz_append_printf(ctx, buf, "/Times %g Tf\n", h); |
1460 | 0 | fz_append_printf(ctx, buf, "%g %g Td\n", (190-tw)/2, y); |
1461 | 0 | write_stamp_string(ctx, buf, font, text); |
1462 | 0 | fz_append_string(ctx, buf, " Tj\n"); |
1463 | 0 | fz_append_string(ctx, buf, "ET\n"); |
1464 | 0 | } |
1465 | | |
1466 | | static void |
1467 | | pdf_write_stamp_appearance_rubber(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, fz_rect *bbox, fz_matrix *matrix, pdf_obj **res) |
1468 | 0 | { |
1469 | 0 | fz_font *font; |
1470 | 0 | pdf_obj *res_font; |
1471 | 0 | pdf_obj *name; |
1472 | 0 | float w, h, xs, ys; |
1473 | 0 | fz_matrix rotate; |
1474 | |
|
1475 | 0 | name = pdf_dict_get(ctx, annot->obj, PDF_NAME(Name)); |
1476 | 0 | if (!name) |
1477 | 0 | name = PDF_NAME(Draft); |
1478 | |
|
1479 | 0 | h = rect->y1 - rect->y0; |
1480 | 0 | w = rect->x1 - rect->x0; |
1481 | 0 | xs = w / 190; |
1482 | 0 | ys = h / 50; |
1483 | |
|
1484 | 0 | font = fz_new_base14_font(ctx, "Times-Bold"); |
1485 | 0 | fz_try(ctx) |
1486 | 0 | { |
1487 | | /* /Resources << /Font << /Times %d 0 R >> >> */ |
1488 | 0 | if (!*res) |
1489 | 0 | *res = pdf_new_dict(ctx, annot->page->doc, 1); |
1490 | 0 | res_font = pdf_dict_put_dict(ctx, *res, PDF_NAME(Font), 1); |
1491 | 0 | pdf_dict_put_drop(ctx, res_font, PDF_NAME(Times), pdf_add_simple_font(ctx, annot->page->doc, font, 0)); |
1492 | |
|
1493 | 0 | pdf_write_opacity(ctx, annot, buf, res); |
1494 | 0 | pdf_write_fill_color_appearance(ctx, annot, buf); |
1495 | 0 | pdf_write_stroke_color_appearance(ctx, annot, buf); |
1496 | 0 | rotate = fz_rotate(0.6f); |
1497 | 0 | fz_append_printf(ctx, buf, "%M cm\n", &rotate); |
1498 | 0 | fz_append_string(ctx, buf, "2 w\n2 2 186 44 re\nS\n"); |
1499 | |
|
1500 | 0 | if (name == PDF_NAME(Approved)) |
1501 | 0 | write_stamp(ctx, buf, font, "APPROVED", 13, 30); |
1502 | 0 | else if (name == PDF_NAME(AsIs)) |
1503 | 0 | write_stamp(ctx, buf, font, "AS IS", 13, 30); |
1504 | 0 | else if (name == PDF_NAME(Confidential)) |
1505 | 0 | write_stamp(ctx, buf, font, "CONFIDENTIAL", 17, 20); |
1506 | 0 | else if (name == PDF_NAME(Departmental)) |
1507 | 0 | write_stamp(ctx, buf, font, "DEPARTMENTAL", 17, 20); |
1508 | 0 | else if (name == PDF_NAME(Experimental)) |
1509 | 0 | write_stamp(ctx, buf, font, "EXPERIMENTAL", 17, 20); |
1510 | 0 | else if (name == PDF_NAME(Expired)) |
1511 | 0 | write_stamp(ctx, buf, font, "EXPIRED", 13, 30); |
1512 | 0 | else if (name == PDF_NAME(Final)) |
1513 | 0 | write_stamp(ctx, buf, font, "FINAL", 13, 30); |
1514 | 0 | else if (name == PDF_NAME(ForComment)) |
1515 | 0 | write_stamp(ctx, buf, font, "FOR COMMENT", 17, 20); |
1516 | 0 | else if (name == PDF_NAME(ForPublicRelease)) |
1517 | 0 | { |
1518 | 0 | write_stamp(ctx, buf, font, "FOR PUBLIC", 26, 18); |
1519 | 0 | write_stamp(ctx, buf, font, "RELEASE", 8.5f, 18); |
1520 | 0 | } |
1521 | 0 | else if (name == PDF_NAME(NotApproved)) |
1522 | 0 | write_stamp(ctx, buf, font, "NOT APPROVED", 17, 20); |
1523 | 0 | else if (name == PDF_NAME(NotForPublicRelease)) |
1524 | 0 | { |
1525 | 0 | write_stamp(ctx, buf, font, "NOT FOR", 26, 18); |
1526 | 0 | write_stamp(ctx, buf, font, "PUBLIC RELEASE", 8.5, 18); |
1527 | 0 | } |
1528 | 0 | else if (name == PDF_NAME(Sold)) |
1529 | 0 | write_stamp(ctx, buf, font, "SOLD", 13, 30); |
1530 | 0 | else if (name == PDF_NAME(TopSecret)) |
1531 | 0 | write_stamp(ctx, buf, font, "TOP SECRET", 14, 26); |
1532 | 0 | else if (name == PDF_NAME(Draft)) |
1533 | 0 | write_stamp(ctx, buf, font, "DRAFT", 13, 30); |
1534 | 0 | else |
1535 | 0 | write_stamp(ctx, buf, font, pdf_to_name(ctx, name), 17, 20); |
1536 | 0 | } |
1537 | 0 | fz_always(ctx) |
1538 | 0 | fz_drop_font(ctx, font); |
1539 | 0 | fz_catch(ctx) |
1540 | 0 | fz_rethrow(ctx); |
1541 | | |
1542 | 0 | *bbox = fz_make_rect(0, 0, 190, 50); |
1543 | 0 | if (xs > ys) |
1544 | 0 | { |
1545 | 0 | float xc = (rect->x1+rect->x0) / 2; |
1546 | 0 | rect->x0 = xc - 95 * ys; |
1547 | 0 | rect->x1 = xc + 95 * ys; |
1548 | 0 | } |
1549 | 0 | else |
1550 | 0 | { |
1551 | 0 | float yc = (rect->y1+rect->y0) / 2; |
1552 | 0 | rect->y0 = yc - 25 * xs; |
1553 | 0 | rect->y1 = yc + 25 * xs; |
1554 | 0 | } |
1555 | |
|
1556 | 0 | *matrix = fz_identity; |
1557 | 0 | } |
1558 | | |
1559 | | static void |
1560 | | pdf_write_stamp_appearance_image(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, fz_rect *bbox, fz_matrix *matrix, pdf_obj **res, pdf_obj *img) |
1561 | 0 | { |
1562 | 0 | pdf_obj *res_xobj; |
1563 | |
|
1564 | 0 | if (!*res) |
1565 | 0 | *res = pdf_new_dict(ctx, annot->page->doc, 1); |
1566 | 0 | res_xobj = pdf_dict_put_dict(ctx, *res, PDF_NAME(XObject), 1); |
1567 | 0 | pdf_dict_put(ctx, res_xobj, PDF_NAME(I), img); |
1568 | |
|
1569 | 0 | pdf_write_opacity(ctx, annot, buf, res); |
1570 | |
|
1571 | 0 | fz_append_string(ctx, buf, "/I Do\n"); |
1572 | |
|
1573 | 0 | *bbox = fz_unit_rect; |
1574 | 0 | *matrix = fz_identity; |
1575 | 0 | } |
1576 | | |
1577 | | static void |
1578 | | pdf_write_stamp_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, fz_rect *bbox, fz_matrix *matrix, pdf_obj **res) |
1579 | 0 | { |
1580 | 0 | pdf_obj *img = pdf_annot_stamp_image_obj(ctx, annot); |
1581 | 0 | if (img) |
1582 | 0 | pdf_write_stamp_appearance_image(ctx, annot, buf, rect, bbox, matrix, res, img); |
1583 | 0 | else |
1584 | 0 | pdf_write_stamp_appearance_rubber(ctx, annot, buf, rect, bbox, matrix, res); |
1585 | 0 | } |
1586 | | |
1587 | | static void |
1588 | | add_required_fonts(fz_context *ctx, pdf_document *doc, pdf_obj *res_font, |
1589 | | fz_text_language lang, fz_font *font, const char *fontname, const char *text) |
1590 | 0 | { |
1591 | 0 | fz_font *cjk_font; |
1592 | 0 | char buf[40]; |
1593 | |
|
1594 | 0 | int add_latin = 0; |
1595 | 0 | int add_greek = 0; |
1596 | 0 | int add_cyrillic = 0; |
1597 | 0 | int add_korean = 0; |
1598 | 0 | int add_japanese = 0; |
1599 | 0 | int add_bopomofo = 0; |
1600 | 0 | int add_han = 0; |
1601 | 0 | int add_hans = 0; |
1602 | 0 | int add_hant = 0; |
1603 | |
|
1604 | 0 | while (*text) |
1605 | 0 | { |
1606 | 0 | int c; |
1607 | 0 | text += fz_chartorune(&c, text); |
1608 | 0 | switch (ucdn_get_script(c)) |
1609 | 0 | { |
1610 | 0 | default: add_latin = 1; /* for fallback bullet character */ break; |
1611 | 0 | case UCDN_SCRIPT_COMMON: break; |
1612 | 0 | case UCDN_SCRIPT_INHERITED: break; |
1613 | 0 | case UCDN_SCRIPT_LATIN: add_latin = 1; break; |
1614 | 0 | case UCDN_SCRIPT_GREEK: add_greek = 1; break; |
1615 | 0 | case UCDN_SCRIPT_CYRILLIC: add_cyrillic = 1; break; |
1616 | 0 | case UCDN_SCRIPT_HANGUL: add_korean = 1; break; |
1617 | 0 | case UCDN_SCRIPT_HIRAGANA: add_japanese = 1; break; |
1618 | 0 | case UCDN_SCRIPT_KATAKANA: add_japanese = 1; break; |
1619 | 0 | case UCDN_SCRIPT_BOPOMOFO: add_bopomofo = 1; break; |
1620 | 0 | case UCDN_SCRIPT_HAN: add_han = 1; break; |
1621 | 0 | } |
1622 | 0 | } |
1623 | | |
1624 | 0 | if (add_han) |
1625 | 0 | { |
1626 | 0 | switch (lang) |
1627 | 0 | { |
1628 | 0 | case FZ_LANG_ko: add_korean = 1; break; |
1629 | 0 | default: /* fall through */ |
1630 | 0 | case FZ_LANG_ja: add_japanese = 1; break; |
1631 | 0 | case FZ_LANG_zh: /* fall through */ |
1632 | 0 | case FZ_LANG_zh_Hant: add_hant = 1; break; |
1633 | 0 | case FZ_LANG_zh_Hans: add_hans = 1; break; |
1634 | 0 | } |
1635 | 0 | } |
1636 | | |
1637 | 0 | if (add_bopomofo) |
1638 | 0 | { |
1639 | 0 | if (lang == FZ_LANG_zh_Hans) |
1640 | 0 | add_hans = 1; |
1641 | 0 | else |
1642 | 0 | add_hant = 1; |
1643 | 0 | } |
1644 | |
|
1645 | 0 | if (!add_greek && !add_cyrillic && !add_korean && !add_japanese && !add_hant && !add_hans) |
1646 | 0 | add_latin = 1; |
1647 | |
|
1648 | 0 | if (add_latin) |
1649 | 0 | { |
1650 | 0 | if (!pdf_dict_gets(ctx, res_font, fontname)) |
1651 | 0 | pdf_dict_puts_drop(ctx, res_font, fontname, |
1652 | 0 | pdf_add_simple_font(ctx, doc, font, PDF_SIMPLE_ENCODING_LATIN)); |
1653 | 0 | } |
1654 | 0 | if (add_greek) |
1655 | 0 | { |
1656 | 0 | fz_snprintf(buf, sizeof buf, "%sGRK", fontname); |
1657 | 0 | if (!pdf_dict_gets(ctx, res_font, buf)) |
1658 | 0 | pdf_dict_puts_drop(ctx, res_font, buf, |
1659 | 0 | pdf_add_simple_font(ctx, doc, font, PDF_SIMPLE_ENCODING_GREEK)); |
1660 | 0 | } |
1661 | 0 | if (add_cyrillic) |
1662 | 0 | { |
1663 | 0 | fz_snprintf(buf, sizeof buf, "%sCYR", fontname); |
1664 | 0 | if (!pdf_dict_gets(ctx, res_font, buf)) |
1665 | 0 | pdf_dict_puts_drop(ctx, res_font, buf, |
1666 | 0 | pdf_add_simple_font(ctx, doc, font, PDF_SIMPLE_ENCODING_CYRILLIC)); |
1667 | 0 | } |
1668 | 0 | if (add_korean && !pdf_dict_gets(ctx, res_font, "Batang")) |
1669 | 0 | { |
1670 | 0 | cjk_font = fz_new_cjk_font(ctx, FZ_ADOBE_KOREA); |
1671 | 0 | pdf_dict_puts_drop(ctx, res_font, "Batang", |
1672 | 0 | pdf_add_cjk_font(ctx, doc, font, FZ_ADOBE_KOREA, 0, 1)); |
1673 | 0 | fz_drop_font(ctx, cjk_font); |
1674 | 0 | } |
1675 | 0 | if (add_japanese && !pdf_dict_gets(ctx, res_font, "Mincho")) |
1676 | 0 | { |
1677 | 0 | cjk_font = fz_new_cjk_font(ctx, FZ_ADOBE_JAPAN); |
1678 | 0 | pdf_dict_puts_drop(ctx, res_font, "Mincho", |
1679 | 0 | pdf_add_cjk_font(ctx, doc, font, FZ_ADOBE_JAPAN, 0, 1)); |
1680 | 0 | fz_drop_font(ctx, cjk_font); |
1681 | 0 | } |
1682 | 0 | if (add_hant && !pdf_dict_gets(ctx, res_font, "Ming")) |
1683 | 0 | { |
1684 | 0 | cjk_font = fz_new_cjk_font(ctx, FZ_ADOBE_CNS); |
1685 | 0 | pdf_dict_puts_drop(ctx, res_font, "Ming", |
1686 | 0 | pdf_add_cjk_font(ctx, doc, font, FZ_ADOBE_CNS, 0, 1)); |
1687 | 0 | fz_drop_font(ctx, cjk_font); |
1688 | 0 | } |
1689 | 0 | if (add_hans && !pdf_dict_gets(ctx, res_font, "Song")) |
1690 | 0 | { |
1691 | 0 | cjk_font = fz_new_cjk_font(ctx, FZ_ADOBE_GB); |
1692 | 0 | pdf_dict_puts_drop(ctx, res_font, "Song", |
1693 | 0 | pdf_add_cjk_font(ctx, doc, font, FZ_ADOBE_GB, 0, 1)); |
1694 | 0 | fz_drop_font(ctx, cjk_font); |
1695 | 0 | } |
1696 | 0 | } |
1697 | | |
1698 | | static int find_initial_script(const char *text) |
1699 | 0 | { |
1700 | 0 | int script = UCDN_SCRIPT_COMMON; |
1701 | 0 | int c; |
1702 | 0 | while (*text) |
1703 | 0 | { |
1704 | 0 | text += fz_chartorune(&c, text); |
1705 | 0 | script = ucdn_get_script(c); |
1706 | 0 | if (script != UCDN_SCRIPT_COMMON && script != UCDN_SCRIPT_INHERITED) |
1707 | 0 | break; |
1708 | 0 | } |
1709 | 0 | if (script == UCDN_SCRIPT_COMMON || script == UCDN_SCRIPT_INHERITED) |
1710 | 0 | script = UCDN_SCRIPT_LATIN; |
1711 | 0 | return script; |
1712 | 0 | } |
1713 | | |
1714 | | enum { ENC_LATIN = 1, ENC_GREEK, ENC_CYRILLIC, ENC_KOREAN, ENC_JAPANESE, ENC_HANT, ENC_HANS }; |
1715 | | |
1716 | | struct text_walk_state |
1717 | | { |
1718 | | const char *text, *end; |
1719 | | fz_font *font; |
1720 | | fz_text_language lang; |
1721 | | int enc, u, c, n, last_script; |
1722 | | float w; |
1723 | | }; |
1724 | | |
1725 | | 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) |
1726 | 0 | { |
1727 | 0 | state->text = text; |
1728 | 0 | state->end = end ? end : text + strlen(text); |
1729 | 0 | state->lang = lang; |
1730 | 0 | state->font = font; |
1731 | 0 | state->last_script = find_initial_script(text); |
1732 | 0 | state->n = 0; |
1733 | 0 | } |
1734 | | |
1735 | | static int next_text_walk(fz_context *ctx, struct text_walk_state *state) |
1736 | 0 | { |
1737 | 0 | int script, g; |
1738 | |
|
1739 | 0 | state->text += state->n; |
1740 | 0 | if (state->text >= state->end) |
1741 | 0 | { |
1742 | 0 | state->n = 0; |
1743 | 0 | return 0; |
1744 | 0 | } |
1745 | | |
1746 | 0 | state->n = fz_chartorune(&state->u, state->text); |
1747 | 0 | script = ucdn_get_script(state->u); |
1748 | 0 | if (script == UCDN_SCRIPT_COMMON || script == UCDN_SCRIPT_INHERITED) |
1749 | 0 | script = state->last_script; |
1750 | 0 | state->last_script = script; |
1751 | |
|
1752 | 0 | switch (script) |
1753 | 0 | { |
1754 | 0 | default: |
1755 | 0 | state->enc = ENC_LATIN; |
1756 | 0 | state->c = REPLACEMENT; |
1757 | 0 | break; |
1758 | 0 | case UCDN_SCRIPT_LATIN: |
1759 | 0 | state->enc = ENC_LATIN; |
1760 | 0 | state->c = fz_windows_1252_from_unicode(state->u); |
1761 | 0 | break; |
1762 | 0 | case UCDN_SCRIPT_GREEK: |
1763 | 0 | state->enc = ENC_GREEK; |
1764 | 0 | state->c = fz_iso8859_7_from_unicode(state->u); |
1765 | 0 | break; |
1766 | 0 | case UCDN_SCRIPT_CYRILLIC: |
1767 | 0 | state->enc = ENC_CYRILLIC; |
1768 | 0 | state->c = fz_koi8u_from_unicode(state->u); |
1769 | 0 | break; |
1770 | 0 | case UCDN_SCRIPT_HANGUL: |
1771 | 0 | state->enc = ENC_KOREAN; |
1772 | 0 | state->c = state->u; |
1773 | 0 | break; |
1774 | 0 | case UCDN_SCRIPT_HIRAGANA: |
1775 | 0 | case UCDN_SCRIPT_KATAKANA: |
1776 | 0 | state->enc = ENC_JAPANESE; |
1777 | 0 | state->c = state->u; |
1778 | 0 | break; |
1779 | 0 | case UCDN_SCRIPT_BOPOMOFO: |
1780 | 0 | state->enc = (state->lang == FZ_LANG_zh_Hans) ? ENC_HANS : ENC_HANT; |
1781 | 0 | state->c = state->u; |
1782 | 0 | break; |
1783 | 0 | case UCDN_SCRIPT_HAN: |
1784 | 0 | switch (state->lang) |
1785 | 0 | { |
1786 | 0 | case FZ_LANG_ko: state->enc = ENC_KOREAN; break; |
1787 | 0 | default: /* fall through */ |
1788 | 0 | case FZ_LANG_ja: state->enc = ENC_JAPANESE; break; |
1789 | 0 | case FZ_LANG_zh: /* fall through */ |
1790 | 0 | case FZ_LANG_zh_Hant: state->enc = ENC_HANT; break; |
1791 | 0 | case FZ_LANG_zh_Hans: state->enc = ENC_HANS; break; |
1792 | 0 | } |
1793 | 0 | state->c = state->u; |
1794 | 0 | break; |
1795 | 0 | } |
1796 | | |
1797 | | /* TODO: check that character is encodable with ENC_KOREAN/etc */ |
1798 | 0 | if (state->c < 0) |
1799 | 0 | { |
1800 | 0 | state->enc = ENC_LATIN; |
1801 | 0 | state->c = REPLACEMENT; |
1802 | 0 | } |
1803 | |
|
1804 | 0 | if (state->enc >= ENC_KOREAN) |
1805 | 0 | { |
1806 | 0 | state->w = 1; |
1807 | 0 | } |
1808 | 0 | else |
1809 | 0 | { |
1810 | 0 | if (state->font != NULL) |
1811 | 0 | { |
1812 | 0 | g = fz_encode_character(ctx, state->font, state->u); |
1813 | 0 | state->w = fz_advance_glyph(ctx, state->font, g, 0); |
1814 | 0 | } |
1815 | 0 | } |
1816 | |
|
1817 | 0 | return 1; |
1818 | 0 | } |
1819 | | |
1820 | | static float |
1821 | | measure_string(fz_context *ctx, fz_text_language lang, fz_font *font, const char *a) |
1822 | 0 | { |
1823 | 0 | struct text_walk_state state; |
1824 | 0 | float w = 0; |
1825 | 0 | init_text_walk(ctx, &state, lang, font, a, NULL); |
1826 | 0 | while (next_text_walk(ctx, &state)) |
1827 | 0 | w += state.w; |
1828 | 0 | return w; |
1829 | 0 | } |
1830 | | |
1831 | | |
1832 | | static float |
1833 | | break_string(fz_context *ctx, fz_text_language lang, fz_font *font, float size, const char *text, const char **endp, float maxw) |
1834 | 0 | { |
1835 | 0 | struct text_walk_state state; |
1836 | 0 | const char *space = NULL; |
1837 | 0 | float space_x, x = 0; |
1838 | 0 | init_text_walk(ctx, &state, lang, font, text, NULL); |
1839 | 0 | while (next_text_walk(ctx, &state)) |
1840 | 0 | { |
1841 | 0 | if (state.u == '\n' || state.u == '\r') |
1842 | 0 | break; |
1843 | 0 | if (state.u == ' ') |
1844 | 0 | { |
1845 | 0 | space = state.text + state.n; |
1846 | 0 | space_x = x; |
1847 | 0 | } |
1848 | 0 | x += state.w * size; |
1849 | 0 | if (space && x > maxw) |
1850 | 0 | return *endp = space, space_x; |
1851 | 0 | } |
1852 | 0 | return *endp = state.text + state.n, x; |
1853 | 0 | } |
1854 | | |
1855 | | static void |
1856 | | write_string(fz_context *ctx, fz_buffer *buf, |
1857 | | fz_text_language lang, fz_font *font, const char *fontname, float size, const char *text, const char *end) |
1858 | 0 | { |
1859 | 0 | struct text_walk_state state; |
1860 | 0 | int last_enc = 0; |
1861 | 0 | init_text_walk(ctx, &state, lang, font, text, end); |
1862 | 0 | while (next_text_walk(ctx, &state)) |
1863 | 0 | { |
1864 | 0 | if (state.enc != last_enc) |
1865 | 0 | { |
1866 | 0 | if (last_enc) |
1867 | 0 | { |
1868 | 0 | if (last_enc < ENC_KOREAN) |
1869 | 0 | fz_append_byte(ctx, buf, ')'); |
1870 | 0 | else |
1871 | 0 | fz_append_byte(ctx, buf, '>'); |
1872 | 0 | fz_append_string(ctx, buf, " Tj\n"); |
1873 | 0 | } |
1874 | |
|
1875 | 0 | switch (state.enc) |
1876 | 0 | { |
1877 | 0 | case ENC_LATIN: fz_append_printf(ctx, buf, "/%s %g Tf\n", fontname, size); break; |
1878 | 0 | case ENC_GREEK: fz_append_printf(ctx, buf, "/%sGRK %g Tf\n", fontname, size); break; |
1879 | 0 | case ENC_CYRILLIC: fz_append_printf(ctx, buf, "/%sCYR %g Tf\n", fontname, size); break; |
1880 | 0 | case ENC_KOREAN: fz_append_printf(ctx, buf, "/Batang %g Tf\n", size); break; |
1881 | 0 | case ENC_JAPANESE: fz_append_printf(ctx, buf, "/Mincho %g Tf\n", size); break; |
1882 | 0 | case ENC_HANT: fz_append_printf(ctx, buf, "/Ming %g Tf\n", size); break; |
1883 | 0 | case ENC_HANS: fz_append_printf(ctx, buf, "/Song %g Tf\n", size); break; |
1884 | 0 | } |
1885 | | |
1886 | 0 | if (state.enc < ENC_KOREAN) |
1887 | 0 | fz_append_byte(ctx, buf, '('); |
1888 | 0 | else |
1889 | 0 | fz_append_byte(ctx, buf, '<'); |
1890 | |
|
1891 | 0 | last_enc = state.enc; |
1892 | 0 | } |
1893 | | |
1894 | 0 | if (state.enc < ENC_KOREAN) |
1895 | 0 | { |
1896 | 0 | if (state.c == '(' || state.c == ')' || state.c == '\\') |
1897 | 0 | fz_append_byte(ctx, buf, '\\'); |
1898 | 0 | fz_append_byte(ctx, buf, state.c); |
1899 | 0 | } |
1900 | 0 | else |
1901 | 0 | { |
1902 | 0 | fz_append_printf(ctx, buf, "%04x", state.c); |
1903 | 0 | } |
1904 | 0 | } |
1905 | | |
1906 | 0 | if (last_enc) |
1907 | 0 | { |
1908 | 0 | if (last_enc < ENC_KOREAN) |
1909 | 0 | fz_append_byte(ctx, buf, ')'); |
1910 | 0 | else |
1911 | 0 | fz_append_byte(ctx, buf, '>'); |
1912 | 0 | fz_append_string(ctx, buf, " Tj\n"); |
1913 | 0 | } |
1914 | 0 | } |
1915 | | |
1916 | | static void |
1917 | | write_string_with_quadding(fz_context *ctx, fz_buffer *buf, |
1918 | | fz_text_language lang, const char *fontname, |
1919 | | fz_font *font, float size, float lineheight, |
1920 | | const char *a, float maxw, int q) |
1921 | 0 | { |
1922 | 0 | const char *b; |
1923 | 0 | float px = 0, x = 0, w; |
1924 | 0 | while (*a) |
1925 | 0 | { |
1926 | 0 | w = break_string(ctx, lang, font, size, a, &b, maxw); |
1927 | 0 | if (b > a) |
1928 | 0 | { |
1929 | 0 | if (q == 0) |
1930 | 0 | x = 0; |
1931 | 0 | else if (q == 1) |
1932 | 0 | x = (maxw - w) / 2; |
1933 | 0 | else |
1934 | 0 | x = (maxw - w); |
1935 | 0 | fz_append_printf(ctx, buf, "%g %g Td\n", x - px, -lineheight); |
1936 | 0 | if (b[-1] == '\n' || b[-1] == '\r') |
1937 | 0 | write_string(ctx, buf, lang, font, fontname, size, a, b-1); |
1938 | 0 | else |
1939 | 0 | write_string(ctx, buf, lang, font, fontname, size, a, b); |
1940 | 0 | a = b; |
1941 | 0 | px = x; |
1942 | 0 | } |
1943 | 0 | } |
1944 | 0 | } |
1945 | | |
1946 | | static void |
1947 | | write_comb_string(fz_context *ctx, fz_buffer *buf, |
1948 | | fz_text_language lang, const char *fontname, |
1949 | | fz_font *font, float size, const char *text, float cell_w) |
1950 | 0 | { |
1951 | 0 | struct text_walk_state state; |
1952 | 0 | int last_enc = 0; |
1953 | 0 | float pad, carry = 0; |
1954 | |
|
1955 | 0 | init_text_walk(ctx, &state, lang, font, text, text + strlen(text)); |
1956 | |
|
1957 | 0 | while (next_text_walk(ctx, &state)) |
1958 | 0 | { |
1959 | 0 | if (state.enc != last_enc) |
1960 | 0 | { |
1961 | 0 | if (last_enc) |
1962 | 0 | fz_append_string(ctx, buf, "] TJ\n"); |
1963 | |
|
1964 | 0 | switch (state.enc) |
1965 | 0 | { |
1966 | 0 | case ENC_LATIN: fz_append_printf(ctx, buf, "/%s %g Tf\n", fontname, size); break; |
1967 | 0 | case ENC_GREEK: fz_append_printf(ctx, buf, "/%sGRK %g Tf\n", fontname, size); break; |
1968 | 0 | case ENC_CYRILLIC: fz_append_printf(ctx, buf, "/%sCYR %g Tf\n", fontname, size); break; |
1969 | 0 | case ENC_KOREAN: fz_append_printf(ctx, buf, "/Batang %g Tf\n", size); break; |
1970 | 0 | case ENC_JAPANESE: fz_append_printf(ctx, buf, "/Mincho %g Tf\n", size); break; |
1971 | 0 | case ENC_HANT: fz_append_printf(ctx, buf, "/Ming %g Tf\n", size); break; |
1972 | 0 | case ENC_HANS: fz_append_printf(ctx, buf, "/Song %g Tf\n", size); break; |
1973 | 0 | } |
1974 | | |
1975 | 0 | fz_append_byte(ctx, buf, '['); |
1976 | |
|
1977 | 0 | last_enc = state.enc; |
1978 | 0 | } |
1979 | | |
1980 | 0 | pad = (cell_w - state.w * 1000) / 2; |
1981 | 0 | fz_append_printf(ctx, buf, "%g", -(carry + pad)); |
1982 | 0 | carry = pad; |
1983 | |
|
1984 | 0 | if (state.enc < ENC_KOREAN) |
1985 | 0 | { |
1986 | 0 | fz_append_byte(ctx, buf, '('); |
1987 | 0 | if (state.c == '(' || state.c == ')' || state.c == '\\') |
1988 | 0 | fz_append_byte(ctx, buf, '\\'); |
1989 | 0 | fz_append_byte(ctx, buf, state.c); |
1990 | 0 | fz_append_byte(ctx, buf, ')'); |
1991 | 0 | } |
1992 | 0 | else |
1993 | 0 | { |
1994 | 0 | fz_append_printf(ctx, buf, "<%04x>", state.c); |
1995 | 0 | } |
1996 | 0 | } |
1997 | 0 | if (last_enc) |
1998 | 0 | fz_append_string(ctx, buf, "] TJ\n"); |
1999 | 0 | } |
2000 | | |
2001 | | static void |
2002 | | layout_comb_string(fz_context *ctx, fz_layout_block *out, float x, float y, |
2003 | | const char *a, const char *b, fz_font *font, float size, float cell_w) |
2004 | 0 | { |
2005 | 0 | int n, c, g; |
2006 | 0 | int first = 1; |
2007 | 0 | float w; |
2008 | 0 | if (a == b) |
2009 | 0 | fz_add_layout_line(ctx, out, x + cell_w / 2, y, size, a); |
2010 | 0 | while (a < b) |
2011 | 0 | { |
2012 | 0 | n = fz_chartorune(&c, a); |
2013 | 0 | c = fz_windows_1252_from_unicode(c); |
2014 | 0 | if (c < 0) c = REPLACEMENT; |
2015 | 0 | g = fz_encode_character(ctx, font, c); |
2016 | 0 | w = fz_advance_glyph(ctx, font, g, 0) * size; |
2017 | 0 | if (first) |
2018 | 0 | { |
2019 | 0 | fz_add_layout_line(ctx, out, x + (cell_w - w) / 2, y, size, a); |
2020 | 0 | first = 0; |
2021 | 0 | } |
2022 | 0 | fz_add_layout_char(ctx, out, x + (cell_w - w) / 2, w, a); |
2023 | 0 | a += n; |
2024 | 0 | x += cell_w; |
2025 | 0 | } |
2026 | 0 | } |
2027 | | |
2028 | | static void |
2029 | | layout_string(fz_context *ctx, fz_layout_block *out, |
2030 | | fz_text_language lang, fz_font *font, float size, |
2031 | | float x, float y, const char *a, const char *b) |
2032 | 0 | { |
2033 | 0 | struct text_walk_state state; |
2034 | 0 | fz_add_layout_line(ctx, out, x, y, size, a); |
2035 | 0 | init_text_walk(ctx, &state, lang, font, a, b); |
2036 | 0 | while (next_text_walk(ctx, &state)) |
2037 | 0 | { |
2038 | 0 | fz_add_layout_char(ctx, out, x, state.w * size, state.text); |
2039 | 0 | x += state.w * size; |
2040 | 0 | } |
2041 | 0 | } |
2042 | | |
2043 | | static void |
2044 | | layout_string_with_quadding(fz_context *ctx, fz_layout_block *out, |
2045 | | fz_text_language lang, fz_font *font, float size, float lineheight, |
2046 | | float xorig, float y, const char *a, float maxw, int q) |
2047 | 0 | { |
2048 | 0 | const char *b; |
2049 | 0 | float x = 0, w; |
2050 | 0 | int add_line_at_end = 0; |
2051 | |
|
2052 | 0 | if (!*a) |
2053 | 0 | add_line_at_end = 1; |
2054 | |
|
2055 | 0 | while (*a) |
2056 | 0 | { |
2057 | 0 | w = break_string(ctx, lang, font, size, a, &b, maxw); |
2058 | 0 | if (b > a) |
2059 | 0 | { |
2060 | 0 | if (q > 0) |
2061 | 0 | { |
2062 | 0 | if (q == 1) |
2063 | 0 | x = (maxw - w) / 2; |
2064 | 0 | else |
2065 | 0 | x = (maxw - w); |
2066 | 0 | } |
2067 | 0 | if (b[-1] == '\n' || b[-1] == '\r') |
2068 | 0 | { |
2069 | 0 | layout_string(ctx, out, lang, font, size, xorig+x, y, a, b-1); |
2070 | 0 | add_line_at_end = 1; |
2071 | 0 | } |
2072 | 0 | else |
2073 | 0 | { |
2074 | 0 | layout_string(ctx, out, lang, font, size, xorig+x, y, a, b); |
2075 | 0 | add_line_at_end = 0; |
2076 | 0 | } |
2077 | 0 | a = b; |
2078 | 0 | y -= lineheight; |
2079 | 0 | } |
2080 | 0 | } |
2081 | 0 | if (add_line_at_end) |
2082 | 0 | fz_add_layout_line(ctx, out, xorig, y, size, a); |
2083 | 0 | } |
2084 | | |
2085 | | static const char *full_font_name(const char **name) |
2086 | 0 | { |
2087 | 0 | if (!strcmp(*name, "Cour")) return "Courier"; |
2088 | 0 | if (!strcmp(*name, "Helv")) return "Helvetica"; |
2089 | 0 | if (!strcmp(*name, "TiRo")) return "Times-Roman"; |
2090 | 0 | if (!strcmp(*name, "Symb")) return "Symbol"; |
2091 | 0 | if (!strcmp(*name, "ZaDb")) return "ZapfDingbats"; |
2092 | 0 | return *name = "Helv", "Helvetica"; |
2093 | 0 | } |
2094 | | |
2095 | | static void |
2096 | | write_variable_text(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, pdf_obj **res, |
2097 | | fz_text_language lang, const char *text, |
2098 | | const char *fontname, float size, int n, float *color, int q, |
2099 | | float w, float h, float padding, float baseline, float lineheight, |
2100 | | int multiline, int comb, int adjust_baseline) |
2101 | 0 | { |
2102 | 0 | fz_font *font; |
2103 | 0 | pdf_obj *res_font; |
2104 | |
|
2105 | 0 | w -= padding * 2; |
2106 | 0 | h -= padding * 2; |
2107 | |
|
2108 | 0 | font = fz_new_base14_font(ctx, full_font_name(&fontname)); |
2109 | 0 | fz_try(ctx) |
2110 | 0 | { |
2111 | 0 | if (!*res) |
2112 | 0 | *res = pdf_new_dict(ctx, annot->page->doc, 1); |
2113 | 0 | res_font = pdf_dict_put_dict(ctx, *res, PDF_NAME(Font), 1); |
2114 | 0 | add_required_fonts(ctx, annot->page->doc, res_font, lang, font, fontname, text); |
2115 | |
|
2116 | 0 | if (size == 0) |
2117 | 0 | { |
2118 | 0 | if (multiline) |
2119 | 0 | size = 12; |
2120 | 0 | else |
2121 | 0 | { |
2122 | 0 | size = w / measure_string(ctx, lang, font, text); |
2123 | 0 | if (size > h) |
2124 | 0 | size = h; |
2125 | 0 | } |
2126 | 0 | } |
2127 | |
|
2128 | 0 | lineheight = size * lineheight; |
2129 | 0 | baseline = size * baseline; |
2130 | |
|
2131 | 0 | if (adjust_baseline) |
2132 | 0 | { |
2133 | | /* Make sure baseline is inside rectangle */ |
2134 | 0 | if (baseline + 0.2f * size > h) |
2135 | 0 | baseline = h - 0.2f * size; |
2136 | 0 | } |
2137 | |
|
2138 | 0 | fz_append_string(ctx, buf, "BT\n"); |
2139 | 0 | write_color0(ctx, buf, n, color, 0); |
2140 | 0 | if (multiline) |
2141 | 0 | { |
2142 | 0 | fz_append_printf(ctx, buf, "%g %g Td\n", padding, padding+h-baseline+lineheight); |
2143 | 0 | write_string_with_quadding(ctx, buf, lang, fontname, font, size, lineheight, text, w, q); |
2144 | 0 | } |
2145 | 0 | else if (comb > 0) |
2146 | 0 | { |
2147 | 0 | float ty = (h - size) / 2; |
2148 | 0 | fz_append_printf(ctx, buf, "%g %g Td\n", padding, padding+h-baseline-ty); |
2149 | 0 | write_comb_string(ctx, buf, lang, fontname, font, size, text, (w * 1000 / size) / comb); |
2150 | 0 | } |
2151 | 0 | else |
2152 | 0 | { |
2153 | 0 | float tx = 0, ty = (h - size) / 2; |
2154 | 0 | if (q > 0) |
2155 | 0 | { |
2156 | 0 | float tw = measure_string(ctx, lang, font, text) * size; |
2157 | 0 | if (q == 1) |
2158 | 0 | tx = (w - tw) / 2; |
2159 | 0 | else |
2160 | 0 | tx = (w - tw); |
2161 | 0 | } |
2162 | 0 | fz_append_printf(ctx, buf, "%g %g Td\n", padding+tx, padding+h-baseline-ty); |
2163 | 0 | write_string(ctx, buf, lang, font, fontname, size, text, text + strlen(text)); |
2164 | 0 | } |
2165 | 0 | fz_append_string(ctx, buf, "ET\n"); |
2166 | 0 | } |
2167 | 0 | fz_always(ctx) |
2168 | 0 | fz_drop_font(ctx, font); |
2169 | 0 | fz_catch(ctx) |
2170 | 0 | fz_rethrow(ctx); |
2171 | 0 | } |
2172 | | |
2173 | | static void |
2174 | | layout_variable_text(fz_context *ctx, fz_layout_block *out, |
2175 | | const char *text, fz_text_language lang, const char *fontname, float size, int q, |
2176 | | float x, float y, float w, float h, float padding, float baseline, float lineheight, |
2177 | | int multiline, int comb, int adjust_baseline) |
2178 | 0 | { |
2179 | 0 | fz_font *font; |
2180 | |
|
2181 | 0 | w -= padding * 2; |
2182 | 0 | h -= padding * 2; |
2183 | |
|
2184 | 0 | font = fz_new_base14_font(ctx, full_font_name(&fontname)); |
2185 | 0 | fz_try(ctx) |
2186 | 0 | { |
2187 | 0 | if (size == 0) |
2188 | 0 | { |
2189 | 0 | if (multiline) |
2190 | 0 | size = 12; |
2191 | 0 | else |
2192 | 0 | { |
2193 | 0 | size = w / measure_string(ctx, lang, font, text); |
2194 | 0 | if (size > h) |
2195 | 0 | size = h; |
2196 | 0 | } |
2197 | 0 | } |
2198 | |
|
2199 | 0 | lineheight = size * lineheight; |
2200 | 0 | baseline = size * baseline; |
2201 | |
|
2202 | 0 | if (adjust_baseline) |
2203 | 0 | { |
2204 | | /* Make sure baseline is inside rectangle */ |
2205 | 0 | if (baseline + 0.2f * size > h) |
2206 | 0 | baseline = h - 0.2f * size; |
2207 | 0 | } |
2208 | |
|
2209 | 0 | if (multiline) |
2210 | 0 | { |
2211 | 0 | x += padding; |
2212 | 0 | y += padding + h - baseline; |
2213 | 0 | layout_string_with_quadding(ctx, out, lang, font, size, lineheight, x, y, text, w, q); |
2214 | 0 | } |
2215 | 0 | else if (comb > 0) |
2216 | 0 | { |
2217 | 0 | float ty = (h - size) / 2; |
2218 | 0 | x += padding; |
2219 | 0 | y += padding + h - baseline - ty; |
2220 | 0 | layout_comb_string(ctx, out, x, y, text, text + strlen(text), font, size, w / comb); |
2221 | 0 | } |
2222 | 0 | else |
2223 | 0 | { |
2224 | 0 | float tx = 0, ty = (h - size) / 2; |
2225 | 0 | if (q > 0) |
2226 | 0 | { |
2227 | 0 | float tw = measure_string(ctx, lang, font, text) * size; |
2228 | 0 | if (q == 1) |
2229 | 0 | tx = (w - tw) / 2; |
2230 | 0 | else |
2231 | 0 | tx = (w - tw); |
2232 | 0 | } |
2233 | 0 | x += padding + tx; |
2234 | 0 | y += padding + h - baseline - ty; |
2235 | 0 | layout_string(ctx, out, lang, font, size, x, y, text, text + strlen(text)); |
2236 | 0 | } |
2237 | 0 | } |
2238 | 0 | fz_always(ctx) |
2239 | 0 | fz_drop_font(ctx, font); |
2240 | 0 | fz_catch(ctx) |
2241 | 0 | fz_rethrow(ctx); |
2242 | 0 | } |
2243 | | |
2244 | | #if FZ_ENABLE_HTML_ENGINE |
2245 | | static void |
2246 | | write_rich_content(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, pdf_obj **res, const char *rc, const char *ds, float size, float w, float h, float b, int multiline) |
2247 | 0 | { |
2248 | | /* border_box is the actual rectangle that we are writing into. |
2249 | | * We use this to feed to the pdfwriting device. */ |
2250 | 0 | fz_rect border_box = { 0, 0, w, h }; |
2251 | | |
2252 | | /* content_box is adjusted for padding and has added height. |
2253 | | * We know a clipping rectangle will have been set to the proper rectangle |
2254 | | * so we can allow text to flow out the bottom of the rectangle rather than |
2255 | | * just missing it out. This matches adobe. */ |
2256 | 0 | fz_rect content_box = fz_make_rect(b, b, w - b*2, h + size * 2); |
2257 | |
|
2258 | 0 | fz_buffer *inbuf = fz_new_buffer_from_copied_data(ctx, (const unsigned char *)rc, strlen(rc)+1); |
2259 | 0 | fz_story *story = NULL; |
2260 | 0 | fz_device *dev = NULL; |
2261 | 0 | fz_buffer *buf2 = NULL; |
2262 | 0 | const char *default_css = "@page{margin:0} body{margin:0;line-height:1.2;white-space:pre-wrap;} p{margin:0}"; |
2263 | 0 | char *css = NULL; |
2264 | |
|
2265 | 0 | fz_var(story); |
2266 | 0 | fz_var(dev); |
2267 | 0 | fz_var(res); |
2268 | 0 | fz_var(buf2); |
2269 | 0 | fz_var(css); |
2270 | | |
2271 | | // single-line should be centered in the box. adjust content box accordingly. |
2272 | | // this matches the math in write_variable_text. |
2273 | 0 | if (!multiline) |
2274 | 0 | { |
2275 | 0 | float ty = ((h - b * 2) - size) / 2; |
2276 | 0 | content_box.y0 = h - b - 0.8f * size + ty; |
2277 | 0 | content_box.y1 = content_box.y0 + size * 2; |
2278 | 0 | } |
2279 | |
|
2280 | 0 | fz_try(ctx) |
2281 | 0 | { |
2282 | 0 | if (ds) |
2283 | 0 | css = fz_asprintf(ctx, "%s body{%s}", default_css, ds); |
2284 | 0 | story = fz_new_story(ctx, inbuf, css ? css : default_css, size, NULL); |
2285 | 0 | dev = pdf_page_write(ctx, annot->page->doc, border_box, res, &buf2); |
2286 | 0 | fz_place_story(ctx, story, content_box, NULL); |
2287 | 0 | fz_draw_story(ctx, story, dev, fz_identity); |
2288 | 0 | fz_close_device(ctx, dev); |
2289 | |
|
2290 | 0 | fz_append_buffer(ctx, buf, buf2); |
2291 | 0 | } |
2292 | 0 | fz_always(ctx) |
2293 | 0 | { |
2294 | 0 | fz_drop_device(ctx, dev); |
2295 | 0 | fz_drop_buffer(ctx, buf2); |
2296 | 0 | fz_drop_story(ctx, story); |
2297 | 0 | fz_drop_buffer(ctx, inbuf); |
2298 | 0 | fz_free(ctx, css); |
2299 | 0 | } |
2300 | 0 | fz_catch(ctx) |
2301 | 0 | fz_rethrow(ctx); |
2302 | 0 | } |
2303 | | #endif |
2304 | | |
2305 | | #if FZ_ENABLE_HTML_ENGINE |
2306 | | |
2307 | | static char * |
2308 | | escape_text(fz_context *ctx, const char *s) |
2309 | 0 | { |
2310 | 0 | size_t len = 1; |
2311 | 0 | char c; |
2312 | 0 | const char *s2; |
2313 | 0 | char *d, *d2; |
2314 | |
|
2315 | 0 | for (s2 = s; (c = *s2++) != 0; len++) |
2316 | 0 | { |
2317 | 0 | if (c == '<') |
2318 | 0 | len += 3; /* < */ |
2319 | 0 | else if (c == '>') |
2320 | 0 | len += 3; /* > */ |
2321 | 0 | else if (c == '&') |
2322 | 0 | len += 4; /* & */ |
2323 | 0 | } |
2324 | |
|
2325 | 0 | d = d2 = fz_malloc(ctx, len); |
2326 | |
|
2327 | 0 | for (s2 = s; (c = *s2++) != 0; ) |
2328 | 0 | { |
2329 | 0 | if (c == '<') |
2330 | 0 | { |
2331 | 0 | *d++ = '&'; |
2332 | 0 | *d++ = 'l'; |
2333 | 0 | *d++ = 't'; |
2334 | 0 | *d++ = ';'; |
2335 | 0 | } |
2336 | 0 | else if (c == '>') |
2337 | 0 | { |
2338 | 0 | *d++ = '&'; |
2339 | 0 | *d++ = 'g'; |
2340 | 0 | *d++ = 't'; |
2341 | 0 | *d++ = ';'; |
2342 | 0 | } |
2343 | 0 | else if (c == '&') |
2344 | 0 | { |
2345 | 0 | *d++ = '&'; |
2346 | 0 | *d++ = 'a'; |
2347 | 0 | *d++ = 'm'; |
2348 | 0 | *d++ = 'p'; |
2349 | 0 | *d++ = ';'; |
2350 | 0 | } |
2351 | 0 | else |
2352 | 0 | *d++ = c; |
2353 | 0 | } |
2354 | 0 | *d++ = 0; |
2355 | |
|
2356 | 0 | return d2; |
2357 | 0 | } |
2358 | | |
2359 | | int text_needs_rich_layout(fz_context *ctx, const char *s) |
2360 | 0 | { |
2361 | 0 | int c, script; |
2362 | 0 | while (*s) |
2363 | 0 | { |
2364 | 0 | s += fz_chartorune(&c, s); |
2365 | | |
2366 | | // base 14 fonts |
2367 | 0 | if (fz_windows_1252_from_unicode(c) > 0) |
2368 | 0 | continue; |
2369 | 0 | if (fz_iso8859_7_from_unicode(c) > 0) |
2370 | 0 | continue; |
2371 | 0 | if (fz_koi8u_from_unicode(c) > 0) |
2372 | 0 | continue; |
2373 | | |
2374 | | // cjk fonts |
2375 | 0 | script = ucdn_get_script(c); |
2376 | 0 | if ( |
2377 | 0 | script == UCDN_SCRIPT_HANGUL || |
2378 | 0 | script == UCDN_SCRIPT_HIRAGANA || |
2379 | 0 | script == UCDN_SCRIPT_KATAKANA || |
2380 | 0 | script == UCDN_SCRIPT_BOPOMOFO || |
2381 | 0 | script == UCDN_SCRIPT_HAN |
2382 | 0 | ) |
2383 | 0 | continue; |
2384 | | |
2385 | 0 | return 1; |
2386 | 0 | } |
2387 | 0 | return 0; |
2388 | 0 | } |
2389 | | |
2390 | | static unsigned int hex_from_color(fz_context *ctx, int n, float color[4]) |
2391 | 0 | { |
2392 | 0 | float rgb[4]; |
2393 | 0 | int r, g, b; |
2394 | 0 | switch (n) |
2395 | 0 | { |
2396 | 0 | default: |
2397 | 0 | r = g = b = 0; |
2398 | 0 | break; |
2399 | 0 | case 1: |
2400 | 0 | r = g = b = color[0] * 255; |
2401 | 0 | break; |
2402 | 0 | case 3: |
2403 | 0 | r = color[0] * 255; |
2404 | 0 | g = color[1] * 255; |
2405 | 0 | b = color[2] * 255; |
2406 | 0 | break; |
2407 | 0 | case 4: |
2408 | 0 | fz_convert_color(ctx, fz_device_cmyk(ctx), color, fz_device_rgb(ctx), rgb, NULL, fz_default_color_params); |
2409 | 0 | r = rgb[0] * 255; |
2410 | 0 | g = rgb[1] * 255; |
2411 | 0 | b = rgb[2] * 255; |
2412 | 0 | break; |
2413 | 0 | } |
2414 | 0 | return (r<<16) | (g<<8) | b; |
2415 | 0 | } |
2416 | | |
2417 | | #endif |
2418 | | |
2419 | | static float |
2420 | | pdf_write_line_caption(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, pdf_obj **res, fz_point a, fz_point b) |
2421 | 0 | { |
2422 | 0 | float dx, dy, line_length; |
2423 | 0 | fz_point co; |
2424 | 0 | float size; |
2425 | 0 | const char *text; |
2426 | 0 | pdf_obj *res_font; |
2427 | 0 | fz_font *font = NULL; |
2428 | 0 | fz_matrix tm; |
2429 | 0 | int lang; |
2430 | 0 | int top; |
2431 | 0 | float tw; |
2432 | |
|
2433 | 0 | fz_var(font); |
2434 | |
|
2435 | 0 | fz_try(ctx) |
2436 | 0 | { |
2437 | | // vector of line |
2438 | 0 | dx = b.x - a.x; |
2439 | 0 | dy = b.y - a.y; |
2440 | 0 | line_length = hypotf(dx, dy); |
2441 | 0 | dx /= line_length; |
2442 | 0 | dy /= line_length; |
2443 | |
|
2444 | 0 | text = pdf_annot_contents(ctx, annot); |
2445 | 0 | lang = pdf_annot_language(ctx, annot); |
2446 | 0 | co = pdf_dict_get_point(ctx, annot->obj, PDF_NAME(CO)); |
2447 | |
|
2448 | 0 | font = fz_new_base14_font(ctx, "Helvetica"); |
2449 | 0 | if (!*res) |
2450 | 0 | *res = pdf_new_dict(ctx, annot->page->doc, 1); |
2451 | 0 | res_font = pdf_dict_put_dict(ctx, *res, PDF_NAME(Font), 1); |
2452 | 0 | add_required_fonts(ctx, annot->page->doc, res_font, lang, font, "Helv", text); |
2453 | 0 | size = 12; |
2454 | |
|
2455 | 0 | tw = measure_string(ctx, lang, font, text) * size; |
2456 | | |
2457 | | // don't inline if CP says so |
2458 | 0 | top = 0; |
2459 | 0 | if (pdf_dict_get(ctx, annot->obj, PDF_NAME(CP)) == PDF_NAME(Top)) |
2460 | 0 | top = 1; |
2461 | | |
2462 | | // don't inline if caption wouldn't fit |
2463 | 0 | if (tw + size > line_length) |
2464 | 0 | top = 1; |
2465 | |
|
2466 | 0 | tm = fz_rotate(atan2(dy, dx) * 180 / M_PI); |
2467 | 0 | tm.e = (a.x + b.x) / 2 - dx * (tw / 2); |
2468 | 0 | tm.f = (a.y + b.y) / 2 - dy * (tw / 2); |
2469 | | |
2470 | | // caption offset |
2471 | 0 | if (co.x || co.y) |
2472 | 0 | { |
2473 | | // don't write text inline |
2474 | 0 | top = 1; |
2475 | |
|
2476 | 0 | if (co.y < 0) |
2477 | 0 | co.y -= size; |
2478 | |
|
2479 | 0 | tm.e += co.x * dx - co.y * dy; |
2480 | 0 | tm.f += co.x * dy + co.y * dx; |
2481 | 0 | } |
2482 | 0 | else if (top) |
2483 | 0 | { |
2484 | 0 | tm.e -= dy * size * 0.2f; |
2485 | 0 | tm.f += dx * size * 0.2f; |
2486 | 0 | } |
2487 | 0 | else |
2488 | 0 | { |
2489 | 0 | tm.e += dy * size * 0.3f; |
2490 | 0 | tm.f -= dx * size * 0.3f; |
2491 | 0 | } |
2492 | |
|
2493 | 0 | fz_append_printf(ctx, buf, "q\n%M cm\n", &tm); |
2494 | 0 | fz_append_string(ctx, buf, "0 g\n"); // Acrobat always draws captions in black |
2495 | 0 | fz_append_printf(ctx, buf, "BT\n"); |
2496 | 0 | write_string(ctx, buf, lang, font, "Helv", size, text, text + strlen(text)); |
2497 | 0 | fz_append_printf(ctx, buf, "ET\n"); |
2498 | 0 | fz_append_printf(ctx, buf, "Q\n"); |
2499 | |
|
2500 | 0 | *rect = fz_include_point_in_rect(*rect, fz_make_point(tm.e - dx * tw/2, tm.f - dy * tw/2)); |
2501 | 0 | *rect = fz_include_point_in_rect(*rect, fz_make_point(tm.e + dx * tw/2, tm.f + dy * tw/2)); |
2502 | 0 | *rect = fz_expand_rect(*rect, size); |
2503 | 0 | } |
2504 | 0 | fz_always(ctx) |
2505 | 0 | { |
2506 | 0 | fz_drop_font(ctx, font); |
2507 | 0 | } |
2508 | 0 | fz_catch(ctx) |
2509 | 0 | fz_rethrow(ctx); |
2510 | | |
2511 | 0 | if (!top) |
2512 | 0 | return (tw + size / 2) / 2; |
2513 | 0 | return 0; |
2514 | 0 | } |
2515 | | |
2516 | | static void |
2517 | | pdf_write_free_text_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, |
2518 | | fz_rect *rect, fz_rect *bbox, fz_matrix *matrix, pdf_obj **res) |
2519 | 0 | { |
2520 | 0 | const char *font; |
2521 | 0 | float size, color[4]; |
2522 | 0 | const char *text; |
2523 | 0 | float w, h, b; |
2524 | 0 | int q, r, n; |
2525 | 0 | int lang; |
2526 | 0 | fz_rect rd; |
2527 | 0 | pdf_obj *le; |
2528 | 0 | int ic; |
2529 | 0 | fz_rect text_box; |
2530 | 0 | fz_matrix tfm; |
2531 | 0 | #if FZ_ENABLE_HTML_ENGINE |
2532 | 0 | const char *rc, *ds; |
2533 | 0 | char *free_rc = NULL; |
2534 | 0 | char ds_buf[400]; |
2535 | 0 | #endif |
2536 | |
|
2537 | 0 | text = pdf_annot_contents(ctx, annot); |
2538 | 0 | q = pdf_annot_quadding(ctx, annot); |
2539 | 0 | pdf_annot_default_appearance(ctx, annot, &font, &size, &n, color); |
2540 | 0 | lang = pdf_annot_language(ctx, annot); |
2541 | 0 | rd = pdf_annot_rect_diff(ctx, annot); |
2542 | | |
2543 | | /* /Rotate is an undocumented annotation property supported by Adobe. |
2544 | | * When Rotate is used, neither the box, nor the arrow move at all. |
2545 | | * Only the position of the text moves within the box. Thus we don't |
2546 | | * need to adjust rd at all! */ |
2547 | 0 | r = pdf_dict_get_int(ctx, annot->obj, PDF_NAME(Rotate)); |
2548 | | |
2549 | | // Adjust input Rect for RD to get the bounds of the text box area |
2550 | 0 | text_box = *rect; |
2551 | 0 | text_box.x0 += rd.x0; |
2552 | 0 | text_box.y0 += rd.y0; |
2553 | 0 | text_box.x1 -= rd.x1; |
2554 | 0 | text_box.y1 -= rd.y1; |
2555 | |
|
2556 | 0 | *rect = text_box; |
2557 | | |
2558 | | // Size of text box area (including padding and border) |
2559 | 0 | w = text_box.x1 - text_box.x0; |
2560 | 0 | h = text_box.y1 - text_box.y0; |
2561 | |
|
2562 | 0 | pdf_write_opacity(ctx, annot, buf, res); |
2563 | 0 | pdf_write_dash_pattern(ctx, annot, buf, res); |
2564 | | |
2565 | | // Set stroke and fill colors for box and callout line |
2566 | 0 | ic = pdf_write_fill_color_appearance(ctx, annot, buf); |
2567 | 0 | write_color0(ctx, buf, n, color, 1); |
2568 | 0 | b = pdf_write_border_appearance(ctx, annot, buf); |
2569 | | |
2570 | | // Draw Callout line |
2571 | 0 | if (pdf_name_eq(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME(IT)), PDF_NAME(FreeTextCallout))) |
2572 | 0 | { |
2573 | 0 | pdf_obj *cl = pdf_dict_get(ctx, annot->obj, PDF_NAME(CL)); |
2574 | 0 | int i, len = pdf_array_len(ctx, cl); |
2575 | 0 | if (len == 4 || len == 6) |
2576 | 0 | { |
2577 | 0 | float xy[6]; |
2578 | |
|
2579 | 0 | for (i = 0; i < len; i += 2) |
2580 | 0 | { |
2581 | 0 | float x = xy[i+0] = pdf_array_get_real(ctx, cl, i+0); |
2582 | 0 | float y = xy[i+1] = pdf_array_get_real(ctx, cl, i+1); |
2583 | 0 | if (x < rect->x0) rect->x0 = x; |
2584 | 0 | if (y < rect->y0) rect->y0 = y; |
2585 | 0 | if (x > rect->x1) rect->x1 = x; |
2586 | 0 | if (y > rect->y1) rect->y1 = y; |
2587 | 0 | } |
2588 | |
|
2589 | 0 | fz_append_printf(ctx, buf, "%g %g m\n", xy[0], xy[1]); |
2590 | 0 | for (i = 2; i < len; i += 2) |
2591 | 0 | fz_append_printf(ctx, buf, "%g %g l\n", xy[i+0], xy[i+1]); |
2592 | 0 | fz_append_printf(ctx, buf, "S\n"); |
2593 | |
|
2594 | 0 | le = pdf_dict_get(ctx, annot->obj, PDF_NAME(LE)); |
2595 | 0 | pdf_write_line_cap_appearance(ctx, buf, rect, |
2596 | 0 | xy[0], xy[1], |
2597 | 0 | xy[2] - xy[0], xy[3] - xy[1], b, |
2598 | 0 | 1, ic, le); |
2599 | 0 | } |
2600 | 0 | } |
2601 | | |
2602 | | // Draw text box background |
2603 | 0 | if (ic) |
2604 | 0 | fz_append_printf(ctx, buf, "%g %g %g %g re\nf\n", text_box.x0, text_box.y0, w, h); |
2605 | | |
2606 | | // Draw text box border |
2607 | 0 | if (b > 0) |
2608 | 0 | fz_append_printf(ctx, buf, "%g %g %g %g re\nS\n", text_box.x0 + b/2, text_box.y0 + b/2, w - b, h - b); |
2609 | | |
2610 | | // Clip text to box |
2611 | 0 | fz_append_printf(ctx, buf, "%g %g %g %g re\nW\nn\n", text_box.x0 + b, text_box.y0 + b, w - b * 2, h - b * 2); |
2612 | | |
2613 | | // Recompute Rect and RD to account for Callout line |
2614 | 0 | rd.x0 = text_box.x0 - rect->x0; |
2615 | 0 | rd.y0 = text_box.y0 - rect->y0; |
2616 | 0 | rd.x1 = rect->x1 - text_box.x1; |
2617 | 0 | rd.y1 = rect->y1 - text_box.y1; |
2618 | | |
2619 | | // Compute rotation (and offset) transform for text |
2620 | 0 | if (r == 90 || r == 270) |
2621 | 0 | { |
2622 | 0 | float t = w; w = h; h = t; |
2623 | 0 | } |
2624 | 0 | tfm = fz_rotate(r); |
2625 | 0 | if (r == 270) |
2626 | 0 | tfm.e += 0, tfm.f += w; |
2627 | 0 | else if (r == 90) |
2628 | 0 | tfm.e += h, tfm.f -= 0; |
2629 | 0 | else if (r == 180) |
2630 | 0 | tfm.e += w, tfm.f += h; |
2631 | 0 | tfm.e += text_box.x0; |
2632 | 0 | tfm.f += text_box.y0; |
2633 | 0 | fz_append_printf(ctx, buf, "q\n%g %g %g %g %g %g cm\n", tfm.a, tfm.b, tfm.c, tfm.d, tfm.e, tfm.f); |
2634 | |
|
2635 | 0 | #if FZ_ENABLE_HTML_ENGINE |
2636 | 0 | ds = pdf_dict_get_text_string_opt(ctx, annot->obj, PDF_NAME(DS)); |
2637 | 0 | rc = pdf_dict_get_text_string_opt(ctx, annot->obj, PDF_NAME(RC)); |
2638 | 0 | if (!rc && (ds || text_needs_rich_layout(ctx, text))) |
2639 | 0 | { |
2640 | 0 | rc = free_rc = escape_text(ctx, text); |
2641 | 0 | if (!ds) |
2642 | 0 | { |
2643 | 0 | fz_snprintf(ds_buf, sizeof ds_buf, |
2644 | 0 | "font-family:%s;font-size:%gpt;color:#%06x;text-align:%s;", |
2645 | 0 | full_font_name(&font), |
2646 | 0 | size, |
2647 | 0 | hex_from_color(ctx, n, color), |
2648 | 0 | (q == 0 ? "left" : q == 1 ? "center" : "right") |
2649 | 0 | ); |
2650 | 0 | ds = ds_buf; |
2651 | 0 | } |
2652 | 0 | } |
2653 | 0 | if (rc) |
2654 | 0 | { |
2655 | 0 | fz_try(ctx) |
2656 | 0 | write_rich_content(ctx, annot, buf, res, rc ? rc : text, ds, size, w, h, b * 2, 1); |
2657 | 0 | fz_always(ctx) |
2658 | 0 | fz_free(ctx, free_rc); |
2659 | 0 | fz_catch(ctx) |
2660 | 0 | fz_rethrow(ctx); |
2661 | 0 | } |
2662 | 0 | else |
2663 | 0 | #endif |
2664 | 0 | { |
2665 | 0 | write_variable_text(ctx, annot, buf, res, lang, text, font, size, n, color, q, w, h, b*2, |
2666 | 0 | 0.8f, 1.2f, 1, 0, 0); |
2667 | 0 | } |
2668 | | |
2669 | 0 | fz_append_printf(ctx, buf, "Q\n"); |
2670 | |
|
2671 | 0 | pdf_dict_put_rect(ctx, annot->obj, PDF_NAME(RD), rd); |
2672 | |
|
2673 | 0 | *matrix = fz_identity; |
2674 | 0 | *bbox = *rect; |
2675 | 0 | } |
2676 | | |
2677 | | static void |
2678 | | pdf_write_tx_widget_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, |
2679 | | const fz_rect *rect, fz_rect *bbox, fz_matrix *matrix, pdf_obj **res, |
2680 | | const char *text, int ff) |
2681 | 0 | { |
2682 | 0 | fz_text_language lang; |
2683 | 0 | const char *font; |
2684 | 0 | float size, color[4]; |
2685 | 0 | float w, h, t, b; |
2686 | 0 | int has_bc = 0; |
2687 | 0 | int q, r, n; |
2688 | |
|
2689 | 0 | #if FZ_ENABLE_HTML_ENGINE |
2690 | 0 | const char *rc, *ds; |
2691 | 0 | char *free_rc = NULL; |
2692 | 0 | char ds_buf[400]; |
2693 | 0 | #endif |
2694 | |
|
2695 | 0 | r = pdf_dict_get_int(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME(MK)), PDF_NAME(R)); |
2696 | 0 | q = pdf_annot_quadding(ctx, annot); |
2697 | 0 | pdf_annot_default_appearance(ctx, annot, &font, &size, &n, color); |
2698 | 0 | lang = pdf_annot_language(ctx, annot); |
2699 | |
|
2700 | 0 | w = rect->x1 - rect->x0; |
2701 | 0 | h = rect->y1 - rect->y0; |
2702 | 0 | r = r % 360; |
2703 | 0 | if (r == 90 || r == 270) |
2704 | 0 | t = h, h = w, w = t; |
2705 | 0 | *matrix = fz_rotate(r); |
2706 | 0 | *bbox = fz_make_rect(0, 0, w, h); |
2707 | |
|
2708 | 0 | fz_append_string(ctx, buf, "/Tx BMC\nq\n"); |
2709 | |
|
2710 | 0 | if (pdf_write_MK_BG_appearance(ctx, annot, buf)) |
2711 | 0 | fz_append_printf(ctx, buf, "0 0 %g %g re\nf\n", w, h); |
2712 | |
|
2713 | 0 | b = pdf_write_border_appearance(ctx, annot, buf); |
2714 | 0 | if (b > 0 && pdf_write_MK_BC_appearance(ctx, annot, buf)) |
2715 | 0 | { |
2716 | 0 | fz_append_printf(ctx, buf, "%g %g %g %g re\ns\n", b/2, b/2, w-b, h-b); |
2717 | 0 | has_bc = 1; |
2718 | 0 | } |
2719 | |
|
2720 | 0 | fz_append_printf(ctx, buf, "%g %g %g %g re\nW\nn\n", b, b, w-b*2, h-b*2); |
2721 | |
|
2722 | 0 | #if FZ_ENABLE_HTML_ENGINE |
2723 | 0 | ds = pdf_dict_get_text_string_opt(ctx, annot->obj, PDF_NAME(DS)); |
2724 | 0 | rc = pdf_dict_get_text_string_opt(ctx, annot->obj, PDF_NAME(RV)); |
2725 | 0 | if (!rc && (ds || text_needs_rich_layout(ctx, text))) |
2726 | 0 | { |
2727 | 0 | rc = free_rc = escape_text(ctx, text); |
2728 | 0 | if (!ds) |
2729 | 0 | { |
2730 | 0 | fz_snprintf(ds_buf, sizeof ds_buf, |
2731 | 0 | "font-family:%s;font-size:%gpt;color:#%06x;text-align:%s", |
2732 | 0 | full_font_name(&font), |
2733 | 0 | size, |
2734 | 0 | hex_from_color(ctx, n, color), |
2735 | 0 | (q == 0 ? "left" : q == 1 ? "center" : "right") |
2736 | 0 | ); |
2737 | 0 | ds = ds_buf; |
2738 | 0 | } |
2739 | 0 | } |
2740 | 0 | if (rc) |
2741 | 0 | { |
2742 | 0 | fz_try(ctx) |
2743 | 0 | write_rich_content(ctx, annot, buf, res, rc ? rc : text, ds, size, w, h, b * 2, |
2744 | 0 | (ff & PDF_TX_FIELD_IS_MULTILINE)); |
2745 | 0 | fz_always(ctx) |
2746 | 0 | fz_free(ctx, free_rc); |
2747 | 0 | fz_catch(ctx) |
2748 | 0 | fz_rethrow(ctx); |
2749 | 0 | } |
2750 | 0 | else |
2751 | 0 | #endif |
2752 | | |
2753 | 0 | if (ff & PDF_TX_FIELD_IS_MULTILINE) |
2754 | 0 | { |
2755 | 0 | write_variable_text(ctx, annot, buf, res, lang, text, font, size, n, color, q, w, h, b*2, |
2756 | 0 | 1.116f, 1.116f, 1, 0, 1); |
2757 | 0 | } |
2758 | 0 | else if (ff & PDF_TX_FIELD_IS_COMB) |
2759 | 0 | { |
2760 | 0 | int maxlen = pdf_dict_get_inheritable_int(ctx, annot->obj, PDF_NAME(MaxLen)); |
2761 | 0 | if (has_bc && maxlen > 1) |
2762 | 0 | { |
2763 | 0 | float cell_w = (w - 2 * b) / maxlen; |
2764 | 0 | int i; |
2765 | 0 | for (i = 1; i < maxlen; ++i) |
2766 | 0 | { |
2767 | 0 | float x = b + cell_w * i; |
2768 | 0 | fz_append_printf(ctx, buf, "%g %g m %g %g l s\n", x, b, x, h-b); |
2769 | 0 | } |
2770 | 0 | } |
2771 | 0 | write_variable_text(ctx, annot, buf, res, lang, text, font, size, n, color, q, w, h, 0, |
2772 | 0 | 0.8f, 1.2f, 0, maxlen, 0); |
2773 | 0 | } |
2774 | 0 | else |
2775 | 0 | { |
2776 | 0 | write_variable_text(ctx, annot, buf, res, lang, text, font, size, n, color, q, w, h, b*2, |
2777 | 0 | 0.8f, 1.2f, 0, 0, 0); |
2778 | 0 | } |
2779 | | |
2780 | 0 | fz_append_string(ctx, buf, "Q\nEMC\n"); |
2781 | 0 | } |
2782 | | |
2783 | | fz_layout_block * |
2784 | | pdf_layout_text_widget(fz_context *ctx, pdf_annot *annot) |
2785 | 0 | { |
2786 | 0 | fz_text_language lang; |
2787 | 0 | fz_layout_block *out; |
2788 | 0 | const char *font; |
2789 | 0 | const char *text; |
2790 | 0 | fz_rect rect; |
2791 | 0 | float size, color[4]; |
2792 | 0 | float w, h, t, b, x, y; |
2793 | 0 | int q, r, n; |
2794 | 0 | int ff; |
2795 | |
|
2796 | 0 | rect = pdf_dict_get_rect(ctx, annot->obj, PDF_NAME(Rect)); |
2797 | 0 | text = pdf_field_value(ctx, annot->obj); |
2798 | 0 | ff = pdf_field_flags(ctx, annot->obj); |
2799 | |
|
2800 | 0 | b = pdf_annot_border_width(ctx, annot); |
2801 | 0 | r = pdf_dict_get_int(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME(MK)), PDF_NAME(R)); |
2802 | 0 | q = pdf_annot_quadding(ctx, annot); |
2803 | 0 | pdf_annot_default_appearance(ctx, annot, &font, &size, &n, color); |
2804 | 0 | lang = pdf_annot_language(ctx, annot); |
2805 | |
|
2806 | 0 | w = rect.x1 - rect.x0; |
2807 | 0 | h = rect.y1 - rect.y0; |
2808 | 0 | r = r % 360; |
2809 | 0 | if (r == 90 || r == 270) |
2810 | 0 | t = h, h = w, w = t; |
2811 | |
|
2812 | 0 | x = rect.x0; |
2813 | 0 | y = rect.y0; |
2814 | |
|
2815 | 0 | out = fz_new_layout(ctx); |
2816 | 0 | fz_try(ctx) |
2817 | 0 | { |
2818 | 0 | pdf_page_transform(ctx, annot->page, NULL, &out->matrix); |
2819 | 0 | out->matrix = fz_concat(out->matrix, fz_rotate(r)); |
2820 | 0 | out->inv_matrix = fz_invert_matrix(out->matrix); |
2821 | |
|
2822 | 0 | if (ff & PDF_TX_FIELD_IS_MULTILINE) |
2823 | 0 | { |
2824 | 0 | layout_variable_text(ctx, out, text, lang, font, size, q, x, y, w, h, b*2, 1.116f, 1.116f, 1, 0, 1); |
2825 | 0 | } |
2826 | 0 | else if (ff & PDF_TX_FIELD_IS_COMB) |
2827 | 0 | { |
2828 | 0 | int maxlen = pdf_dict_get_inheritable_int(ctx, annot->obj, PDF_NAME(MaxLen)); |
2829 | 0 | layout_variable_text(ctx, out, text, lang, font, size, q, x, y, w, h, 0, 0.8f, 1.2f, 0, maxlen, 0); |
2830 | 0 | } |
2831 | 0 | else |
2832 | 0 | { |
2833 | 0 | layout_variable_text(ctx, out, text, lang, font, size, q, x, y, w, h, b*2, 0.8f, 1.2f, 0, 0, 0); |
2834 | 0 | } |
2835 | 0 | } |
2836 | 0 | fz_catch(ctx) |
2837 | 0 | { |
2838 | 0 | fz_drop_layout(ctx, out); |
2839 | 0 | fz_rethrow(ctx); |
2840 | 0 | } |
2841 | 0 | return out; |
2842 | 0 | } |
2843 | | |
2844 | | static void |
2845 | | pdf_write_ch_widget_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, |
2846 | | const fz_rect *rect, fz_rect *bbox, fz_matrix *matrix, pdf_obj **res) |
2847 | 0 | { |
2848 | 0 | int ff = pdf_field_flags(ctx, annot->obj); |
2849 | 0 | if (ff & PDF_CH_FIELD_IS_COMBO) |
2850 | 0 | { |
2851 | | /* TODO: Pop-down arrow */ |
2852 | 0 | pdf_write_tx_widget_appearance(ctx, annot, buf, rect, bbox, matrix, res, |
2853 | 0 | pdf_field_value(ctx, annot->obj), 0); |
2854 | 0 | } |
2855 | 0 | else |
2856 | 0 | { |
2857 | 0 | fz_buffer *text = fz_new_buffer(ctx, 1024); |
2858 | 0 | fz_try(ctx) |
2859 | 0 | { |
2860 | 0 | pdf_obj *opt = pdf_dict_get(ctx, annot->obj, PDF_NAME(Opt)); |
2861 | 0 | int i = pdf_dict_get_int(ctx, annot->obj, PDF_NAME(TI)); |
2862 | 0 | int n = pdf_array_len(ctx, opt); |
2863 | | /* TODO: Scrollbar */ |
2864 | | /* TODO: Highlight selected items */ |
2865 | 0 | if (i < 0) |
2866 | 0 | i = 0; |
2867 | 0 | for (; i < n; ++i) |
2868 | 0 | { |
2869 | 0 | pdf_obj *val = pdf_array_get(ctx, opt, i); |
2870 | 0 | if (pdf_is_array(ctx, val)) |
2871 | 0 | fz_append_string(ctx, text, pdf_array_get_text_string(ctx, val, 1)); |
2872 | 0 | else |
2873 | 0 | fz_append_string(ctx, text, pdf_to_text_string(ctx, val)); |
2874 | 0 | fz_append_byte(ctx, text, '\n'); |
2875 | 0 | } |
2876 | 0 | pdf_write_tx_widget_appearance(ctx, annot, buf, rect, bbox, matrix, res, |
2877 | 0 | fz_string_from_buffer(ctx, text), PDF_TX_FIELD_IS_MULTILINE); |
2878 | 0 | } |
2879 | 0 | fz_always(ctx) |
2880 | 0 | fz_drop_buffer(ctx, text); |
2881 | 0 | fz_catch(ctx) |
2882 | 0 | fz_rethrow(ctx); |
2883 | 0 | } |
2884 | 0 | } |
2885 | | |
2886 | | static void |
2887 | | pdf_write_sig_widget_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, |
2888 | | const fz_rect *rect, fz_rect *bbox, fz_matrix *matrix, pdf_obj **res) |
2889 | 0 | { |
2890 | 0 | float x0 = rect->x0 + 1; |
2891 | 0 | float y0 = rect->y0 + 1; |
2892 | 0 | float x1 = rect->x1 - 1; |
2893 | 0 | float y1 = rect->y1 - 1; |
2894 | 0 | float w = x1 - x0; |
2895 | 0 | float h = y1 - y0; |
2896 | 0 | fz_append_printf(ctx, buf, "1 w\n0 G\n"); |
2897 | 0 | fz_append_printf(ctx, buf, "%g %g %g %g re\n", x0, y0, w, h); |
2898 | 0 | fz_append_printf(ctx, buf, "%g %g m %g %g l\n", x0, y0, x1, y1); |
2899 | 0 | fz_append_printf(ctx, buf, "%g %g m %g %g l\n", x1, y0, x0, y1); |
2900 | 0 | fz_append_printf(ctx, buf, "s\n"); |
2901 | 0 | *bbox = *rect; |
2902 | 0 | *matrix = fz_identity; |
2903 | 0 | } |
2904 | | |
2905 | | static void |
2906 | | pdf_write_widget_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, |
2907 | | fz_rect *rect, fz_rect *bbox, fz_matrix *matrix, pdf_obj **res) |
2908 | 0 | { |
2909 | 0 | pdf_obj *ft = pdf_dict_get_inheritable(ctx, annot->obj, PDF_NAME(FT)); |
2910 | 0 | if (pdf_name_eq(ctx, ft, PDF_NAME(Tx))) |
2911 | 0 | { |
2912 | 0 | int ff = pdf_field_flags(ctx, annot->obj); |
2913 | 0 | char *format = NULL; |
2914 | 0 | const char *text = NULL; |
2915 | 0 | if (!annot->ignore_trigger_events) |
2916 | 0 | { |
2917 | 0 | format = pdf_field_event_format(ctx, annot->page->doc, annot->obj); |
2918 | 0 | if (format) |
2919 | 0 | text = format; |
2920 | 0 | else |
2921 | 0 | text = pdf_field_value(ctx, annot->obj); |
2922 | 0 | } |
2923 | 0 | else |
2924 | 0 | { |
2925 | 0 | text = pdf_field_value(ctx, annot->obj); |
2926 | 0 | } |
2927 | 0 | fz_try(ctx) |
2928 | 0 | pdf_write_tx_widget_appearance(ctx, annot, buf, rect, bbox, matrix, res, text, ff); |
2929 | 0 | fz_always(ctx) |
2930 | 0 | fz_free(ctx, format); |
2931 | 0 | fz_catch(ctx) |
2932 | 0 | fz_rethrow(ctx); |
2933 | 0 | } |
2934 | 0 | else if (pdf_name_eq(ctx, ft, PDF_NAME(Ch))) |
2935 | 0 | { |
2936 | 0 | pdf_write_ch_widget_appearance(ctx, annot, buf, rect, bbox, matrix, res); |
2937 | 0 | } |
2938 | 0 | else if (pdf_name_eq(ctx, ft, PDF_NAME(Sig))) |
2939 | 0 | { |
2940 | 0 | pdf_write_sig_widget_appearance(ctx, annot, buf, rect, bbox, matrix, res); |
2941 | 0 | } |
2942 | 0 | else |
2943 | 0 | { |
2944 | 0 | fz_throw(ctx, FZ_ERROR_ARGUMENT, "cannot create appearance stream for %s widgets", pdf_to_name(ctx, ft)); |
2945 | 0 | } |
2946 | 0 | } |
2947 | | |
2948 | | static void |
2949 | | pdf_write_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, |
2950 | | fz_rect *rect, fz_rect *bbox, fz_matrix *matrix, pdf_obj **res) |
2951 | 0 | { |
2952 | 0 | switch (pdf_annot_type(ctx, annot)) |
2953 | 0 | { |
2954 | 0 | default: |
2955 | 0 | fz_throw(ctx, FZ_ERROR_UNSUPPORTED, "cannot create appearance stream for %s annotations", |
2956 | 0 | pdf_dict_get_name(ctx, annot->obj, PDF_NAME(Subtype))); |
2957 | 0 | case PDF_ANNOT_WIDGET: |
2958 | 0 | pdf_write_widget_appearance(ctx, annot, buf, rect, bbox, matrix, res); |
2959 | 0 | break; |
2960 | 0 | case PDF_ANNOT_INK: |
2961 | 0 | pdf_write_ink_appearance(ctx, annot, buf, rect, res); |
2962 | 0 | *matrix = fz_identity; |
2963 | 0 | *bbox = *rect; |
2964 | 0 | break; |
2965 | 0 | case PDF_ANNOT_POLYGON: |
2966 | 0 | pdf_write_polygon_appearance(ctx, annot, buf, rect, res, 1); |
2967 | 0 | *matrix = fz_identity; |
2968 | 0 | *bbox = *rect; |
2969 | 0 | break; |
2970 | 0 | case PDF_ANNOT_POLY_LINE: |
2971 | 0 | pdf_write_polygon_appearance(ctx, annot, buf, rect, res, 0); |
2972 | 0 | *matrix = fz_identity; |
2973 | 0 | *bbox = *rect; |
2974 | 0 | break; |
2975 | 0 | case PDF_ANNOT_LINE: |
2976 | 0 | pdf_write_line_appearance(ctx, annot, buf, rect, res); |
2977 | 0 | *matrix = fz_identity; |
2978 | 0 | *bbox = *rect; |
2979 | 0 | break; |
2980 | 0 | case PDF_ANNOT_SQUARE: |
2981 | 0 | pdf_write_square_appearance(ctx, annot, buf, rect, res); |
2982 | 0 | *matrix = fz_identity; |
2983 | 0 | *bbox = *rect; |
2984 | 0 | break; |
2985 | 0 | case PDF_ANNOT_CIRCLE: |
2986 | 0 | pdf_write_circle_appearance(ctx, annot, buf, rect, res); |
2987 | 0 | *matrix = fz_identity; |
2988 | 0 | *bbox = *rect; |
2989 | 0 | break; |
2990 | 0 | case PDF_ANNOT_CARET: |
2991 | 0 | pdf_write_caret_appearance(ctx, annot, buf, rect, bbox, res); |
2992 | 0 | *matrix = fz_identity; |
2993 | 0 | break; |
2994 | 0 | case PDF_ANNOT_TEXT: |
2995 | 0 | case PDF_ANNOT_FILE_ATTACHMENT: |
2996 | 0 | case PDF_ANNOT_SOUND: |
2997 | 0 | pdf_write_icon_appearance(ctx, annot, buf, rect, bbox, res); |
2998 | 0 | *matrix = fz_identity; |
2999 | 0 | break; |
3000 | 0 | case PDF_ANNOT_HIGHLIGHT: |
3001 | 0 | pdf_write_highlight_appearance(ctx, annot, buf, rect, res); |
3002 | 0 | *matrix = fz_identity; |
3003 | 0 | *bbox = *rect; |
3004 | 0 | break; |
3005 | 0 | case PDF_ANNOT_UNDERLINE: |
3006 | 0 | pdf_write_underline_appearance(ctx, annot, buf, rect, res); |
3007 | 0 | *matrix = fz_identity; |
3008 | 0 | *bbox = *rect; |
3009 | 0 | break; |
3010 | 0 | case PDF_ANNOT_STRIKE_OUT: |
3011 | 0 | pdf_write_strike_out_appearance(ctx, annot, buf, rect, res); |
3012 | 0 | *matrix = fz_identity; |
3013 | 0 | *bbox = *rect; |
3014 | 0 | break; |
3015 | 0 | case PDF_ANNOT_SQUIGGLY: |
3016 | 0 | pdf_write_squiggly_appearance(ctx, annot, buf, rect, res); |
3017 | 0 | *matrix = fz_identity; |
3018 | 0 | *bbox = *rect; |
3019 | 0 | break; |
3020 | 0 | case PDF_ANNOT_REDACT: |
3021 | 0 | pdf_write_redact_appearance(ctx, annot, buf, rect, res); |
3022 | 0 | *matrix = fz_identity; |
3023 | 0 | *bbox = *rect; |
3024 | 0 | break; |
3025 | 0 | case PDF_ANNOT_STAMP: |
3026 | 0 | pdf_write_stamp_appearance(ctx, annot, buf, rect, bbox, matrix, res); |
3027 | 0 | break; |
3028 | 0 | case PDF_ANNOT_FREE_TEXT: |
3029 | 0 | pdf_write_free_text_appearance(ctx, annot, buf, rect, bbox, matrix, res); |
3030 | 0 | break; |
3031 | 0 | } |
3032 | 0 | } |
3033 | | |
3034 | | static pdf_obj *draw_push_button(fz_context *ctx, pdf_annot *annot, fz_rect bbox, fz_matrix matrix, float w, float h, |
3035 | | const char *caption, const char *font, float size, int n, float *color, |
3036 | | int down) |
3037 | 0 | { |
3038 | 0 | pdf_obj *ap, *res = NULL; |
3039 | 0 | fz_buffer *buf; |
3040 | 0 | float bc[3] = { 0, 0, 0 }; |
3041 | 0 | float bg[3] = { 0.8f, 0.8f, 0.8f }; |
3042 | 0 | float hi[3], sh[3]; |
3043 | 0 | int has_bg, has_bc; |
3044 | 0 | float b; |
3045 | 0 | int i; |
3046 | |
|
3047 | 0 | buf = fz_new_buffer(ctx, 1024); |
3048 | 0 | fz_var(res); |
3049 | 0 | fz_try(ctx) |
3050 | 0 | { |
3051 | 0 | b = pdf_annot_border_width(ctx, annot); |
3052 | 0 | has_bc = pdf_annot_MK_BC_rgb(ctx, annot, bc); |
3053 | 0 | has_bg = pdf_annot_MK_BG_rgb(ctx, annot, bg); |
3054 | |
|
3055 | 0 | for (i = 0; i < 3; ++i) |
3056 | 0 | { |
3057 | 0 | if (down) |
3058 | 0 | { |
3059 | 0 | sh[i] = 1 - (1 - bg[i]) / 2; |
3060 | 0 | hi[i] = bg[i] / 2; |
3061 | 0 | } |
3062 | 0 | else |
3063 | 0 | { |
3064 | 0 | hi[i] = 1 - (1 - bg[i]) / 2; |
3065 | 0 | sh[i] = bg[i] / 2; |
3066 | 0 | } |
3067 | 0 | } |
3068 | |
|
3069 | 0 | fz_append_string(ctx, buf, "q\n"); |
3070 | 0 | fz_append_printf(ctx, buf, "%g w\n", b); |
3071 | 0 | if (has_bg) |
3072 | 0 | { |
3073 | 0 | fz_append_printf(ctx, buf, "%g %g %g rg\n", bg[0], bg[1], bg[2]); |
3074 | 0 | fz_append_printf(ctx, buf, "0 0 %g %g re\nf\n", 0, 0, w, h); |
3075 | 0 | } |
3076 | 0 | if (has_bc && b > 0) |
3077 | 0 | { |
3078 | 0 | fz_append_printf(ctx, buf, "%g %g %g RG\n", bc[0], bc[1], bc[2]); |
3079 | 0 | fz_append_printf(ctx, buf, "%g %g %g %g re\nS\n", b/2, b/2, w-b, h-b); |
3080 | 0 | } |
3081 | 0 | if (has_bg) |
3082 | 0 | { |
3083 | 0 | fz_append_printf(ctx, buf, "%g %g %g rg\n", hi[0], hi[1], hi[2]); |
3084 | 0 | 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", |
3085 | 0 | b, b, b, h-b, w-b, h-b, w-b-2, h-b-2, b+2, h-b-2, b+2, b+2); |
3086 | 0 | fz_append_printf(ctx, buf, "%g %g %g rg\n", sh[0], sh[1], sh[2]); |
3087 | 0 | 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", |
3088 | 0 | b, b, b+2, b+2, w-b-2, b+2, w-b-2, h-b-2, w-b, h-b, w-b, b); |
3089 | 0 | } |
3090 | 0 | if (down) |
3091 | 0 | fz_append_string(ctx, buf, "1 0 0 1 2 -2 cm\n"); |
3092 | 0 | write_variable_text(ctx, annot, buf, &res, FZ_LANG_UNSET, caption, font, size, n, color, 1, w, h, b+6, 0.8f, 1.2f, 0, 0, 0); |
3093 | 0 | fz_append_string(ctx, buf, "Q\n"); |
3094 | |
|
3095 | 0 | ap = pdf_new_xobject(ctx, annot->page->doc, bbox, matrix, res, buf); |
3096 | 0 | } |
3097 | 0 | fz_always(ctx) |
3098 | 0 | { |
3099 | 0 | pdf_drop_obj(ctx, res); |
3100 | 0 | fz_drop_buffer(ctx, buf); |
3101 | 0 | } |
3102 | 0 | fz_catch(ctx) |
3103 | 0 | fz_rethrow(ctx); |
3104 | 0 | return ap; |
3105 | 0 | } |
3106 | | |
3107 | | static pdf_obj *draw_radio_button(fz_context *ctx, pdf_annot *annot, fz_rect bbox, fz_matrix matrix, float w, float h, int yes) |
3108 | 0 | { |
3109 | 0 | pdf_obj *ap; |
3110 | 0 | fz_buffer *buf; |
3111 | 0 | float b; |
3112 | |
|
3113 | 0 | buf = fz_new_buffer(ctx, 1024); |
3114 | 0 | fz_try(ctx) |
3115 | 0 | { |
3116 | 0 | fz_append_string(ctx, buf, "q\n"); |
3117 | 0 | if (pdf_write_MK_BG_appearance(ctx, annot, buf)) |
3118 | 0 | { |
3119 | 0 | draw_circle_in_box(ctx, buf, 0, 0, 0, w, h); |
3120 | 0 | fz_append_string(ctx, buf, "f\n"); |
3121 | 0 | } |
3122 | 0 | b = pdf_write_border_appearance(ctx, annot, buf); |
3123 | 0 | if (b > 0 && pdf_write_MK_BC_appearance(ctx, annot, buf)) |
3124 | 0 | { |
3125 | 0 | draw_circle_in_box(ctx, buf, b, 0, 0, w, h); |
3126 | 0 | fz_append_string(ctx, buf, "s\n"); |
3127 | 0 | } |
3128 | 0 | if (yes) |
3129 | 0 | { |
3130 | 0 | fz_append_string(ctx, buf, "0 g\n"); |
3131 | 0 | draw_circle(ctx, buf, (w-b*2)/4, (h-b*2)/4, w/2, h/2); |
3132 | 0 | fz_append_string(ctx, buf, "f\n"); |
3133 | 0 | } |
3134 | 0 | fz_append_string(ctx, buf, "Q\n"); |
3135 | 0 | ap = pdf_new_xobject(ctx, annot->page->doc, bbox, matrix, NULL, buf); |
3136 | 0 | } |
3137 | 0 | fz_always(ctx) |
3138 | 0 | fz_drop_buffer(ctx, buf); |
3139 | 0 | fz_catch(ctx) |
3140 | 0 | fz_rethrow(ctx); |
3141 | 0 | return ap; |
3142 | 0 | } |
3143 | | |
3144 | | static pdf_obj *draw_check_button(fz_context *ctx, pdf_annot *annot, fz_rect bbox, fz_matrix matrix, float w, float h, int yes) |
3145 | 0 | { |
3146 | 0 | float black[1] = { 0 }; |
3147 | 0 | pdf_obj *ap, *res = NULL; |
3148 | 0 | fz_buffer *buf; |
3149 | 0 | float b; |
3150 | |
|
3151 | 0 | fz_var(res); |
3152 | |
|
3153 | 0 | buf = fz_new_buffer(ctx, 1024); |
3154 | 0 | fz_try(ctx) |
3155 | 0 | { |
3156 | 0 | fz_append_string(ctx, buf, "q\n"); |
3157 | 0 | if (pdf_write_MK_BG_appearance(ctx, annot, buf)) |
3158 | 0 | fz_append_printf(ctx, buf, "0 0 %g %g re\nf\n", w, h); |
3159 | 0 | b = pdf_write_border_appearance(ctx, annot, buf); |
3160 | 0 | if (b > 0 && pdf_write_MK_BC_appearance(ctx, annot, buf)) |
3161 | 0 | fz_append_printf(ctx, buf, "%g %g %g %g re\nS\n", b/2, b/2, w-b, h-b); |
3162 | 0 | if (yes) |
3163 | 0 | write_variable_text(ctx, annot, buf, &res, FZ_LANG_UNSET, "3", "ZaDb", h, nelem(black), black, 0, w, h, b+h/10, 0.8f, 1.2f, 0, 0, 0); |
3164 | 0 | fz_append_string(ctx, buf, "Q\n"); |
3165 | 0 | ap = pdf_new_xobject(ctx, annot->page->doc, bbox, matrix, res, buf); |
3166 | 0 | } |
3167 | 0 | fz_always(ctx) |
3168 | 0 | { |
3169 | 0 | pdf_drop_obj(ctx, res); |
3170 | 0 | fz_drop_buffer(ctx, buf); |
3171 | 0 | } |
3172 | 0 | fz_catch(ctx) |
3173 | 0 | fz_rethrow(ctx); |
3174 | 0 | return ap; |
3175 | 0 | } |
3176 | | |
3177 | | static void pdf_update_button_appearance(fz_context *ctx, pdf_annot *annot) |
3178 | 0 | { |
3179 | 0 | int ff = pdf_field_flags(ctx, annot->obj); |
3180 | 0 | fz_rect rect = pdf_dict_get_rect(ctx, annot->obj, PDF_NAME(Rect)); |
3181 | 0 | fz_matrix matrix; |
3182 | 0 | fz_rect bbox; |
3183 | 0 | float w, h, t; |
3184 | 0 | int r; |
3185 | |
|
3186 | 0 | r = pdf_dict_get_int(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME(MK)), PDF_NAME(R)); |
3187 | 0 | w = rect.x1 - rect.x0; |
3188 | 0 | h = rect.y1 - rect.y0; |
3189 | 0 | r = r % 360; |
3190 | 0 | if (r == 90 || r == 270) |
3191 | 0 | t = h, h = w, w = t; |
3192 | 0 | matrix = fz_rotate(r); |
3193 | 0 | bbox = fz_make_rect(0, 0, w, h); |
3194 | | |
3195 | |
|
3196 | 0 | if (ff & PDF_BTN_FIELD_IS_PUSHBUTTON) |
3197 | 0 | { |
3198 | 0 | pdf_obj *ap_n = NULL; |
3199 | 0 | pdf_obj *ap_d = NULL; |
3200 | 0 | fz_var(ap_n); |
3201 | 0 | fz_var(ap_d); |
3202 | 0 | fz_try(ctx) |
3203 | 0 | { |
3204 | 0 | pdf_obj *ap, *MK, *CA, *AC; |
3205 | 0 | const char *font; |
3206 | 0 | const char *label; |
3207 | 0 | float size, color[4]; |
3208 | 0 | int n; |
3209 | |
|
3210 | 0 | pdf_annot_default_appearance(ctx, annot, &font, &size, &n, color); |
3211 | |
|
3212 | 0 | MK = pdf_dict_get(ctx, annot->obj, PDF_NAME(MK)); |
3213 | 0 | CA = pdf_dict_get(ctx, MK, PDF_NAME(CA)); |
3214 | 0 | AC = pdf_dict_get(ctx, MK, PDF_NAME(AC)); |
3215 | |
|
3216 | 0 | label = pdf_to_text_string(ctx, CA); |
3217 | 0 | ap_n = draw_push_button(ctx, annot, bbox, matrix, w, h, label, font, size, n, color, 0); |
3218 | |
|
3219 | 0 | label = pdf_to_text_string(ctx, AC ? AC : CA); |
3220 | 0 | ap_d = draw_push_button(ctx, annot, bbox, matrix, w, h, label, font, size, n, color, 1); |
3221 | |
|
3222 | 0 | ap = pdf_dict_put_dict(ctx, annot->obj, PDF_NAME(AP), 2); |
3223 | 0 | pdf_dict_put(ctx, ap, PDF_NAME(N), ap_n); |
3224 | 0 | pdf_dict_put(ctx, ap, PDF_NAME(D), ap_d); |
3225 | 0 | } |
3226 | 0 | fz_always(ctx) |
3227 | 0 | { |
3228 | 0 | pdf_drop_obj(ctx, ap_n); |
3229 | 0 | pdf_drop_obj(ctx, ap_d); |
3230 | 0 | } |
3231 | 0 | fz_catch(ctx) |
3232 | 0 | fz_rethrow(ctx); |
3233 | 0 | } |
3234 | 0 | else |
3235 | 0 | { |
3236 | 0 | pdf_obj *as_yes = NULL; |
3237 | 0 | pdf_obj *ap_off = NULL; |
3238 | 0 | pdf_obj *ap_yes = NULL; |
3239 | 0 | fz_var(ap_off); |
3240 | 0 | fz_var(ap_yes); |
3241 | 0 | fz_var(as_yes); |
3242 | 0 | fz_try(ctx) |
3243 | 0 | { |
3244 | 0 | pdf_obj *ap, *ap_n, *as; |
3245 | |
|
3246 | 0 | if (w > h) w = h; |
3247 | 0 | if (h > w) h = w; |
3248 | |
|
3249 | 0 | if (ff & PDF_BTN_FIELD_IS_RADIO) |
3250 | 0 | { |
3251 | 0 | ap_off = draw_radio_button(ctx, annot, bbox, matrix, w, h, 0); |
3252 | 0 | ap_yes = draw_radio_button(ctx, annot, bbox, matrix, w, h, 1); |
3253 | 0 | } |
3254 | 0 | else |
3255 | 0 | { |
3256 | 0 | ap_off = draw_check_button(ctx, annot, bbox, matrix, w, h, 0); |
3257 | 0 | ap_yes = draw_check_button(ctx, annot, bbox, matrix, w, h, 1); |
3258 | 0 | } |
3259 | |
|
3260 | 0 | as = pdf_dict_get(ctx, annot->obj, PDF_NAME(AS)); |
3261 | 0 | if (!as) |
3262 | 0 | { |
3263 | 0 | pdf_dict_put(ctx, annot->obj, PDF_NAME(AS), PDF_NAME(Off)); |
3264 | 0 | as = PDF_NAME(Off); |
3265 | 0 | } |
3266 | 0 | else |
3267 | 0 | as = pdf_resolve_indirect_chain(ctx, as); |
3268 | |
|
3269 | 0 | if (as == PDF_NAME(Off)) |
3270 | 0 | as_yes = pdf_keep_obj(ctx, pdf_button_field_on_state(ctx, annot->obj)); |
3271 | 0 | else |
3272 | 0 | as_yes = pdf_keep_obj(ctx, as); |
3273 | |
|
3274 | 0 | ap = pdf_dict_put_dict(ctx, annot->obj, PDF_NAME(AP), 2); |
3275 | 0 | ap_n = pdf_dict_put_dict(ctx, ap, PDF_NAME(N), 2); |
3276 | 0 | pdf_dict_put(ctx, ap_n, PDF_NAME(Off), ap_off); |
3277 | 0 | pdf_dict_put(ctx, ap_n, as_yes, ap_yes); |
3278 | 0 | } |
3279 | 0 | fz_always(ctx) |
3280 | 0 | { |
3281 | 0 | pdf_drop_obj(ctx, as_yes); |
3282 | 0 | pdf_drop_obj(ctx, ap_yes); |
3283 | 0 | pdf_drop_obj(ctx, ap_off); |
3284 | 0 | } |
3285 | 0 | fz_catch(ctx) |
3286 | 0 | { |
3287 | 0 | fz_rethrow(ctx); |
3288 | 0 | } |
3289 | 0 | } |
3290 | 0 | pdf_set_annot_resynthesised(ctx, annot); |
3291 | 0 | } |
3292 | | |
3293 | | static void draw_logo(fz_context *ctx, fz_path *path) |
3294 | 0 | { |
3295 | | /* Use mupdf logo for signature appearance background. */ |
3296 | 0 | fz_moveto(ctx, path, 122.25f, 0.0f); |
3297 | 0 | fz_lineto(ctx, path, 122.25f, 14.249f); |
3298 | 0 | fz_curveto(ctx, path, 125.98f, 13.842f, 129.73f, 13.518f, 133.5f, 13.277f); |
3299 | 0 | fz_lineto(ctx, path, 133.5f, 0.0f); |
3300 | 0 | fz_lineto(ctx, path, 122.25f, 0.0f); |
3301 | 0 | fz_closepath(ctx, path); |
3302 | 0 | fz_moveto(ctx, path, 140.251f, 0.0f); |
3303 | 0 | fz_lineto(ctx, path, 140.251f, 12.935f); |
3304 | 0 | fz_curveto(ctx, path, 152.534f, 12.477f, 165.03f, 12.899f, 177.75f, 14.249f); |
3305 | 0 | fz_lineto(ctx, path, 177.75f, 21.749f); |
3306 | 0 | fz_curveto(ctx, path, 165.304f, 20.413f, 152.809f, 19.871f, 140.251f, 20.348f); |
3307 | 0 | fz_lineto(ctx, path, 140.251f, 39.0f); |
3308 | 0 | fz_lineto(ctx, path, 133.5f, 39.0f); |
3309 | 0 | fz_lineto(ctx, path, 133.5f, 20.704f); |
3310 | 0 | fz_curveto(ctx, path, 129.756f, 20.956f, 126.006f, 21.302f, 122.25f, 21.749f); |
3311 | 0 | fz_lineto(ctx, path, 122.25f, 50.999f); |
3312 | 0 | fz_lineto(ctx, path, 177.751f, 50.999f); |
3313 | 0 | fz_lineto(ctx, path, 177.751f, 0.0f); |
3314 | 0 | fz_lineto(ctx, path, 140.251f, 0.0f); |
3315 | 0 | fz_closepath(ctx, path); |
3316 | 0 | fz_moveto(ctx, path, 23.482f, 129.419f); |
3317 | 0 | fz_curveto(ctx, path, -20.999f, 199.258f, -0.418f, 292.039f, 69.42f, 336.519f); |
3318 | 0 | fz_curveto(ctx, path, 139.259f, 381.0f, 232.04f, 360.419f, 276.52f, 290.581f); |
3319 | 0 | fz_curveto(ctx, path, 321.001f, 220.742f, 300.42f, 127.961f, 230.582f, 83.481f); |
3320 | 0 | fz_curveto(ctx, path, 160.743f, 39.0f, 67.962f, 59.581f, 23.482f, 129.419f); |
3321 | 0 | fz_closepath(ctx, path); |
3322 | 0 | fz_moveto(ctx, path, 254.751f, 128.492f); |
3323 | 0 | fz_curveto(ctx, path, 303.074f, 182.82f, 295.364f, 263.762f, 237.541f, 309.165f); |
3324 | 0 | fz_curveto(ctx, path, 179.718f, 354.568f, 93.57f, 347.324f, 45.247f, 292.996f); |
3325 | 0 | fz_curveto(ctx, path, -3.076f, 238.668f, 4.634f, 157.726f, 62.457f, 112.323f); |
3326 | 0 | fz_curveto(ctx, path, 120.28f, 66.92f, 206.428f, 74.164f, 254.751f, 128.492f); |
3327 | 0 | fz_closepath(ctx, path); |
3328 | 0 | fz_moveto(ctx, path, 111.0f, 98.999f); |
3329 | 0 | fz_curveto(ctx, path, 87.424f, 106.253f, 68.25f, 122.249f, 51.75f, 144.749f); |
3330 | 0 | fz_lineto(ctx, path, 103.5f, 297.749f); |
3331 | 0 | fz_lineto(ctx, path, 213.75f, 298.499f); |
3332 | 0 | fz_curveto(ctx, path, 206.25f, 306.749f, 195.744f, 311.478f, 185.25f, 314.249f); |
3333 | 0 | fz_curveto(ctx, path, 164.22f, 319.802f, 141.22f, 319.775f, 120.0f, 314.999f); |
3334 | 0 | fz_curveto(ctx, path, 96.658f, 309.745f, 77.25f, 298.499f, 55.5f, 283.499f); |
3335 | 0 | fz_curveto(ctx, path, 69.75f, 299.249f, 84.617f, 311.546f, 102.75f, 319.499f); |
3336 | 0 | fz_curveto(ctx, path, 117.166f, 325.822f, 133.509f, 327.689f, 149.25f, 327.749f); |
3337 | 0 | fz_curveto(ctx, path, 164.21f, 327.806f, 179.924f, 326.532f, 193.5f, 320.249f); |
3338 | 0 | fz_curveto(ctx, path, 213.95f, 310.785f, 232.5f, 294.749f, 245.25f, 276.749f); |
3339 | 0 | fz_lineto(ctx, path, 227.25f, 276.749f); |
3340 | 0 | fz_curveto(ctx, path, 213.963f, 276.749f, 197.25f, 263.786f, 197.25f, 250.499f); |
3341 | 0 | fz_lineto(ctx, path, 197.25f, 112.499f); |
3342 | 0 | fz_curveto(ctx, path, 213.75f, 114.749f, 228.0f, 127.499f, 241.5f, 140.999f); |
3343 | 0 | fz_curveto(ctx, path, 231.75f, 121.499f, 215.175f, 109.723f, 197.25f, 101.249f); |
3344 | 0 | fz_curveto(ctx, path, 181.5f, 95.249f, 168.412f, 94.775f, 153.0f, 94.499f); |
3345 | 0 | fz_curveto(ctx, path, 139.42f, 94.256f, 120.75f, 95.999f, 111.0f, 98.999f); |
3346 | 0 | fz_closepath(ctx, path); |
3347 | 0 | fz_moveto(ctx, path, 125.25f, 105.749f); |
3348 | 0 | fz_lineto(ctx, path, 125.25f, 202.499f); |
3349 | 0 | fz_lineto(ctx, path, 95.25f, 117.749f); |
3350 | 0 | fz_curveto(ctx, path, 105.75f, 108.749f, 114.0f, 105.749f, 125.25f, 105.749f); |
3351 | 0 | fz_closepath(ctx, path); |
3352 | 0 | }; |
3353 | | |
3354 | | static float logo_color[3] = { (float)0xa4 / (float)0xFF, (float)0xca / (float)0xFF, (float)0xf5 / (float)0xFF }; |
3355 | | |
3356 | | fz_display_list * |
3357 | | pdf_signature_appearance_signed(fz_context *ctx, fz_rect rect, fz_text_language lang, fz_image *img, const char *left_text, const char *right_text, int include_logo) |
3358 | 0 | { |
3359 | 0 | fz_display_list *dlist = NULL; |
3360 | 0 | fz_device *dev = NULL; |
3361 | 0 | fz_text *text = NULL; |
3362 | 0 | fz_colorspace *cs = NULL; |
3363 | 0 | fz_path *path = NULL; |
3364 | 0 | fz_font *font = NULL; |
3365 | |
|
3366 | 0 | fz_var(path); |
3367 | 0 | fz_var(dlist); |
3368 | 0 | fz_var(dev); |
3369 | 0 | fz_var(text); |
3370 | 0 | fz_var(font); |
3371 | 0 | fz_try(ctx) |
3372 | 0 | { |
3373 | 0 | fz_rect prect; |
3374 | 0 | fz_rect logo_bounds; |
3375 | 0 | fz_matrix logo_tm; |
3376 | 0 | float color[] = { 0.0, 0.0, 0.0 }; |
3377 | |
|
3378 | 0 | font = fz_new_base14_font(ctx, "Helvetica"); |
3379 | |
|
3380 | 0 | dlist = fz_new_display_list(ctx, rect); |
3381 | 0 | dev = fz_new_list_device(ctx, dlist); |
3382 | 0 | cs = fz_device_rgb(ctx); |
3383 | |
|
3384 | 0 | if (include_logo) |
3385 | 0 | { |
3386 | 0 | path = fz_new_path(ctx); |
3387 | 0 | draw_logo(ctx, path); |
3388 | 0 | logo_bounds = fz_bound_path(ctx, path, NULL, fz_identity); |
3389 | 0 | logo_tm = center_rect_within_rect(logo_bounds, rect); |
3390 | 0 | fz_fill_path(ctx, dev, path, 0, logo_tm, cs, logo_color, 1.0f, fz_default_color_params); |
3391 | 0 | } |
3392 | |
|
3393 | 0 | prect = rect; |
3394 | | /* If there is to be info on the right then use only the left half of the rectangle for |
3395 | | * what is intended for the left */ |
3396 | 0 | if (right_text) |
3397 | 0 | prect.x1 = (prect.x0 + prect.x1) / 2.0f; |
3398 | |
|
3399 | 0 | if (img) |
3400 | 0 | { |
3401 | 0 | float img_aspect = (float) img->w / img->h; |
3402 | 0 | float rectw = prect.x1 - prect.x0; |
3403 | 0 | float recth = prect.y1 - prect.y0; |
3404 | 0 | float midx = (prect.x0 + prect.x1) / 2.0f; |
3405 | 0 | float midy = (prect.y0 + prect.y1) / 2.0f; |
3406 | 0 | float rect_aspect = rectw / recth; |
3407 | 0 | float scale = img_aspect > rect_aspect ? rectw / img->w : recth / img->h; |
3408 | 0 | fz_matrix ctm = fz_pre_translate(fz_pre_scale(fz_translate(midx, midy), scale * img->w, scale * img->h), -0.5, -0.5); |
3409 | 0 | fz_fill_image(ctx, dev, img, ctm, 1.0f, fz_default_color_params); |
3410 | 0 | } |
3411 | |
|
3412 | 0 | if (left_text) |
3413 | 0 | { |
3414 | 0 | text = pdf_layout_fit_text(ctx, font, lang, left_text, prect); |
3415 | 0 | fz_fill_text(ctx, dev, text, fz_identity, cs, color, 1.0f, fz_default_color_params); |
3416 | 0 | fz_drop_text(ctx, text); |
3417 | 0 | text = NULL; |
3418 | 0 | } |
3419 | |
|
3420 | 0 | prect = rect; |
3421 | | /* If there is to be info on the left then use only the right half of the rectangle for |
3422 | | * what is intended for the right */ |
3423 | 0 | if (img || left_text) |
3424 | 0 | prect.x0 = (prect.x0 + prect.x1) / 2.0f; |
3425 | |
|
3426 | 0 | if (right_text) |
3427 | 0 | { |
3428 | 0 | text = pdf_layout_fit_text(ctx, font, lang, right_text, prect); |
3429 | 0 | fz_fill_text(ctx, dev, text, fz_identity, cs, color, 1.0f, fz_default_color_params); |
3430 | 0 | } |
3431 | 0 | } |
3432 | 0 | fz_always(ctx) |
3433 | 0 | { |
3434 | 0 | fz_drop_device(ctx, dev); |
3435 | 0 | fz_drop_path(ctx, path); |
3436 | 0 | fz_drop_text(ctx, text); |
3437 | 0 | fz_drop_font(ctx, font); |
3438 | 0 | } |
3439 | 0 | fz_catch(ctx) |
3440 | 0 | { |
3441 | 0 | fz_drop_display_list(ctx, dlist); |
3442 | 0 | fz_rethrow(ctx); |
3443 | 0 | } |
3444 | | |
3445 | 0 | return dlist; |
3446 | 0 | } |
3447 | | |
3448 | | fz_display_list * |
3449 | | pdf_signature_appearance_unsigned(fz_context *ctx, fz_rect rect, fz_text_language lang) |
3450 | 0 | { |
3451 | 0 | fz_display_list *dlist = NULL; |
3452 | 0 | fz_device *dev = NULL; |
3453 | 0 | fz_text *text = NULL; |
3454 | 0 | fz_colorspace *cs = NULL; |
3455 | 0 | fz_path *path = NULL; |
3456 | 0 | fz_font *font = NULL; |
3457 | |
|
3458 | 0 | fz_var(path); |
3459 | 0 | fz_var(dlist); |
3460 | 0 | fz_var(dev); |
3461 | 0 | fz_var(text); |
3462 | 0 | fz_var(font); |
3463 | 0 | fz_try(ctx) |
3464 | 0 | { |
3465 | 0 | float text_color[] = { 1.0f, 1.0f, 1.0f }; |
3466 | 0 | float arrow_color[] = { 0.95f, 0.33f, 0.18f }; |
3467 | |
|
3468 | 0 | dlist = fz_new_display_list(ctx, rect); |
3469 | 0 | dev = fz_new_list_device(ctx, dlist); |
3470 | |
|
3471 | 0 | rect.y1 = rect.y0 + (rect.y1 - rect.y0) / 6; |
3472 | 0 | rect.x1 = rect.x0 + (rect.y1 - rect.y0) * 4; |
3473 | 0 | font = fz_new_base14_font(ctx, "Helvetica"); |
3474 | |
|
3475 | 0 | path = fz_new_path(ctx); |
3476 | | /* Draw a rectangle with a protrusion to the right [xxxxx> */ |
3477 | 0 | fz_moveto(ctx, path, rect.x0, rect.y0); |
3478 | 0 | fz_lineto(ctx, path, rect.x1, rect.y0); |
3479 | 0 | fz_lineto(ctx, path, rect.x1 + (rect.y1 - rect.y0) / 2.0f, (rect.y0 + rect.y1) / 2.0f); |
3480 | 0 | fz_lineto(ctx, path, rect.x1, rect.y1); |
3481 | 0 | fz_lineto(ctx, path, rect.x0, rect.y1); |
3482 | 0 | fz_closepath(ctx, path); |
3483 | 0 | cs = fz_device_rgb(ctx); |
3484 | 0 | fz_fill_path(ctx, dev, path, 0, fz_identity, cs, arrow_color, 1.0f, fz_default_color_params); |
3485 | |
|
3486 | 0 | text = pdf_layout_fit_text(ctx, font, lang, "SIGN", rect); |
3487 | 0 | fz_fill_text(ctx, dev, text, fz_identity, cs, text_color, 1.0f, fz_default_color_params); |
3488 | 0 | fz_drop_text(ctx, text); |
3489 | 0 | text = NULL; |
3490 | 0 | } |
3491 | 0 | fz_always(ctx) |
3492 | 0 | { |
3493 | 0 | fz_drop_device(ctx, dev); |
3494 | 0 | fz_drop_path(ctx, path); |
3495 | 0 | fz_drop_text(ctx, text); |
3496 | 0 | fz_drop_font(ctx, font); |
3497 | 0 | } |
3498 | 0 | fz_catch(ctx) |
3499 | 0 | { |
3500 | 0 | fz_drop_display_list(ctx, dlist); |
3501 | 0 | fz_rethrow(ctx); |
3502 | 0 | } |
3503 | | |
3504 | 0 | return dlist; |
3505 | 0 | } |
3506 | | |
3507 | | char * |
3508 | | pdf_signature_info(fz_context *ctx, const char *name, pdf_pkcs7_distinguished_name *dn, const char *reason, const char *location, int64_t date, int include_labels) |
3509 | 0 | { |
3510 | 0 | fz_buffer *fzbuf = NULL; |
3511 | 0 | char *dn_str = NULL; |
3512 | 0 | char *full_str = NULL; |
3513 | 0 | time_t tdate = (time_t)date; |
3514 | |
|
3515 | 0 | fz_var(fzbuf); |
3516 | 0 | fz_var(dn_str); |
3517 | 0 | fz_try(ctx) |
3518 | 0 | { |
3519 | 0 | #ifdef _POSIX_SOURCE |
3520 | 0 | struct tm tmbuf, *tm = localtime_r(&tdate, &tmbuf); |
3521 | | #else |
3522 | | struct tm *tm = localtime(&tdate); |
3523 | | #endif |
3524 | 0 | char now_str[40]; |
3525 | 0 | size_t len = 0; |
3526 | | #ifdef CLUSTER |
3527 | | memset(&date, 0, sizeof(date)); |
3528 | | memset(tm, 0, sizeof(*tm)); |
3529 | | #endif |
3530 | |
|
3531 | 0 | fzbuf = fz_new_buffer(ctx, 256); |
3532 | 0 | if (name && strlen(name) > 0) |
3533 | 0 | { |
3534 | 0 | if (include_labels) |
3535 | 0 | fz_append_string(ctx, fzbuf, "Digitally signed by "); |
3536 | 0 | fz_append_string(ctx, fzbuf, name); |
3537 | 0 | } |
3538 | |
|
3539 | 0 | if (dn) |
3540 | 0 | { |
3541 | 0 | fz_append_string(ctx, fzbuf, "\n"); |
3542 | 0 | if (include_labels) |
3543 | 0 | fz_append_string(ctx, fzbuf, "DN: "); |
3544 | 0 | dn_str = pdf_signature_format_distinguished_name(ctx, dn); |
3545 | 0 | fz_append_string(ctx, fzbuf, dn_str); |
3546 | 0 | } |
3547 | |
|
3548 | 0 | if (reason && strlen(reason) > 0) |
3549 | 0 | { |
3550 | 0 | fz_append_string(ctx, fzbuf, "\n"); |
3551 | 0 | if (include_labels) |
3552 | 0 | fz_append_string(ctx, fzbuf, "Reason: "); |
3553 | 0 | fz_append_string(ctx, fzbuf, reason); |
3554 | 0 | } |
3555 | |
|
3556 | 0 | if (location && strlen(location) > 0) |
3557 | 0 | { |
3558 | 0 | fz_append_string(ctx, fzbuf, "\n"); |
3559 | 0 | if (include_labels) |
3560 | 0 | fz_append_string(ctx, fzbuf, "Location: "); |
3561 | 0 | fz_append_string(ctx, fzbuf, location); |
3562 | 0 | } |
3563 | |
|
3564 | 0 | if (date >= 0) |
3565 | 0 | { |
3566 | 0 | len = strftime(now_str, sizeof now_str, "%FT%T%z", tm); |
3567 | 0 | if (len) |
3568 | 0 | { |
3569 | 0 | fz_append_string(ctx, fzbuf, "\n"); |
3570 | 0 | if (include_labels) |
3571 | 0 | fz_append_string(ctx, fzbuf, "Date: "); |
3572 | 0 | fz_append_string(ctx, fzbuf, now_str); |
3573 | 0 | } |
3574 | 0 | } |
3575 | |
|
3576 | 0 | fz_terminate_buffer(ctx, fzbuf); |
3577 | 0 | (void)fz_buffer_extract(ctx, fzbuf, (unsigned char **)&full_str); |
3578 | 0 | } |
3579 | 0 | fz_always(ctx) |
3580 | 0 | { |
3581 | 0 | fz_drop_buffer(ctx, fzbuf); |
3582 | 0 | fz_free(ctx, dn_str); |
3583 | 0 | } |
3584 | 0 | fz_catch(ctx) |
3585 | 0 | { |
3586 | 0 | fz_rethrow(ctx); |
3587 | 0 | } |
3588 | | |
3589 | 0 | return full_str; |
3590 | 0 | } |
3591 | | |
3592 | | void |
3593 | | pdf_annot_push_local_xref(fz_context *ctx, pdf_annot *annot) |
3594 | 0 | { |
3595 | 0 | pdf_document *doc; |
3596 | |
|
3597 | 0 | if (!annot->page) |
3598 | 0 | fz_throw(ctx, FZ_ERROR_ARGUMENT, "annotation not bound to any page"); |
3599 | | |
3600 | 0 | doc = annot->page->doc; |
3601 | |
|
3602 | | #ifdef PDF_DEBUG_APPEARANCE_SYNTHESIS |
3603 | | if (doc->local_xref_nesting == 0 && doc->local_xref) |
3604 | | fz_write_printf(ctx, fz_stddbg(ctx), "push local_xref for annot\n"); |
3605 | | #endif |
3606 | 0 | doc->local_xref_nesting++; |
3607 | 0 | } |
3608 | | |
3609 | | void |
3610 | | pdf_annot_ensure_local_xref(fz_context *ctx, pdf_annot *annot) |
3611 | 0 | { |
3612 | 0 | pdf_document *doc = annot->page->doc; |
3613 | |
|
3614 | 0 | if (doc->local_xref != NULL) |
3615 | 0 | return; |
3616 | | |
3617 | | #ifdef PDF_DEBUG_APPEARANCE_SYNTHESIS |
3618 | | fz_write_printf(ctx, fz_stddbg(ctx), "creating local_xref\n"); |
3619 | | #endif |
3620 | | |
3621 | | /* We have no local_xref, but we want to be using one. */ |
3622 | | /* First off, create one. */ |
3623 | 0 | doc->local_xref = pdf_new_local_xref(ctx, doc); |
3624 | 0 | } |
3625 | | |
3626 | | void |
3627 | | pdf_annot_pop_local_xref(fz_context *ctx, pdf_annot *annot) |
3628 | 0 | { |
3629 | 0 | pdf_document *doc = annot->page->doc; |
3630 | |
|
3631 | 0 | --doc->local_xref_nesting; |
3632 | | #ifdef PDF_DEBUG_APPEARANCE_SYNTHESIS |
3633 | | if (doc->local_xref_nesting == 0 && doc->local_xref) |
3634 | | fz_write_printf(ctx, fz_stddbg(ctx), "pop local_xref for annot\n"); |
3635 | | #endif |
3636 | 0 | } |
3637 | | |
3638 | | void pdf_annot_pop_and_discard_local_xref(fz_context *ctx, pdf_annot *annot) |
3639 | 0 | { |
3640 | 0 | pdf_document *doc = annot->page->doc; |
3641 | |
|
3642 | | #ifdef PDF_DEBUG_APPEARANCE_SYNTHESIS |
3643 | | if (doc->local_xref) |
3644 | | fz_write_printf(ctx, fz_stddbg(ctx), "pop and discard local_xref for annot\n"); |
3645 | | #endif |
3646 | 0 | --doc->local_xref_nesting; |
3647 | 0 | assert(doc->local_xref_nesting == 0); |
3648 | 0 | pdf_drop_local_xref_and_resources(ctx, doc); |
3649 | 0 | } |
3650 | | |
3651 | | static void pdf_update_appearance(fz_context *ctx, pdf_annot *annot) |
3652 | 0 | { |
3653 | 0 | pdf_obj *subtype; |
3654 | 0 | pdf_obj *ft = NULL; |
3655 | 0 | pdf_obj *ap_n; |
3656 | 0 | int pop_local_xref = 1; |
3657 | |
|
3658 | 0 | retry_after_repair: |
3659 | | /* Must have any local xref in place in order to check if it's dirty. */ |
3660 | 0 | pdf_annot_push_local_xref(ctx, annot); |
3661 | |
|
3662 | 0 | pdf_begin_implicit_operation(ctx, annot->page->doc); |
3663 | 0 | fz_start_throw_on_repair(ctx); |
3664 | |
|
3665 | 0 | fz_var(pop_local_xref); |
3666 | |
|
3667 | 0 | fz_try(ctx) |
3668 | 0 | { |
3669 | 0 | int needs_resynth; |
3670 | 0 | int local_synthesis = 0; |
3671 | | |
3672 | | /* Never update Popup and Link annotations */ |
3673 | 0 | subtype = pdf_dict_get(ctx, annot->obj, PDF_NAME(Subtype)); |
3674 | 0 | if (subtype == PDF_NAME(Popup) || subtype == PDF_NAME(Link)) |
3675 | 0 | { |
3676 | 0 | pdf_end_operation(ctx, annot->page->doc); |
3677 | 0 | break; |
3678 | 0 | } |
3679 | | |
3680 | | /* Never update signed Signature widgets */ |
3681 | 0 | if (subtype == PDF_NAME(Widget)) |
3682 | 0 | { |
3683 | 0 | ft = pdf_dict_get_inheritable(ctx, annot->obj, PDF_NAME(FT)); |
3684 | 0 | if (ft == PDF_NAME(Sig)) |
3685 | 0 | { |
3686 | | /* We cannot synthesise an appearance for a signed Sig, so don't even try. */ |
3687 | 0 | if (pdf_signature_is_signed(ctx, annot->page->doc, annot->obj)) |
3688 | 0 | { |
3689 | 0 | pdf_end_operation(ctx, annot->page->doc); |
3690 | 0 | break; |
3691 | 0 | } |
3692 | 0 | } |
3693 | 0 | } |
3694 | | |
3695 | | /* Check if the field is dirtied by JS events */ |
3696 | 0 | if (pdf_obj_is_dirty(ctx, annot->obj)) |
3697 | 0 | pdf_annot_request_resynthesis(ctx, annot); |
3698 | | |
3699 | | /* Find the current appearance stream, if one exists. */ |
3700 | 0 | ap_n = pdf_annot_ap(ctx, annot); |
3701 | | |
3702 | | /* If there is no appearance stream, we need to create a local one for display purposes. */ |
3703 | 0 | if (!ap_n) |
3704 | 0 | local_synthesis = 1; |
3705 | | |
3706 | | /* Ignore appearance streams not created by us (so not local) |
3707 | | * for unsigned digital signature widgets. They are often blank |
3708 | | * and we want the "sign" arrow to be visible. Never write back |
3709 | | * the forced appearance stream for unsigned signatures. */ |
3710 | 0 | if (subtype == PDF_NAME(Widget) && ft == PDF_NAME(Sig)) |
3711 | 0 | { |
3712 | 0 | if (ap_n && !pdf_is_local_object(ctx, annot->page->doc, ap_n)) |
3713 | 0 | local_synthesis = 1; |
3714 | 0 | } |
3715 | | |
3716 | | /* We need to put this appearance stream back into the document. */ |
3717 | 0 | needs_resynth = pdf_annot_needs_resynthesis(ctx, annot); |
3718 | 0 | if (needs_resynth > 0) |
3719 | 0 | local_synthesis = 0; |
3720 | 0 | if (needs_resynth < 0) |
3721 | 0 | local_synthesis = 1; |
3722 | | |
3723 | | /* Some appearances can NEVER be resynthesised. Spot those here. */ |
3724 | 0 | if (needs_resynth) |
3725 | 0 | { |
3726 | 0 | if (pdf_name_eq(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME(Subtype)), PDF_NAME(Stamp))) |
3727 | 0 | { |
3728 | | /* Don't resynthesize Stamps with non-standard names if |
3729 | | * they already have an appearance. These usually have |
3730 | | * custom images already set for their appearance. |
3731 | | */ |
3732 | 0 | if (!pdf_annot_is_standard_stamp(ctx, annot) && ap_n) |
3733 | 0 | { |
3734 | | /* However, we allow changing the Rect even if we don't |
3735 | | * resynthesize the appearance. This should also count |
3736 | | * as having a changed appearance. */ |
3737 | 0 | pdf_set_annot_resynthesised(ctx, annot); |
3738 | 0 | needs_resynth = 0; |
3739 | 0 | } |
3740 | 0 | } |
3741 | 0 | } |
3742 | |
|
3743 | 0 | if (local_synthesis || needs_resynth) |
3744 | 0 | { |
3745 | 0 | fz_display_list *dlist; |
3746 | 0 | fz_rect rect, bbox; |
3747 | 0 | fz_buffer *buf; |
3748 | 0 | pdf_obj *res = NULL; |
3749 | 0 | pdf_obj *new_ap_n = NULL; |
3750 | 0 | fz_var(res); |
3751 | 0 | fz_var(new_ap_n); |
3752 | |
|
3753 | | #ifdef PDF_DEBUG_APPEARANCE_SYNTHESIS |
3754 | | fz_write_printf(ctx, fz_stddbg(ctx), "Update Appearance: %d\n", pdf_to_num(ctx, annot->obj)); |
3755 | | pdf_debug_obj(ctx, annot->obj); |
3756 | | fz_write_printf(ctx, fz_stddbg(ctx), "\n"); |
3757 | | #endif |
3758 | |
|
3759 | 0 | if (local_synthesis) |
3760 | 0 | { |
3761 | | #ifdef PDF_DEBUG_APPEARANCE_SYNTHESIS |
3762 | | fz_write_printf(ctx, fz_stddbg(ctx), "Local synthesis\n"); |
3763 | | #endif |
3764 | 0 | pdf_annot_ensure_local_xref(ctx, annot); |
3765 | 0 | } |
3766 | 0 | else |
3767 | 0 | { |
3768 | | #ifdef PDF_DEBUG_APPEARANCE_SYNTHESIS |
3769 | | fz_write_printf(ctx, fz_stddbg(ctx), "Non-Local synthesis\n"); |
3770 | | #endif |
3771 | | /* We don't want to be using any local xref, so |
3772 | | * bin any that we have. */ |
3773 | 0 | pdf_annot_pop_and_discard_local_xref(ctx, annot); |
3774 | | /* Binning the xref may leave ap_n holding an invalid pointer. |
3775 | | * We don't use it from this point onwards anymore, but beware |
3776 | | * in future code changes. */ |
3777 | 0 | pop_local_xref = 0; |
3778 | 0 | } |
3779 | |
|
3780 | 0 | if (subtype == PDF_NAME(Widget) && ft == PDF_NAME(Btn)) |
3781 | 0 | { |
3782 | | /* Special case for Btn widgets that need multiple appearance streams. */ |
3783 | 0 | pdf_update_button_appearance(ctx, annot); |
3784 | 0 | } |
3785 | 0 | else if (subtype == PDF_NAME(Widget) && ft == PDF_NAME(Sig)) |
3786 | 0 | { |
3787 | | /* Special case for unsigned signature widgets, |
3788 | | * which are most easily created via a display list. */ |
3789 | 0 | rect = pdf_annot_rect(ctx, annot); |
3790 | 0 | dlist = pdf_signature_appearance_unsigned(ctx, rect, pdf_annot_language(ctx, annot)); |
3791 | 0 | fz_try(ctx) |
3792 | 0 | pdf_set_annot_appearance_from_display_list(ctx, annot, "N", NULL, fz_identity, dlist); |
3793 | 0 | fz_always(ctx) |
3794 | 0 | fz_drop_display_list(ctx, dlist); |
3795 | 0 | fz_catch(ctx) |
3796 | 0 | fz_rethrow(ctx); |
3797 | 0 | } |
3798 | 0 | else |
3799 | 0 | { |
3800 | 0 | buf = fz_new_buffer(ctx, 1024); |
3801 | 0 | fz_try(ctx) |
3802 | 0 | { |
3803 | 0 | fz_matrix matrix = fz_identity; |
3804 | 0 | rect = pdf_dict_get_rect(ctx, annot->obj, PDF_NAME(Rect)); |
3805 | 0 | pdf_write_appearance(ctx, annot, buf, &rect, &bbox, &matrix, &res); |
3806 | 0 | pdf_dict_put_rect(ctx, annot->obj, PDF_NAME(Rect), rect); |
3807 | 0 | pdf_set_annot_appearance(ctx, annot, "N", NULL, matrix, bbox, res, buf); |
3808 | 0 | } |
3809 | 0 | fz_always(ctx) |
3810 | 0 | { |
3811 | 0 | fz_drop_buffer(ctx, buf); |
3812 | 0 | pdf_drop_obj(ctx, res); |
3813 | 0 | pdf_drop_obj(ctx, new_ap_n); |
3814 | 0 | } |
3815 | 0 | fz_catch(ctx) |
3816 | 0 | { |
3817 | 0 | fz_rethrow_if(ctx, FZ_ERROR_REPAIRED); |
3818 | 0 | fz_rethrow_if(ctx, FZ_ERROR_SYSTEM); |
3819 | 0 | fz_report_error(ctx); |
3820 | 0 | fz_warn(ctx, "cannot create appearance stream"); |
3821 | 0 | } |
3822 | 0 | } |
3823 | |
|
3824 | | #ifdef PDF_DEBUG_APPEARANCE_SYNTHESIS |
3825 | | fz_write_printf(ctx, fz_stddbg(ctx), "Annot obj after synthesis\n"); |
3826 | | pdf_debug_obj(ctx, annot->obj); |
3827 | | fz_write_printf(ctx, fz_stddbg(ctx), "\nAppearance after synthesis\n"); |
3828 | | pdf_debug_obj(ctx, pdf_dict_getp(ctx, annot->obj, "AP/N")); |
3829 | | fz_write_printf(ctx, fz_stddbg(ctx), "\n"); |
3830 | | #endif |
3831 | 0 | } |
3832 | | |
3833 | 0 | pdf_clean_obj(ctx, annot->obj); |
3834 | 0 | pdf_end_operation(ctx, annot->page->doc); |
3835 | 0 | } |
3836 | 0 | fz_always(ctx) |
3837 | 0 | { |
3838 | 0 | if (pop_local_xref) |
3839 | 0 | pdf_annot_pop_local_xref(ctx, annot); |
3840 | 0 | fz_end_throw_on_repair(ctx); |
3841 | 0 | } |
3842 | 0 | fz_catch(ctx) |
3843 | 0 | { |
3844 | 0 | pdf_abandon_operation(ctx, annot->page->doc); |
3845 | | /* If we hit a repair while synthesising, we need to give it another |
3846 | | * go. Do that directly here, rather than waiting for the next time |
3847 | | * we are called, because we don't want to risk discarding any |
3848 | | * local_xrefs on the second pass through the list of annotations. |
3849 | | * Repairs only ever happen once for a document, so no infinite |
3850 | | * loop potential here. */ |
3851 | 0 | if (fz_caught(ctx) == FZ_ERROR_REPAIRED) |
3852 | 0 | { |
3853 | 0 | fz_report_error(ctx); |
3854 | 0 | goto retry_after_repair; |
3855 | 0 | } |
3856 | 0 | fz_rethrow(ctx); |
3857 | 0 | } |
3858 | 0 | } |
3859 | | |
3860 | | static void * |
3861 | | update_appearances(fz_context *ctx, fz_page *page_, void *state) |
3862 | 0 | { |
3863 | 0 | pdf_page *page = (pdf_page *)page_; |
3864 | 0 | pdf_annot *annot; |
3865 | |
|
3866 | 0 | for (annot = pdf_first_annot(ctx, page); annot; annot = pdf_next_annot(ctx, annot)) |
3867 | 0 | pdf_update_appearance(ctx, annot); |
3868 | 0 | for (annot = pdf_first_widget(ctx, page); annot; annot = pdf_next_widget(ctx, annot)) |
3869 | 0 | pdf_update_appearance(ctx, annot); |
3870 | |
|
3871 | 0 | return NULL; |
3872 | 0 | } |
3873 | | |
3874 | | static void |
3875 | | update_all_appearances(fz_context *ctx, pdf_page *page) |
3876 | 0 | { |
3877 | 0 | pdf_document *doc = page->doc; |
3878 | | |
3879 | | /* Update all the annotations on all the pages open in the document. |
3880 | | * At least one annotation should be resynthesised because we'll |
3881 | | * only reach here if resynth_required was set. Any such resynthesis |
3882 | | * that changes the document will throw away any local_xref. */ |
3883 | 0 | fz_process_opened_pages(ctx, &doc->super, update_appearances, NULL); |
3884 | | /* If the page isn't linked in yet (as is the case whilst loading |
3885 | | * the annots for a page), process that too. */ |
3886 | 0 | if (page->super.prev == NULL && page->super.next == NULL) |
3887 | 0 | update_appearances(ctx, &page->super, NULL); |
3888 | | |
3889 | | /* Run it a second time, so that any annotations whose synthesised |
3890 | | * appearances live in the local_xref (such as unsigned sigs) can |
3891 | | * be regenerated too. Running this for annots which are up to date |
3892 | | * should be fast. */ |
3893 | 0 | fz_process_opened_pages(ctx, &doc->super, update_appearances, NULL); |
3894 | | /* And cope with a non-linked in page again. */ |
3895 | 0 | if (page->super.prev == NULL && page->super.next == NULL) |
3896 | 0 | update_appearances(ctx, &page->super, NULL); |
3897 | |
|
3898 | 0 | doc->resynth_required = 0; |
3899 | 0 | } |
3900 | | |
3901 | | int |
3902 | | pdf_update_annot(fz_context *ctx, pdf_annot *annot) |
3903 | 0 | { |
3904 | 0 | int changed; |
3905 | |
|
3906 | 0 | if (!annot->page) |
3907 | 0 | fz_throw(ctx, FZ_ERROR_ARGUMENT, "annotation not bound to any page"); |
3908 | | |
3909 | 0 | if (annot->page->doc->resynth_required) |
3910 | 0 | update_all_appearances(ctx, annot->page); |
3911 | |
|
3912 | 0 | changed = annot->has_new_ap; |
3913 | 0 | annot->has_new_ap = 0; |
3914 | 0 | return changed; |
3915 | 0 | } |