]> rtime.felk.cvut.cz Git - hornmich/skoda-qr-demo.git/blob - QRScanner/mobile/jni/pdf/pdf-appearance.c
Add MuPDF native source codes
[hornmich/skoda-qr-demo.git] / QRScanner / mobile / jni / pdf / pdf-appearance.c
1 #include "mupdf/pdf.h"
2
3 #include <ft2build.h>
4 #include FT_FREETYPE_H
5 #include FT_ADVANCES_H
6
7 #define MATRIX_COEFS (6)
8 #define STRIKE_HEIGHT (0.375f)
9 #define UNDERLINE_HEIGHT (0.075f)
10 #define LINE_THICKNESS (0.07f)
11 #define SMALL_FLOAT (0.00001)
12
13 enum
14 {
15         Q_Left = 0,
16         Q_Cent = 1,
17         Q_Right = 2
18 };
19
20 enum
21 {
22         BS_Solid,
23         BS_Dashed,
24         BS_Beveled,
25         BS_Inset,
26         BS_Underline
27 };
28
29 typedef struct font_info_s
30 {
31         pdf_da_info da_rec;
32         pdf_font_desc *font;
33         float lineheight;
34 } font_info;
35
36 typedef struct text_widget_info_s
37 {
38         pdf_obj *dr;
39         pdf_obj *col;
40         font_info font_rec;
41         int q;
42         int multiline;
43         int comb;
44         int max_len;
45 } text_widget_info;
46
47 static const char *fmt_re = "%f %f %f %f re\n";
48 static const char *fmt_f = "f\n";
49 static const char *fmt_s = "s\n";
50 static const char *fmt_g = "%f g\n";
51 static const char *fmt_m = "%f %f m\n";
52 static const char *fmt_l = "%f %f l\n";
53 static const char *fmt_w = "%f w\n";
54 static const char *fmt_Tx_BMC = "/Tx BMC\n";
55 static const char *fmt_q = "q\n";
56 static const char *fmt_W = "W\n";
57 static const char *fmt_n = "n\n";
58 static const char *fmt_BT = "BT\n";
59 static const char *fmt_Tm = "%f %f %f %f %f %f Tm\n";
60 static const char *fmt_Td = "%f %f Td\n";
61 static const char *fmt_Tj = " Tj\n";
62 static const char *fmt_ET = "ET\n";
63 static const char *fmt_Q = "Q\n";
64 static const char *fmt_EMC = "EMC\n";
65
66 void pdf_da_info_fin(fz_context *ctx, pdf_da_info *di)
67 {
68         fz_free(ctx, di->font_name);
69         di->font_name = NULL;
70 }
71
72 static void da_check_stack(float *stack, int *top)
73 {
74         if (*top == 32)
75         {
76                 memmove(stack, stack + 1, 31 * sizeof(stack[0]));
77                 *top = 31;
78         }
79 }
80
81 void pdf_parse_da(fz_context *ctx, char *da, pdf_da_info *di)
82 {
83         float stack[32] = { 0.0f };
84         int top = 0;
85         pdf_token tok;
86         char *name = NULL;
87         pdf_lexbuf lbuf;
88         fz_stream *str = fz_open_memory(ctx, (unsigned char *)da, strlen(da));
89
90         pdf_lexbuf_init(ctx, &lbuf, PDF_LEXBUF_SMALL);
91
92         fz_var(str);
93         fz_var(name);
94         fz_try(ctx)
95         {
96                 for (tok = pdf_lex(str, &lbuf); tok != PDF_TOK_EOF; tok = pdf_lex(str, &lbuf))
97                 {
98                         switch (tok)
99                         {
100                         case PDF_TOK_NAME:
101                                 fz_free(ctx, name);
102                                 name = fz_strdup(ctx, lbuf.scratch);
103                                 break;
104
105                         case PDF_TOK_INT:
106                                 da_check_stack(stack, &top);
107                                 stack[top] = lbuf.i;
108                                 top ++;
109                                 break;
110
111                         case PDF_TOK_REAL:
112                                 da_check_stack(stack, &top);
113                                 stack[top] = lbuf.f;
114                                 top ++;
115                                 break;
116
117                         case PDF_TOK_KEYWORD:
118                                 if (!strcmp(lbuf.scratch, "Tf"))
119                                 {
120                                         di->font_size = stack[0];
121                                         di->font_name = name;
122                                         name = NULL;
123                                 }
124                                 else if (!strcmp(lbuf.scratch, "rg"))
125                                 {
126                                         di->col[0] = stack[0];
127                                         di->col[1] = stack[1];
128                                         di->col[2] = stack[2];
129                                         di->col_size = 3;
130                                 }
131                                 else if (!strcmp(lbuf.scratch, "g"))
132                                 {
133                                         di->col[0] = stack[0];
134                                         di->col_size = 1;
135                                 }
136
137                                 fz_free(ctx, name);
138                                 name = NULL;
139                                 top = 0;
140                                 break;
141
142                         default:
143                                 break;
144                         }
145                 }
146         }
147         fz_always(ctx)
148         {
149                 fz_free(ctx, name);
150                 fz_close(str);
151                 pdf_lexbuf_fin(&lbuf);
152         }
153         fz_catch(ctx)
154         {
155                 fz_rethrow(ctx);
156         }
157 }
158
159 static void get_font_info(pdf_document *doc, pdf_obj *dr, char *da, font_info *font_rec)
160 {
161         fz_context *ctx = doc->ctx;
162         pdf_font_desc *font;
163
164         pdf_parse_da(ctx, da, &font_rec->da_rec);
165         if (font_rec->da_rec.font_name == NULL)
166                 fz_throw(ctx, FZ_ERROR_GENERIC, "No font name in default appearance");
167
168         font_rec->font = font = pdf_load_font(doc, dr, pdf_dict_gets(pdf_dict_gets(dr, "Font"), font_rec->da_rec.font_name), 0);
169         font_rec->lineheight = 1.0;
170         if (font && font->ascent != 0.0f && font->descent != 0.0f)
171                 font_rec->lineheight = (font->ascent - font->descent) / 1000.0;
172 }
173
174 static void font_info_fin(fz_context *ctx, font_info *font_rec)
175 {
176         pdf_drop_font(ctx, font_rec->font);
177         font_rec->font = NULL;
178         pdf_da_info_fin(ctx, &font_rec->da_rec);
179 }
180
181 static void get_text_widget_info(pdf_document *doc, pdf_obj *widget, text_widget_info *info)
182 {
183         char *da = pdf_to_str_buf(pdf_get_inheritable(doc, widget, "DA"));
184         int ff = pdf_get_field_flags(doc, widget);
185         pdf_obj *ml = pdf_get_inheritable(doc, widget, "MaxLen");
186
187         info->dr = pdf_get_inheritable(doc, widget, "DR");
188         info->col = pdf_dict_getp(widget, "MK/BG");
189         info->q = pdf_to_int(pdf_get_inheritable(doc, widget, "Q"));
190         info->multiline = (ff & Ff_Multiline) != 0;
191         info->comb = (ff & (Ff_Multiline|Ff_Password|Ff_FileSelect|Ff_Comb)) == Ff_Comb;
192
193         if (ml == NULL)
194                 info->comb = 0;
195         else
196                 info->max_len = pdf_to_int(ml);
197
198         get_font_info(doc, info->dr, da, &info->font_rec);
199 }
200
201 void pdf_fzbuf_print_da(fz_context *ctx, fz_buffer *fzbuf, pdf_da_info *di)
202 {
203         if (di->font_name != NULL && di->font_size != 0)
204                 fz_buffer_printf(ctx, fzbuf, "/%s %d Tf", di->font_name, di->font_size);
205
206         switch (di->col_size)
207         {
208         case 1:
209                 fz_buffer_printf(ctx, fzbuf, " %f g", di->col[0]);
210                 break;
211
212         case 3:
213                 fz_buffer_printf(ctx, fzbuf, " %f %f %f rg", di->col[0], di->col[1], di->col[2]);
214                 break;
215
216         case 4:
217                 fz_buffer_printf(ctx, fzbuf, " %f %f %f %f k", di->col[0], di->col[1], di->col[2], di->col[3]);
218                 break;
219
220         default:
221                 fz_buffer_printf(ctx, fzbuf, " 0 g");
222                 break;
223         }
224 }
225
226 static fz_rect *measure_text(pdf_document *doc, font_info *font_rec, const fz_matrix *tm, char *text, fz_rect *bbox)
227 {
228         pdf_measure_text(doc->ctx, font_rec->font, (unsigned char *)text, strlen(text), bbox);
229
230         bbox->x0 *= font_rec->da_rec.font_size * tm->a;
231         bbox->y0 *= font_rec->da_rec.font_size * tm->d;
232         bbox->x1 *= font_rec->da_rec.font_size * tm->a;
233         bbox->y1 *= font_rec->da_rec.font_size * tm->d;
234
235         return bbox;
236 }
237
238 static void fzbuf_print_color(fz_context *ctx, fz_buffer *fzbuf, pdf_obj *arr, int stroke, float adj)
239 {
240         switch (pdf_array_len(arr))
241         {
242         case 1:
243                 fz_buffer_printf(ctx, fzbuf, stroke?"%f G\n":"%f g\n",
244                         pdf_to_real(pdf_array_get(arr, 0)) + adj);
245                 break;
246         case 3:
247                 fz_buffer_printf(ctx, fzbuf, stroke?"%f %f %f RG\n":"%f %f %f rg\n",
248                         pdf_to_real(pdf_array_get(arr, 0)) + adj,
249                         pdf_to_real(pdf_array_get(arr, 1)) + adj,
250                         pdf_to_real(pdf_array_get(arr, 2)) + adj);
251                 break;
252         case 4:
253                 fz_buffer_printf(ctx, fzbuf, stroke?"%f %f %f %f K\n":"%f %f %f %f k\n",
254                         pdf_to_real(pdf_array_get(arr, 0)),
255                         pdf_to_real(pdf_array_get(arr, 1)),
256                         pdf_to_real(pdf_array_get(arr, 2)),
257                         pdf_to_real(pdf_array_get(arr, 3)));
258                 break;
259         }
260 }
261
262 static void fzbuf_print_text(fz_context *ctx, fz_buffer *fzbuf, const fz_rect *clip, pdf_obj *col, font_info *font_rec, const fz_matrix *tm, char *text)
263 {
264         fz_buffer_printf(ctx, fzbuf, fmt_q);
265         if (clip)
266         {
267                 fz_buffer_printf(ctx, fzbuf, fmt_re, clip->x0, clip->y0, clip->x1 - clip->x0, clip->y1 - clip->y0);
268                 fz_buffer_printf(ctx, fzbuf, fmt_W);
269                 if (col)
270                 {
271                         fzbuf_print_color(ctx, fzbuf, col, 0, 0.0);
272                         fz_buffer_printf(ctx, fzbuf, fmt_f);
273                 }
274                 else
275                 {
276                         fz_buffer_printf(ctx, fzbuf, fmt_n);
277                 }
278         }
279
280         fz_buffer_printf(ctx, fzbuf, fmt_BT);
281
282         pdf_fzbuf_print_da(ctx, fzbuf, &font_rec->da_rec);
283
284         fz_buffer_printf(ctx, fzbuf, "\n");
285         if (tm)
286                 fz_buffer_printf(ctx, fzbuf, fmt_Tm, tm->a, tm->b, tm->c, tm->d, tm->e, tm->f);
287
288         fz_buffer_cat_pdf_string(ctx, fzbuf, text);
289         fz_buffer_printf(ctx, fzbuf, fmt_Tj);
290         fz_buffer_printf(ctx, fzbuf, fmt_ET);
291         fz_buffer_printf(ctx, fzbuf, fmt_Q);
292 }
293
294 static fz_buffer *create_text_buffer(fz_context *ctx, const fz_rect *clip, text_widget_info *info, const fz_matrix *tm, char *text)
295 {
296         fz_buffer *fzbuf = fz_new_buffer(ctx, 0);
297
298         fz_try(ctx)
299         {
300                 fz_buffer_printf(ctx, fzbuf, fmt_Tx_BMC);
301                 fzbuf_print_text(ctx, fzbuf, clip, info->col, &info->font_rec, tm, text);
302                 fz_buffer_printf(ctx, fzbuf, fmt_EMC);
303         }
304         fz_catch(ctx)
305         {
306                 fz_drop_buffer(ctx, fzbuf);
307                 fz_rethrow(ctx);
308         }
309
310         return fzbuf;
311 }
312
313 static fz_buffer *create_aligned_text_buffer(pdf_document *doc, const fz_rect *clip, text_widget_info *info, const fz_matrix *tm, char *text)
314 {
315         fz_context *ctx = doc->ctx;
316         fz_matrix atm = *tm;
317
318         if (info->q != Q_Left)
319         {
320                 fz_rect rect;
321
322                 measure_text(doc, &info->font_rec, tm, text, &rect);
323                 atm.e -= info->q == Q_Right ? rect.x1 : (rect.x1 - rect.x0) / 2;
324         }
325
326         return create_text_buffer(ctx, clip, info, &atm, text);
327 }
328
329 static void measure_ascent_descent(pdf_document *doc, font_info *finf, char *text, float *ascent, float *descent)
330 {
331         fz_context *ctx = doc->ctx;
332         char *testtext = NULL;
333         fz_rect bbox;
334         font_info tinf = *finf;
335
336         fz_var(testtext);
337         fz_try(ctx)
338         {
339                 /* Heuristic: adding "My" to text will in most cases
340                  * produce a measurement that will encompass all chars */
341                 testtext = fz_malloc(ctx, strlen(text) + 3);
342                 strcpy(testtext, "My");
343                 strcat(testtext, text);
344                 tinf.da_rec.font_size = 1;
345                 measure_text(doc, &tinf, &fz_identity, testtext, &bbox);
346                 *descent = -bbox.y0;
347                 *ascent = bbox.y1;
348         }
349         fz_always(ctx)
350         {
351                 fz_free(ctx, testtext);
352         }
353         fz_catch(ctx)
354         {
355                 fz_rethrow(ctx);
356         }
357 }
358
359 typedef struct text_splitter_s
360 {
361         font_info *info;
362         float width;
363         float height;
364         float scale;
365         float unscaled_width;
366         float fontsize;
367         float lineheight;
368         char *text;
369         int done;
370         float x_orig;
371         float y_orig;
372         float x;
373         float x_end;
374         int text_start;
375         int text_end;
376         int max_lines;
377         int retry;
378 } text_splitter;
379
380 static void text_splitter_init(text_splitter *splitter, font_info *info, char *text, float width, float height, int variable)
381 {
382         float fontsize = info->da_rec.font_size;
383
384         memset(splitter, 0, sizeof(*splitter));
385         splitter->info = info;
386         splitter->text = text;
387         splitter->width = width;
388         splitter->unscaled_width = width;
389         splitter->height = height;
390         splitter->fontsize = fontsize;
391         splitter->scale = 1.0;
392         splitter->lineheight = fontsize * info->lineheight ;
393         /* RJW: The cast in the following line is important, as otherwise
394          * under MSVC in the variable = 0 case, splitter->max_lines becomes
395          * INT_MIN. */
396         splitter->max_lines = variable ? (int)(height/splitter->lineheight) : INT_MAX;
397 }
398
399 static void text_splitter_start_pass(text_splitter *splitter)
400 {
401         splitter->text_end = 0;
402         splitter->x_orig = 0;
403         splitter->y_orig = 0;
404 }
405
406 static void text_splitter_start_line(text_splitter *splitter)
407 {
408         splitter->x_end = 0;
409 }
410
411 static int text_splitter_layout(fz_context *ctx, text_splitter *splitter)
412 {
413         char *text;
414         float room;
415         float stride;
416         int count;
417         int len;
418         float fontsize = splitter->info->da_rec.font_size;
419
420         splitter->x = splitter->x_end;
421         splitter->text_start = splitter->text_end;
422
423         text = splitter->text + splitter->text_start;
424         room = splitter->unscaled_width - splitter->x;
425
426         if (strchr("\r\n", text[0]))
427         {
428                 /* Consume return chars and report end of line */
429                 splitter->text_end += strspn(text, "\r\n");
430                 splitter->text_start = splitter->text_end;
431                 splitter->done = (splitter->text[splitter->text_end] == '\0');
432                 return 0;
433         }
434         else if (text[0] == ' ')
435         {
436                 /* Treat each space as a word */
437                 len = 1;
438         }
439         else
440         {
441                 len = 0;
442                 while (text[len] != '\0' && !strchr(" \r\n", text[len]))
443                         len ++;
444         }
445
446         stride = pdf_text_stride(ctx, splitter->info->font, fontsize, (unsigned char *)text, len, room, &count);
447
448         /* If not a single char fits although the line is empty, then force one char */
449         if (count == 0 && splitter->x == 0.0)
450                 stride = pdf_text_stride(ctx, splitter->info->font, fontsize, (unsigned char *)text, 1, FLT_MAX, &count);
451
452         if (count < len && splitter->retry)
453         {
454                 /* The word didn't fit and we are in retry mode. Work out the
455                  * least additional scaling that may help */
456                 float fitwidth; /* width if we force the word in */
457                 float hstretchwidth; /* width if we just bump by 10% */
458                 float vstretchwidth; /* width resulting from forcing in another line */
459                 float bestwidth;
460
461                 fitwidth = splitter->x +
462                         pdf_text_stride(ctx, splitter->info->font, fontsize, (unsigned char *)text, len, FLT_MAX, &count);
463                 /* FIXME: temporary fiddle factor. Would be better to work in integers */
464                 fitwidth *= 1.001f;
465
466                 /* Stretching by 10% is worth trying only if processing the first word on the line */
467                 hstretchwidth = splitter->x == 0.0
468                         ? splitter->width * 1.1 / splitter->scale
469                         : FLT_MAX;
470
471                 vstretchwidth = splitter->width * (splitter->max_lines + 1) * splitter->lineheight
472                         / splitter->height;
473
474                 bestwidth = fz_min(fitwidth, fz_min(hstretchwidth, vstretchwidth));
475
476                 if (bestwidth == vstretchwidth)
477                         splitter->max_lines ++;
478
479                 splitter->scale = splitter->width / bestwidth;
480                 splitter->unscaled_width = bestwidth;
481
482                 splitter->retry = 0;
483
484                 /* Try again */
485                 room = splitter->unscaled_width - splitter->x;
486                 stride = pdf_text_stride(ctx, splitter->info->font, fontsize, (unsigned char *)text, len, room, &count);
487         }
488
489         /* This is not the first word on the line. Best to give up on this line and push
490          * the word onto the next */
491         if (count < len && splitter->x > 0.0)
492                 return 0;
493
494         splitter->text_end = splitter->text_start + count;
495         splitter->x_end = splitter->x + stride;
496         splitter->done = (splitter->text[splitter->text_end] == '\0');
497         return 1;
498 }
499
500 static void text_splitter_move(text_splitter *splitter, float newy, float *relx, float *rely)
501 {
502         *relx = splitter->x - splitter->x_orig;
503         *rely = newy * splitter->lineheight - splitter->y_orig;
504
505         splitter->x_orig = splitter->x;
506         splitter->y_orig = newy * splitter->lineheight;
507 }
508
509 static void text_splitter_retry(text_splitter *splitter)
510 {
511         if (splitter->retry)
512         {
513                 /* Already tried expanding lines. Overflow must
514                  * be caused by carriage control */
515                 splitter->max_lines ++;
516                 splitter->retry = 0;
517                 splitter->unscaled_width = splitter->width * splitter->max_lines * splitter->lineheight
518                         / splitter->height;
519                 splitter->scale = splitter->width / splitter->unscaled_width;
520         }
521         else
522         {
523                 splitter->retry = 1;
524         }
525 }
526
527 static void fzbuf_print_text_start(fz_context *ctx, fz_buffer *fzbuf, const fz_rect *clip, pdf_obj *col, font_info *font, const fz_matrix *tm)
528 {
529         fz_buffer_printf(ctx, fzbuf, fmt_Tx_BMC);
530         fz_buffer_printf(ctx, fzbuf, fmt_q);
531
532         if (clip)
533         {
534                 fz_buffer_printf(ctx, fzbuf, fmt_re, clip->x0, clip->y0, clip->x1 - clip->x0, clip->y1 - clip->y0);
535                 fz_buffer_printf(ctx, fzbuf, fmt_W);
536                 if (col)
537                 {
538                         fzbuf_print_color(ctx, fzbuf, col, 0, 0.0);
539                         fz_buffer_printf(ctx, fzbuf, fmt_f);
540                 }
541                 else
542                 {
543                         fz_buffer_printf(ctx, fzbuf, fmt_n);
544                 }
545         }
546
547         fz_buffer_printf(ctx, fzbuf, fmt_BT);
548
549         pdf_fzbuf_print_da(ctx, fzbuf, &font->da_rec);
550         fz_buffer_printf(ctx, fzbuf, "\n");
551
552         fz_buffer_printf(ctx, fzbuf, fmt_Tm, tm->a, tm->b, tm->c, tm->d, tm->e, tm->f);
553 }
554
555 static void fzbuf_print_text_end(fz_context *ctx, fz_buffer *fzbuf)
556 {
557         fz_buffer_printf(ctx, fzbuf, fmt_ET);
558         fz_buffer_printf(ctx, fzbuf, fmt_Q);
559         fz_buffer_printf(ctx, fzbuf, fmt_EMC);
560 }
561
562 static void fzbuf_print_text_word(fz_context *ctx, fz_buffer *fzbuf, float x, float y, char *text, int count)
563 {
564         int i;
565
566         fz_buffer_printf(ctx, fzbuf, fmt_Td, x, y);
567         fz_buffer_printf(ctx, fzbuf, "(");
568
569         for (i = 0; i < count; i++)
570                 fz_buffer_printf(ctx, fzbuf, "%c", text[i]);
571
572         fz_buffer_printf(ctx, fzbuf, ") Tj\n");
573 }
574
575 static fz_buffer *create_text_appearance(pdf_document *doc, const fz_rect *bbox, const fz_matrix *oldtm, text_widget_info *info, char *text)
576 {
577         fz_context *ctx = doc->ctx;
578         int fontsize;
579         int variable;
580         float height, width, full_width;
581         fz_buffer *fzbuf = NULL;
582         fz_buffer *fztmp = NULL;
583         fz_rect rect;
584         fz_rect tbox;
585         rect = *bbox;
586
587         if (rect.x1 - rect.x0 > 3.0 && rect.y1 - rect.y0 > 3.0)
588         {
589                 rect.x0 += 1.0;
590                 rect.x1 -= 1.0;
591                 rect.y0 += 1.0;
592                 rect.y1 -= 1.0;
593         }
594
595         height = rect.y1 - rect.y0;
596         width = rect.x1 - rect.x0;
597         full_width = bbox->x1 - bbox->x0;
598
599         fz_var(fzbuf);
600         fz_var(fztmp);
601         fz_try(ctx)
602         {
603                 float ascent, descent;
604                 fz_matrix tm;
605
606                 variable = (info->font_rec.da_rec.font_size == 0);
607                 fontsize = variable
608                         ? (info->multiline ? 14.0 : height / info->font_rec.lineheight)
609                         : info->font_rec.da_rec.font_size;
610
611                 info->font_rec.da_rec.font_size = fontsize;
612
613                 measure_ascent_descent(doc, &info->font_rec, text, &ascent, &descent);
614
615                 if (info->multiline)
616                 {
617                         text_splitter splitter;
618
619                         text_splitter_init(&splitter, &info->font_rec, text, width, height, variable);
620
621                         while (!splitter.done)
622                         {
623                                 /* Try a layout pass */
624                                 int line = 0;
625
626                                 fz_drop_buffer(ctx, fztmp);
627                                 fztmp = NULL;
628                                 fztmp = fz_new_buffer(ctx, 0);
629
630                                 text_splitter_start_pass(&splitter);
631
632                                 /* Layout unscaled text to a scaled-up width, so that
633                                  * the scaled-down text will fit the unscaled width */
634
635                                 while (!splitter.done && line < splitter.max_lines)
636                                 {
637                                         /* Layout a line */
638                                         text_splitter_start_line(&splitter);
639
640                                         while (!splitter.done && text_splitter_layout(ctx, &splitter))
641                                         {
642                                                 if (splitter.text[splitter.text_start] != ' ')
643                                                 {
644                                                         float x, y;
645                                                         char *word = text+splitter.text_start;
646                                                         int wordlen = splitter.text_end-splitter.text_start;
647
648                                                         text_splitter_move(&splitter, -line, &x, &y);
649                                                         fzbuf_print_text_word(ctx, fztmp, x, y, word, wordlen);
650                                                 }
651                                         }
652
653                                         line ++;
654                                 }
655
656                                 if (!splitter.done)
657                                         text_splitter_retry(&splitter);
658                         }
659
660                         fzbuf = fz_new_buffer(ctx, 0);
661
662                         tm.a = splitter.scale;
663                         tm.b = 0.0;
664                         tm.c = 0.0;
665                         tm.d = splitter.scale;
666                         tm.e = rect.x0;
667                         tm.f = rect.y1 - (1.0+ascent-descent)*fontsize*splitter.scale/2.0;
668
669                         fzbuf_print_text_start(ctx, fzbuf, &rect, info->col, &info->font_rec, &tm);
670
671                         fz_buffer_cat(ctx, fzbuf, fztmp);
672
673                         fzbuf_print_text_end(ctx, fzbuf);
674                 }
675                 else if (info->comb)
676                 {
677                         int i, n = fz_mini((int)strlen(text), info->max_len);
678                         float comb_width = full_width/info->max_len;
679                         float char_width = pdf_text_stride(ctx, info->font_rec.font, fontsize, (unsigned char *)"M", 1, FLT_MAX, NULL);
680                         float init_skip = (comb_width - char_width)/2.0;
681
682                         fz_translate(&tm, rect.x0, rect.y1 - (height+(ascent-descent)*fontsize)/2.0);
683
684                         fzbuf = fz_new_buffer(ctx, 0);
685
686                         fzbuf_print_text_start(ctx, fzbuf, &rect, info->col, &info->font_rec, &tm);
687
688                         for (i = 0; i < n; i++)
689                                 fzbuf_print_text_word(ctx, fzbuf, i == 0 ? init_skip : comb_width, 0.0, text+i, 1);
690
691                         fzbuf_print_text_end(ctx, fzbuf);
692                 }
693                 else
694                 {
695                         if (oldtm)
696                         {
697                                 tm = *oldtm;
698                         }
699                         else
700                         {
701                                 fz_translate(&tm, rect.x0, rect.y1 - (height+(ascent-descent)*fontsize)/2.0);
702
703                                 switch (info->q)
704                                 {
705                                 case Q_Right: tm.e += width; break;
706                                 case Q_Cent: tm.e += width/2; break;
707                                 }
708                         }
709
710                         if (variable)
711                         {
712                                 measure_text(doc, &info->font_rec, &tm, text, &tbox);
713
714                                 if (tbox.x1 - tbox.x0 > width)
715                                 {
716                                         /* Scale the text to fit but use the same offset
717                                         * to keep the baseline constant */
718                                         tm.a *= width / (tbox.x1 - tbox.x0);
719                                         tm.d *= width / (tbox.x1 - tbox.x0);
720                                 }
721                         }
722
723                         fzbuf = create_aligned_text_buffer(doc, &rect, info, &tm, text);
724                 }
725         }
726         fz_always(ctx)
727         {
728                 fz_drop_buffer(ctx, fztmp);
729         }
730         fz_catch(ctx)
731         {
732                 fz_drop_buffer(ctx, fzbuf);
733                 fz_rethrow(ctx);
734         }
735
736         return fzbuf;
737 }
738
739 static int get_matrix(pdf_document *doc, pdf_xobject *form, int q, fz_matrix *mt)
740 {
741         fz_context *ctx = doc->ctx;
742         int found = 0;
743         pdf_lexbuf lbuf;
744         fz_stream *str;
745
746         str = pdf_open_stream(doc, pdf_to_num(form->contents), pdf_to_gen(form->contents));
747
748         pdf_lexbuf_init(ctx, &lbuf, PDF_LEXBUF_SMALL);
749
750         fz_try(ctx)
751         {
752                 int tok;
753                 float coefs[MATRIX_COEFS];
754                 int coef_i = 0;
755
756                 /* Look for the text matrix Tm in the stream */
757                 for (tok = pdf_lex(str, &lbuf); tok != PDF_TOK_EOF; tok = pdf_lex(str, &lbuf))
758                 {
759                         if (tok == PDF_TOK_INT || tok == PDF_TOK_REAL)
760                         {
761                                 if (coef_i >= MATRIX_COEFS)
762                                 {
763                                         int i;
764                                         for (i = 0; i < MATRIX_COEFS-1; i++)
765                                                 coefs[i] = coefs[i+1];
766
767                                         coef_i = MATRIX_COEFS-1;
768                                 }
769
770                                 coefs[coef_i++] = tok == PDF_TOK_INT ? lbuf.i : lbuf.f;
771                         }
772                         else
773                         {
774                                 if (tok == PDF_TOK_KEYWORD && !strcmp(lbuf.scratch, "Tm") && coef_i == MATRIX_COEFS)
775                                 {
776                                         found = 1;
777                                         mt->a = coefs[0];
778                                         mt->b = coefs[1];
779                                         mt->c = coefs[2];
780                                         mt->d = coefs[3];
781                                         mt->e = coefs[4];
782                                         mt->f = coefs[5];
783                                 }
784
785                                 coef_i = 0;
786                         }
787                 }
788
789                 if (found)
790                 {
791                         fz_rect bbox;
792                         pdf_to_rect(ctx, pdf_dict_gets(form->contents, "BBox"), &bbox);
793
794                         switch (q)
795                         {
796                         case Q_Left:
797                                 mt->e = bbox.x0 + 1;
798                                 break;
799
800                         case Q_Cent:
801                                 mt->e = (bbox.x1 - bbox.x0) / 2;
802                                 break;
803
804                         case Q_Right:
805                                 mt->e = bbox.x1 - 1;
806                                 break;
807                         }
808                 }
809         }
810         fz_always(ctx)
811         {
812                 fz_close(str);
813                 pdf_lexbuf_fin(&lbuf);
814         }
815         fz_catch(ctx)
816         {
817                 fz_rethrow(ctx);
818         }
819
820         return found;
821 }
822
823 static char *to_font_encoding(fz_context *ctx, pdf_font_desc *font, char *utf8)
824 {
825         int i;
826         int needs_converting = 0;
827
828         /* Temporay partial solution. We are using a slow lookup in the conversion
829          * below, so we avoid performing the conversion unnecessarily. We check for
830          * top-bit-set chars, and convert only if they are present. We should also
831          * check that the font encoding is one that agrees with utf8 from 0 to 7f,
832          * but for now we get away without doing so. This is after all an improvement
833          * on just strdup */
834         for (i = 0; utf8[i] != '\0'; i++)
835         {
836                 if (utf8[i] & 0x80)
837                         needs_converting = 1;
838         }
839
840         /* Even if we need to convert, we cannot do so if the font has no cid_to_ucs mapping */
841         if (needs_converting && font->cid_to_ucs)
842         {
843                 char *buf = fz_malloc(ctx, strlen(utf8) + 1);
844                 char *bufp = buf;
845
846                 fz_try(ctx)
847                 {
848                         while(*utf8)
849                         {
850                                 if (*utf8 & 0x80)
851                                 {
852                                         int rune;
853
854                                         utf8 += fz_chartorune(&rune, utf8);
855
856                                         /* Slow search for the cid that maps to the unicode value held in 'rune" */
857                                         for (i = 0; i < font->cid_to_ucs_len && font->cid_to_ucs[i] != rune; i++)
858                                                 ;
859
860                                         /* If found store the cid */
861                                         if (i < font->cid_to_ucs_len)
862                                                 *bufp++ = i;
863                                 }
864                                 else
865                                 {
866                                         *bufp++ = *utf8++;
867                                 }
868                         }
869
870                         *bufp = '\0';
871                 }
872                 fz_catch(ctx)
873                 {
874                         fz_free(ctx, buf);
875                         fz_rethrow(ctx);
876                 }
877
878                 return buf;
879         }
880         else
881         {
882                 /* If either no conversion is needed or the font has no cid_to_ucs
883                  * mapping then leave unconverted, although in the latter case the result
884                  * is likely incorrect */
885                 return fz_strdup(ctx, utf8);
886         }
887 }
888
889 static void account_for_rot(fz_rect *rect, fz_matrix *mat, int rot)
890 {
891         float width = rect->x1;
892         float height = rect->y1;
893
894         switch (rot)
895         {
896         default:
897                 *mat = fz_identity;
898                 break;
899         case 90:
900                 fz_pre_rotate(fz_translate(mat, width, 0), rot);
901                 rect->x1 = height;
902                 rect->y1 = width;
903                 break;
904         case 180:
905                 fz_pre_rotate(fz_translate(mat, width, height), rot);
906                 break;
907         case 270:
908                 fz_pre_rotate(fz_translate(mat, 0, height), rot);
909                 rect->x1 = height;
910                 rect->y1 = width;
911                 break;
912         }
913 }
914
915 static void copy_resources(pdf_obj *dst, pdf_obj *src)
916 {
917         int i, len;
918
919         len = pdf_dict_len(src);
920         for (i = 0; i < len; i++)
921         {
922                 pdf_obj *key = pdf_dict_get_key(src, i);
923
924                 if (!pdf_dict_get(dst, key))
925                         pdf_dict_put(dst, key, pdf_dict_get_val(src, i));
926         }
927 }
928
929 static pdf_xobject *load_or_create_form(pdf_document *doc, pdf_obj *obj, fz_rect *rect)
930 {
931         fz_context *ctx = doc->ctx;
932         pdf_obj *ap = NULL;
933         fz_matrix mat;
934         int rot;
935         pdf_obj *formobj = NULL;
936         pdf_xobject *form = NULL;
937         char *dn = "N";
938         fz_buffer *fzbuf = NULL;
939         int create_form = 0;
940
941         fz_var(formobj);
942         fz_var(form);
943         fz_var(fzbuf);
944         fz_try(ctx)
945         {
946                 rot = pdf_to_int(pdf_dict_getp(obj, "MK/R"));
947                 pdf_to_rect(ctx, pdf_dict_gets(obj, "Rect"), rect);
948                 rect->x1 -= rect->x0;
949                 rect->y1 -= rect->y0;
950                 rect->x0 = rect->y0 = 0;
951                 account_for_rot(rect, &mat, rot);
952
953                 ap = pdf_dict_gets(obj, "AP");
954                 if (ap == NULL)
955                 {
956                         ap = pdf_new_dict(doc, 1);
957                         pdf_dict_puts_drop(obj, "AP", ap);
958                 }
959
960                 formobj = pdf_dict_gets(ap, dn);
961                 if (formobj == NULL)
962                 {
963                         formobj = pdf_new_xobject(doc, rect, &mat);
964                         pdf_dict_puts_drop(ap, dn, formobj);
965                         create_form = 1;
966                 }
967
968                 form = pdf_load_xobject(doc, formobj);
969                 if (create_form)
970                 {
971                         fzbuf = fz_new_buffer(ctx, 1);
972                         pdf_update_xobject_contents(doc, form, fzbuf);
973                 }
974
975                 copy_resources(form->resources, pdf_get_inheritable(doc, obj, "DR"));
976         }
977         fz_always(ctx)
978         {
979                 fz_drop_buffer(ctx, fzbuf);
980         }
981         fz_catch(ctx)
982         {
983                 pdf_drop_xobject(ctx, form);
984                 fz_rethrow(ctx);
985         }
986
987         return form;
988 }
989
990 static void update_marked_content(pdf_document *doc, pdf_xobject *form, fz_buffer *fzbuf)
991 {
992         fz_context *ctx = doc->ctx;
993         pdf_token tok;
994         pdf_lexbuf lbuf;
995         fz_stream *str_outer = NULL;
996         fz_stream *str_inner = NULL;
997         unsigned char *buf;
998         int len;
999         fz_buffer *newbuf = NULL;
1000
1001         pdf_lexbuf_init(ctx, &lbuf, PDF_LEXBUF_SMALL);
1002
1003         fz_var(str_outer);
1004         fz_var(str_inner);
1005         fz_var(newbuf);
1006         fz_try(ctx)
1007         {
1008                 int bmc_found;
1009                 int first = 1;
1010
1011                 newbuf = fz_new_buffer(ctx, 0);
1012                 str_outer = pdf_open_stream(doc, pdf_to_num(form->contents), pdf_to_gen(form->contents));
1013                 len = fz_buffer_storage(ctx, fzbuf, &buf);
1014                 str_inner = fz_open_memory(ctx, buf, len);
1015
1016                 /* Copy the existing appearance stream to newbuf while looking for BMC */
1017                 for (tok = pdf_lex(str_outer, &lbuf); tok != PDF_TOK_EOF; tok = pdf_lex(str_outer, &lbuf))
1018                 {
1019                         if (first)
1020                                 first = 0;
1021                         else
1022                                 fz_buffer_printf(ctx, newbuf, " ");
1023
1024                         pdf_print_token(ctx, newbuf, tok, &lbuf);
1025                         if (tok == PDF_TOK_KEYWORD && !strcmp(lbuf.scratch, "BMC"))
1026                                 break;
1027                 }
1028
1029                 bmc_found = (tok != PDF_TOK_EOF);
1030
1031                 if (bmc_found)
1032                 {
1033                         /* Drop Tx BMC from the replacement appearance stream */
1034                         (void)pdf_lex(str_inner, &lbuf);
1035                         (void)pdf_lex(str_inner, &lbuf);
1036                 }
1037
1038                 /* Copy the replacement appearance stream to newbuf */
1039                 for (tok = pdf_lex(str_inner, &lbuf); tok != PDF_TOK_EOF; tok = pdf_lex(str_inner, &lbuf))
1040                 {
1041                         fz_buffer_printf(ctx, newbuf, " ");
1042                         pdf_print_token(ctx, newbuf, tok, &lbuf);
1043                 }
1044
1045                 if (bmc_found)
1046                 {
1047                         /* Drop the rest of the existing appearance stream until EMC found */
1048                         for (tok = pdf_lex(str_outer, &lbuf); tok != PDF_TOK_EOF; tok = pdf_lex(str_outer, &lbuf))
1049                         {
1050                                 if (tok == PDF_TOK_KEYWORD && !strcmp(lbuf.scratch, "EMC"))
1051                                         break;
1052                         }
1053
1054                         /* Copy the rest of the existing appearance stream to newbuf */
1055                         for (tok = pdf_lex(str_outer, &lbuf); tok != PDF_TOK_EOF; tok = pdf_lex(str_outer, &lbuf))
1056                         {
1057                                 fz_buffer_printf(ctx, newbuf, " ");
1058                                 pdf_print_token(ctx, newbuf, tok, &lbuf);
1059                         }
1060                 }
1061
1062                 /* Use newbuf in place of the existing appearance stream */
1063                 pdf_update_xobject_contents(doc, form, newbuf);
1064         }
1065         fz_always(ctx)
1066         {
1067                 fz_close(str_outer);
1068                 fz_close(str_inner);
1069                 fz_drop_buffer(ctx, newbuf);
1070                 pdf_lexbuf_fin(&lbuf);
1071         }
1072         fz_catch(ctx)
1073         {
1074                 fz_rethrow(ctx);
1075         }
1076 }
1077
1078 static int get_border_style(pdf_obj *obj)
1079 {
1080         char *sname = pdf_to_name(pdf_dict_getp(obj, "BS/S"));
1081
1082         if (!strcmp(sname, "D"))
1083                 return BS_Dashed;
1084         else if (!strcmp(sname, "B"))
1085                 return BS_Beveled;
1086         else if (!strcmp(sname, "I"))
1087                 return BS_Inset;
1088         else if (!strcmp(sname, "U"))
1089                 return BS_Underline;
1090         else
1091                 return BS_Solid;
1092 }
1093
1094 static float get_border_width(pdf_obj *obj)
1095 {
1096         float w = pdf_to_real(pdf_dict_getp(obj, "BS/W"));
1097         return w == 0.0 ? 1.0 : w;
1098 }
1099
1100 void pdf_update_text_appearance(pdf_document *doc, pdf_obj *obj, char *eventValue)
1101 {
1102         fz_context *ctx = doc->ctx;
1103         text_widget_info info;
1104         pdf_xobject *form = NULL;
1105         fz_buffer *fzbuf = NULL;
1106         fz_matrix tm;
1107         fz_rect rect;
1108         int has_tm;
1109         char *text = NULL;
1110
1111         memset(&info, 0, sizeof(info));
1112
1113         fz_var(info);
1114         fz_var(form);
1115         fz_var(fzbuf);
1116         fz_var(text);
1117         fz_try(ctx)
1118         {
1119                 get_text_widget_info(doc, obj, &info);
1120
1121                 if (eventValue)
1122                         text = to_font_encoding(ctx, info.font_rec.font, eventValue);
1123                 else
1124                         text = pdf_field_value(doc, obj);
1125
1126                 form = load_or_create_form(doc, obj, &rect);
1127
1128                 has_tm = get_matrix(doc, form, info.q, &tm);
1129                 fzbuf = create_text_appearance(doc, &form->bbox, has_tm ? &tm : NULL, &info,
1130                         text?text:"");
1131                 update_marked_content(doc, form, fzbuf);
1132         }
1133         fz_always(ctx)
1134         {
1135                 fz_free(ctx, text);
1136                 pdf_drop_xobject(ctx, form);
1137                 fz_drop_buffer(ctx, fzbuf);
1138                 font_info_fin(ctx, &info.font_rec);
1139         }
1140         fz_catch(ctx)
1141         {
1142                 fz_warn(ctx, "update_text_appearance failed");
1143         }
1144 }
1145
1146 void pdf_update_combobox_appearance(pdf_document *doc, pdf_obj *obj)
1147 {
1148         fz_context *ctx = doc->ctx;
1149         text_widget_info info;
1150         pdf_xobject *form = NULL;
1151         fz_buffer *fzbuf = NULL;
1152         fz_matrix tm;
1153         fz_rect rect;
1154         int has_tm;
1155         pdf_obj *val;
1156         char *text;
1157
1158         memset(&info, 0, sizeof(info));
1159
1160         fz_var(info);
1161         fz_var(form);
1162         fz_var(fzbuf);
1163         fz_try(ctx)
1164         {
1165                 get_text_widget_info(doc, obj, &info);
1166
1167                 val = pdf_get_inheritable(doc, obj, "V");
1168
1169                 if (pdf_is_array(val))
1170                         val = pdf_array_get(val, 0);
1171
1172                 text = pdf_to_str_buf(val);
1173
1174                 if (!text)
1175                         text = "";
1176
1177                 form = load_or_create_form(doc, obj, &rect);
1178
1179                 has_tm = get_matrix(doc, form, info.q, &tm);
1180                 fzbuf = create_text_appearance(doc, &form->bbox, has_tm ? &tm : NULL, &info,
1181                         text?text:"");
1182                 update_marked_content(doc, form, fzbuf);
1183         }
1184         fz_always(ctx)
1185         {
1186                 pdf_drop_xobject(ctx, form);
1187                 fz_drop_buffer(ctx, fzbuf);
1188                 font_info_fin(ctx, &info.font_rec);
1189         }
1190         fz_catch(ctx)
1191         {
1192                 fz_warn(ctx, "update_text_appearance failed");
1193         }
1194 }
1195
1196 void pdf_update_pushbutton_appearance(pdf_document *doc, pdf_obj *obj)
1197 {
1198         fz_context *ctx = doc->ctx;
1199         fz_rect rect;
1200         pdf_xobject *form = NULL;
1201         fz_buffer *fzbuf = NULL;
1202         pdf_obj *tobj = NULL;
1203         font_info font_rec;
1204         int bstyle;
1205         float bwidth;
1206         float btotal;
1207
1208         memset(&font_rec, 0, sizeof(font_rec));
1209
1210         fz_var(font_rec);
1211         fz_var(form);
1212         fz_var(fzbuf);
1213         fz_try(ctx)
1214         {
1215                 form = load_or_create_form(doc, obj, &rect);
1216                 fzbuf = fz_new_buffer(ctx, 0);
1217                 tobj = pdf_dict_getp(obj, "MK/BG");
1218                 if (pdf_is_array(tobj))
1219                 {
1220                         fzbuf_print_color(ctx, fzbuf, tobj, 0, 0.0);
1221                         fz_buffer_printf(ctx, fzbuf, fmt_re,
1222                                 rect.x0, rect.y0, rect.x1, rect.y1);
1223                         fz_buffer_printf(ctx, fzbuf, fmt_f);
1224                 }
1225                 bstyle = get_border_style(obj);
1226                 bwidth = get_border_width(obj);
1227                 btotal = bwidth;
1228                 if (bstyle == BS_Beveled || bstyle == BS_Inset)
1229                 {
1230                         btotal += bwidth;
1231
1232                         if (bstyle == BS_Beveled)
1233                                 fz_buffer_printf(ctx, fzbuf, fmt_g, 1.0);
1234                         else
1235                                 fz_buffer_printf(ctx, fzbuf, fmt_g, 0.33);
1236                         fz_buffer_printf(ctx, fzbuf, fmt_m, bwidth, bwidth);
1237                         fz_buffer_printf(ctx, fzbuf, fmt_l, bwidth, rect.y1 - bwidth);
1238                         fz_buffer_printf(ctx, fzbuf, fmt_l, rect.x1 - bwidth, rect.y1 - bwidth);
1239                         fz_buffer_printf(ctx, fzbuf, fmt_l, rect.x1 - 2 * bwidth, rect.y1 - 2 * bwidth);
1240                         fz_buffer_printf(ctx, fzbuf, fmt_l, 2 * bwidth, rect.y1 - 2 * bwidth);
1241                         fz_buffer_printf(ctx, fzbuf, fmt_l, 2 * bwidth, 2 * bwidth);
1242                         fz_buffer_printf(ctx, fzbuf, fmt_f);
1243                         if (bstyle == BS_Beveled)
1244                                 fzbuf_print_color(ctx, fzbuf, tobj, 0, -0.25);
1245                         else
1246                                 fz_buffer_printf(ctx, fzbuf, fmt_g, 0.66);
1247                         fz_buffer_printf(ctx, fzbuf, fmt_m, rect.x1 - bwidth, rect.y1 - bwidth);
1248                         fz_buffer_printf(ctx, fzbuf, fmt_l, rect.x1 - bwidth, bwidth);
1249                         fz_buffer_printf(ctx, fzbuf, fmt_l, bwidth, bwidth);
1250                         fz_buffer_printf(ctx, fzbuf, fmt_l, 2 * bwidth, 2 * bwidth);
1251                         fz_buffer_printf(ctx, fzbuf, fmt_l, rect.x1 - 2 * bwidth, 2 * bwidth);
1252                         fz_buffer_printf(ctx, fzbuf, fmt_l, rect.x1 - 2 * bwidth, rect.y1 - 2 * bwidth);
1253                         fz_buffer_printf(ctx, fzbuf, fmt_f);
1254                 }
1255
1256                 tobj = pdf_dict_getp(obj, "MK/BC");
1257                 if (tobj)
1258                 {
1259                         fzbuf_print_color(ctx, fzbuf, tobj, 1, 0.0);
1260                         fz_buffer_printf(ctx, fzbuf, fmt_w, bwidth);
1261                         fz_buffer_printf(ctx, fzbuf, fmt_re,
1262                                 bwidth/2, bwidth/2,
1263                                 rect.x1 -bwidth/2, rect.y1 - bwidth/2);
1264                         fz_buffer_printf(ctx, fzbuf, fmt_s);
1265                 }
1266
1267                 tobj = pdf_dict_getp(obj, "MK/CA");
1268                 if (tobj)
1269                 {
1270                         fz_rect clip = rect;
1271                         fz_rect bounds;
1272                         fz_matrix mat;
1273                         char *da = pdf_to_str_buf(pdf_get_inheritable(doc, obj, "DA"));
1274                         char *text = pdf_to_str_buf(tobj);
1275
1276                         clip.x0 += btotal;
1277                         clip.y0 += btotal;
1278                         clip.x1 -= btotal;
1279                         clip.y1 -= btotal;
1280
1281                         get_font_info(doc, form->resources, da, &font_rec);
1282                         measure_text(doc, &font_rec, &fz_identity, text, &bounds);
1283                         fz_translate(&mat, (rect.x1 - bounds.x1)/2, (rect.y1 - bounds.y1)/2);
1284                         fzbuf_print_text(ctx, fzbuf, &clip, NULL, &font_rec, &mat, text);
1285                 }
1286
1287                 pdf_update_xobject_contents(doc, form, fzbuf);
1288         }
1289         fz_always(ctx)
1290         {
1291                 font_info_fin(ctx, &font_rec);
1292                 fz_drop_buffer(ctx, fzbuf);
1293                 pdf_drop_xobject(ctx, form);
1294         }
1295         fz_catch(ctx)
1296         {
1297                 fz_rethrow(ctx);
1298         }
1299 }
1300
1301 void pdf_update_text_markup_appearance(pdf_document *doc, pdf_annot *annot, fz_annot_type type)
1302 {
1303         float color[3];
1304         float alpha;
1305         float line_height;
1306         float line_thickness;
1307
1308         switch (type)
1309         {
1310                 case FZ_ANNOT_HIGHLIGHT:
1311                         color[0] = 1.0;
1312                         color[1] = 1.0;
1313                         color[2] = 0.0;
1314                         alpha = 0.5;
1315                         line_thickness = 1.0;
1316                         line_height = 0.5;
1317                         break;
1318                 case FZ_ANNOT_UNDERLINE:
1319                         color[0] = 0.0;
1320                         color[1] = 0.0;
1321                         color[2] = 1.0;
1322                         alpha = 1.0;
1323                         line_thickness = LINE_THICKNESS;
1324                         line_height = UNDERLINE_HEIGHT;
1325                         break;
1326                 case FZ_ANNOT_STRIKEOUT:
1327                         color[0] = 1.0;
1328                         color[1] = 0.0;
1329                         color[2] = 0.0;
1330                         alpha = 1.0;
1331                         line_thickness = LINE_THICKNESS;
1332                         line_height = STRIKE_HEIGHT;
1333                         break;
1334                 default:
1335                         return;
1336         }
1337
1338         pdf_set_markup_appearance(doc, annot, color, alpha, line_thickness, line_height);
1339 }
1340
1341 static void update_rect(fz_context *ctx, pdf_annot *annot)
1342 {
1343         pdf_to_rect(ctx, pdf_dict_gets(annot->obj, "Rect"), &annot->rect);
1344         annot->pagerect = annot->rect;
1345         fz_transform_rect(&annot->pagerect, &annot->page->ctm);
1346 }
1347
1348 void pdf_set_annot_appearance(pdf_document *doc, pdf_annot *annot, fz_rect *rect, fz_display_list *disp_list)
1349 {
1350         fz_context *ctx = doc->ctx;
1351         pdf_obj *obj = annot->obj;
1352         const fz_matrix *page_ctm = &annot->page->ctm;
1353         fz_matrix ctm;
1354         fz_matrix mat = fz_identity;
1355         fz_device *dev = NULL;
1356         pdf_xobject *xobj = NULL;
1357
1358         fz_invert_matrix(&ctm, page_ctm);
1359
1360         fz_var(dev);
1361         fz_try(ctx)
1362         {
1363                 pdf_obj *ap_obj;
1364                 fz_rect trect = *rect;
1365
1366                 fz_transform_rect(&trect, &ctm);
1367
1368                 pdf_dict_puts_drop(obj, "Rect", pdf_new_rect(doc, &trect));
1369
1370                 /* See if there is a current normal appearance */
1371                 ap_obj = pdf_dict_getp(obj, "AP/N");
1372                 if (!pdf_is_stream(doc, pdf_to_num(ap_obj), pdf_to_gen(ap_obj)))
1373                         ap_obj = NULL;
1374
1375                 if (ap_obj == NULL)
1376                 {
1377                         ap_obj = pdf_new_xobject(doc, &trect, &mat);
1378                         pdf_dict_putp_drop(obj, "AP/N", ap_obj);
1379                 }
1380                 else
1381                 {
1382                         pdf_xref_ensure_incremental_object(doc, pdf_to_num(ap_obj));
1383                         /* Update bounding box and matrix in reused xobject obj */
1384                         pdf_dict_puts_drop(ap_obj, "BBox", pdf_new_rect(doc, &trect));
1385                         pdf_dict_puts_drop(ap_obj, "Matrix", pdf_new_matrix(doc, &mat));
1386                 }
1387
1388                 dev = pdf_new_pdf_device(doc, ap_obj, pdf_dict_gets(ap_obj, "Resources"), &mat);
1389                 fz_run_display_list(disp_list, dev, &ctm, &fz_infinite_rect, NULL);
1390                 fz_free_device(dev);
1391
1392                 /* Mark the appearance as changed - required for partial update */
1393                 xobj = pdf_load_xobject(doc, ap_obj);
1394                 if (xobj)
1395                 {
1396                         /* Update bounding box and matrix also in the xobject structure */
1397                         xobj->bbox = trect;
1398                         xobj->matrix = mat;
1399                         xobj->iteration++;
1400                         pdf_drop_xobject(ctx, xobj);
1401                 }
1402
1403                 doc->dirty = 1;
1404
1405                 update_rect(ctx, annot);
1406         }
1407         fz_catch(ctx)
1408         {
1409                 fz_free_device(dev);
1410                 fz_rethrow(ctx);
1411         }
1412 }
1413
1414 static fz_point *
1415 quadpoints(pdf_document *doc, pdf_obj *annot, int *nout)
1416 {
1417         fz_context *ctx = doc->ctx;
1418         pdf_obj *quad = pdf_dict_gets(annot, "QuadPoints");
1419         fz_point *qp = NULL;
1420         int i, n;
1421
1422         if (!quad)
1423                 return NULL;
1424
1425         n = pdf_array_len(quad);
1426
1427         if (n%8 != 0)
1428                 return NULL;
1429
1430         fz_var(qp);
1431         fz_try(ctx)
1432         {
1433                 qp = fz_malloc_array(ctx, n/2, sizeof(fz_point));
1434
1435                 for (i = 0; i < n; i += 2)
1436                 {
1437                         qp[i/2].x = pdf_to_real(pdf_array_get(quad, i));
1438                         qp[i/2].y = pdf_to_real(pdf_array_get(quad, i+1));
1439                 }
1440         }
1441         fz_catch(ctx)
1442         {
1443                 fz_free(ctx, qp);
1444                 fz_rethrow(ctx);
1445         }
1446
1447         *nout = n/2;
1448
1449         return qp;
1450 }
1451
1452 void pdf_set_markup_appearance(pdf_document *doc, pdf_annot *annot, float color[3], float alpha, float line_thickness, float line_height)
1453 {
1454         fz_context *ctx = doc->ctx;
1455         const fz_matrix *page_ctm = &annot->page->ctm;
1456         fz_path *path = NULL;
1457         fz_stroke_state *stroke = NULL;
1458         fz_device *dev = NULL;
1459         fz_display_list *strike_list = NULL;
1460         int i, n;
1461         fz_point *qp = quadpoints(doc, annot->obj, &n);
1462
1463         if (!qp || n <= 0)
1464                 return;
1465
1466         fz_var(path);
1467         fz_var(stroke);
1468         fz_var(dev);
1469         fz_var(strike_list);
1470         fz_try(ctx)
1471         {
1472                 fz_rect rect = fz_empty_rect;
1473
1474                 rect.x0 = rect.x1 = qp[0].x;
1475                 rect.y0 = rect.y1 = qp[0].y;
1476                 for (i = 0; i < n; i++)
1477                         fz_include_point_in_rect(&rect, &qp[i]);
1478
1479                 strike_list = fz_new_display_list(ctx);
1480                 dev = fz_new_list_device(ctx, strike_list);
1481
1482                 for (i = 0; i < n; i += 4)
1483                 {
1484                         fz_point pt0 = qp[i];
1485                         fz_point pt1 = qp[i+1];
1486                         fz_point up;
1487                         float thickness;
1488
1489                         up.x = qp[i+2].x - qp[i+1].x;
1490                         up.y = qp[i+2].y - qp[i+1].y;
1491
1492                         pt0.x += line_height * up.x;
1493                         pt0.y += line_height * up.y;
1494                         pt1.x += line_height * up.x;
1495                         pt1.y += line_height * up.y;
1496
1497                         thickness = sqrtf(up.x * up.x + up.y * up.y) * line_thickness;
1498
1499                         if (!stroke || fz_abs(stroke->linewidth - thickness) < SMALL_FLOAT)
1500                         {
1501                                 if (stroke)
1502                                 {
1503                                         // assert(path)
1504                                         fz_stroke_path(dev, path, stroke, page_ctm, fz_device_rgb(ctx), color, alpha);
1505                                         fz_drop_stroke_state(ctx, stroke);
1506                                         stroke = NULL;
1507                                         fz_free_path(ctx, path);
1508                                         path = NULL;
1509                                 }
1510
1511                                 stroke = fz_new_stroke_state(ctx);
1512                                 stroke->linewidth = thickness;
1513                                 path = fz_new_path(ctx);
1514                         }
1515
1516                         fz_moveto(ctx, path, pt0.x, pt0.y);
1517                         fz_lineto(ctx, path, pt1.x, pt1.y);
1518                 }
1519
1520                 if (stroke)
1521                 {
1522                         fz_stroke_path(dev, path, stroke, page_ctm, fz_device_rgb(ctx), color, alpha);
1523                 }
1524
1525                 fz_transform_rect(&rect, page_ctm);
1526                 pdf_set_annot_appearance(doc, annot, &rect, strike_list);
1527         }
1528         fz_always(ctx)
1529         {
1530                 fz_free(ctx, qp);
1531                 fz_free_device(dev);
1532                 fz_drop_stroke_state(ctx, stroke);
1533                 fz_free_path(ctx, path);
1534                 fz_drop_display_list(ctx, strike_list);
1535         }
1536         fz_catch(ctx)
1537         {
1538                 fz_rethrow(ctx);
1539         }
1540 }
1541
1542 static fz_colorspace *pdf_to_color(pdf_document *doc, pdf_obj *col, float color[4])
1543 {
1544         fz_colorspace *cs;
1545         int i, ncol = pdf_array_len(col);
1546
1547         switch (ncol)
1548         {
1549         case 1: cs = fz_device_gray(doc->ctx); break;
1550         case 3: cs = fz_device_rgb(doc->ctx); break;
1551         case 4: cs = fz_device_cmyk(doc->ctx); break;
1552         default: return NULL;
1553         }
1554
1555         for (i = 0; i < ncol; i++)
1556                 color[i] = pdf_to_real(pdf_array_get(col, i));
1557
1558         return cs;
1559 }
1560
1561 void pdf_update_ink_appearance(pdf_document *doc, pdf_annot *annot)
1562 {
1563         fz_context *ctx = doc->ctx;
1564         const fz_matrix *page_ctm = &annot->page->ctm;
1565         fz_path *path = NULL;
1566         fz_stroke_state *stroke = NULL;
1567         fz_device *dev = NULL;
1568         fz_display_list *strike_list = NULL;
1569         fz_colorspace *cs = NULL;
1570
1571         fz_var(path);
1572         fz_var(stroke);
1573         fz_var(dev);
1574         fz_var(strike_list);
1575         fz_var(cs);
1576         fz_try(ctx)
1577         {
1578                 fz_rect rect = fz_empty_rect;
1579                 float color[4];
1580                 float width;
1581                 pdf_obj *list;
1582                 int n, m, i, j;
1583                 int empty = 1;
1584
1585                 cs = pdf_to_color(doc, pdf_dict_gets(annot->obj, "C"), color);
1586                 if (!cs)
1587                 {
1588                         cs = fz_device_rgb(ctx);
1589                         color[0] = 1.0f;
1590                         color[1] = 0.0f;
1591                         color[2] = 0.0f;
1592                 }
1593
1594                 width = pdf_to_real(pdf_dict_gets(pdf_dict_gets(annot->obj, "BS"), "W"));
1595                 if (width == 0.0f)
1596                         width = 1.0f;
1597
1598                 list = pdf_dict_gets(annot->obj, "InkList");
1599
1600                 n = pdf_array_len(list);
1601
1602                 strike_list = fz_new_display_list(ctx);
1603                 dev = fz_new_list_device(ctx, strike_list);
1604                 path = fz_new_path(ctx);
1605                 stroke = fz_new_stroke_state(ctx);
1606                 stroke->linewidth = width;
1607                 stroke->start_cap = stroke->end_cap = FZ_LINECAP_ROUND;
1608                 stroke->linejoin = FZ_LINEJOIN_ROUND;
1609
1610                 for (i = 0; i < n; i ++)
1611                 {
1612                         fz_point pt_last;
1613                         pdf_obj *arc = pdf_array_get(list, i);
1614                         m = pdf_array_len(arc);
1615
1616                         for (j = 0; j < m-1; j += 2)
1617                         {
1618                                 fz_point pt;
1619                                 pt.x = pdf_to_real(pdf_array_get(arc, j));
1620                                 pt.y = pdf_to_real(pdf_array_get(arc, j+1));
1621
1622                                 if (i == 0 && j == 0)
1623                                 {
1624                                         rect.x0 = rect.x1 = pt.x;
1625                                         rect.y0 = rect.y1 = pt.y;
1626                                         empty = 0;
1627                                 }
1628                                 else
1629                                 {
1630                                         fz_include_point_in_rect(&rect, &pt);
1631                                 }
1632
1633                                 if (j == 0)
1634                                         fz_moveto(ctx, path, pt.x, pt.y);
1635                                 else
1636                                         fz_curvetov(ctx, path, pt_last.x, pt_last.y, (pt.x + pt_last.x) / 2, (pt.y + pt_last.y) / 2);
1637                                 pt_last = pt;
1638                         }
1639                         fz_lineto(ctx, path, pt_last.x, pt_last.y);
1640                 }
1641
1642                 fz_stroke_path(dev, path, stroke, page_ctm, cs, color, 1.0f);
1643
1644                 fz_expand_rect(&rect, width);
1645                 /*
1646                         Expand the rectangle by width all around. We cannot use
1647                         fz_expand_rect because the rectangle might be empty in the
1648                         single point case
1649                 */
1650                 if (!empty)
1651                 {
1652                         rect.x0 -= width;
1653                         rect.y0 -= width;
1654                         rect.x1 += width;
1655                         rect.y1 += width;
1656                 }
1657
1658
1659                 fz_transform_rect(&rect, page_ctm);
1660                 pdf_set_annot_appearance(doc, annot, &rect, strike_list);
1661         }
1662         fz_always(ctx)
1663         {
1664                 fz_drop_colorspace(ctx, cs);
1665                 fz_free_device(dev);
1666                 fz_drop_stroke_state(ctx, stroke);
1667                 fz_free_path(ctx, path);
1668                 fz_drop_display_list(ctx, strike_list);
1669         }
1670         fz_catch(ctx)
1671         {
1672                 fz_rethrow(ctx);
1673         }
1674 }
1675
1676 static void add_text(fz_context *ctx, font_info *font_rec, fz_text *text, char *str, int str_len, float x, float y)
1677 {
1678         fz_font *font = font_rec->font->font;
1679         int mask = FT_LOAD_NO_SCALE | FT_LOAD_IGNORE_TRANSFORM;
1680
1681         while (str_len--)
1682         {
1683                 FT_Fixed adv;
1684
1685                 /* FIXME: convert str from utf8 to WinAnsi */
1686                 int gid = FT_Get_Char_Index(font->ft_face, *str);
1687                 fz_add_text(ctx, text, gid, *str++, x, y);
1688
1689                 FT_Get_Advance(font->ft_face, gid, mask, &adv);
1690                 x += ((float)adv) * font_rec->da_rec.font_size / ((FT_Face)font->ft_face)->units_per_EM;
1691         }
1692 }
1693
1694 static fz_text *layout_text(fz_context *ctx, font_info *font_rec, char *str, float x, float y)
1695 {
1696         fz_matrix tm;
1697         fz_font *font = font_rec->font->font;
1698         fz_text *text;
1699
1700         fz_scale(&tm, font_rec->da_rec.font_size, font_rec->da_rec.font_size);
1701         text = fz_new_text(ctx, font, &tm, 0);
1702
1703         fz_try(ctx)
1704         {
1705                 add_text(ctx, font_rec, text, str, strlen(str), x, y);
1706         }
1707         fz_catch(ctx)
1708         {
1709                 fz_free_text(ctx, text);
1710                 fz_rethrow(ctx);
1711         }
1712
1713         return text;
1714 }
1715
1716 static fz_text *fit_text(fz_context *ctx, font_info *font_rec, char *str, fz_rect *bounds)
1717 {
1718         float width = bounds->x1 - bounds->x0;
1719         float height = bounds->y1 - bounds->y0;
1720         fz_matrix tm;
1721         fz_text *text = NULL;
1722         text_splitter splitter;
1723         float ascender;
1724
1725         /* Initially aim for one-line of text */
1726         font_rec->da_rec.font_size = height / font_rec->lineheight;
1727
1728         text_splitter_init(&splitter, font_rec, str, width, height, 1);
1729
1730         fz_var(text);
1731         fz_try(ctx)
1732         {
1733                 int i;
1734                 while (!splitter.done)
1735                 {
1736                         /* Try a layout pass */
1737                         int line = 0;
1738                         float font_size;
1739                         float x = 0.0;
1740                         float y = 0.0;
1741
1742
1743                         fz_free_text(ctx, text);
1744                         text = NULL;
1745                         font_size = font_rec->da_rec.font_size;
1746                         fz_scale(&tm, font_size, font_size);
1747                         text = fz_new_text(ctx, font_rec->font->font, &tm, 0);
1748
1749                         text_splitter_start_pass(&splitter);
1750
1751                         /* Layout unscaled text to a scaled-up width, so that
1752                         * the scaled-down text will fit the unscaled width */
1753
1754                         while (!splitter.done && line < splitter.max_lines)
1755                         {
1756                                 /* Layout a line */
1757                                 text_splitter_start_line(&splitter);
1758
1759                                 while (!splitter.done && text_splitter_layout(ctx, &splitter))
1760                                 {
1761                                         if (splitter.text[splitter.text_start] != ' ')
1762                                         {
1763                                                 float dx, dy;
1764                                                 char *word = str+splitter.text_start;
1765                                                 int wordlen = splitter.text_end-splitter.text_start;
1766
1767                                                 text_splitter_move(&splitter, -line, &dx, &dy);
1768                                                 x += dx;
1769                                                 y += dy;
1770                                                 add_text(ctx, font_rec, text, word, wordlen, x, y);
1771                                         }
1772                                 }
1773
1774                                 line ++;
1775                         }
1776
1777                         if (!splitter.done)
1778                                 text_splitter_retry(&splitter);
1779                 }
1780
1781                 /* Post process text with the scale determined by the splitter
1782                  * and with the required offst */
1783                 fz_pre_scale(&text->trm, splitter.scale, splitter.scale);
1784                 ascender = font_rec->font->ascent * font_rec->da_rec.font_size * splitter.scale / 1000.0f;
1785                 for (i = 0; i < text->len; i++)
1786                 {
1787                         text->items[i].x = text->items[i].x * splitter.scale + bounds->x0;
1788                         text->items[i].y = text->items[i].y * splitter.scale + bounds->y1 - ascender;
1789                 }
1790         }
1791         fz_catch(ctx)
1792         {
1793                 fz_free_text(ctx, text);
1794                 fz_rethrow(ctx);
1795         }
1796
1797         return text;
1798 }
1799
1800 static void rect_center(const fz_rect *rect, fz_point *c)
1801 {
1802         c->x = (rect->x0 + rect->x1) / 2.0f;
1803         c->y = (rect->y0 + rect->y1) / 2.0f;
1804 }
1805
1806 static void center_rect_within_rect(const fz_rect *tofit, const fz_rect *within, fz_matrix *mat)
1807 {
1808         float xscale = (within->x1 - within->x0) / (tofit->x1 - tofit->x0);
1809         float yscale = (within->y1 - within->y0) / (tofit->y1 - tofit->y0);
1810         float scale = fz_min(xscale, yscale);
1811         fz_point tofit_center;
1812         fz_point within_center;
1813
1814         rect_center(within, &within_center);
1815         rect_center(tofit, &tofit_center);
1816
1817         /* Translate "tofit" to be centered on the origin
1818          * Scale "tofit" to a size that fits within "within"
1819          * Translate "tofit" to "within's" center
1820          * Do all the above in reverse order so that we can use the fz_pre_xx functions */
1821         fz_translate(mat, within_center.x, within_center.y);
1822         fz_pre_scale(mat, scale, scale);
1823         fz_pre_translate(mat, -tofit_center.x, -tofit_center.y);
1824 }
1825
1826 static const float outline_thickness = 15.0f;
1827
1828 static void draw_rounded_rect(fz_context *ctx, fz_path *path)
1829 {
1830         fz_moveto(ctx, path, 20.0, 60.0);
1831         fz_curveto(ctx, path, 20.0, 30.0, 30.0, 20.0, 60.0, 20.0);
1832         fz_lineto(ctx, path, 340.0, 20.0);
1833         fz_curveto(ctx, path, 370.0, 20.0, 380.0, 30.0, 380.0, 60.0);
1834         fz_lineto(ctx, path, 380.0, 340.0);
1835         fz_curveto(ctx, path, 380.0, 370.0, 370.0, 380.0, 340.0, 380.0);
1836         fz_lineto(ctx, path, 60.0, 380.0);
1837         fz_curveto(ctx, path, 30.0, 380.0, 20.0, 370.0, 20.0, 340.0);
1838         fz_closepath(ctx, path);
1839 }
1840
1841 static void draw_speech_bubble(fz_context *ctx, fz_path *path)
1842 {
1843         fz_moveto(ctx, path, 199.0f, 315.6f);
1844         fz_curveto(ctx, path, 35.6f, 315.6f, 27.0f, 160.8f, 130.2f, 131.77f);
1845         fz_curveto(ctx, path, 130.2f, 93.07f, 113.0f, 83.4f, 113.0f, 83.4f);
1846         fz_curveto(ctx, path, 138.8f, 73.72f, 173.2f, 83.4f, 190.4f, 122.1f);
1847         fz_curveto(ctx, path, 391.64f, 122.1f, 362.4f, 315.6f, 199.0f, 315.6f);
1848         fz_closepath(ctx, path);
1849 }
1850
1851 void pdf_update_text_annot_appearance(pdf_document *doc, pdf_annot *annot)
1852 {
1853         static float white[3] = {1.0, 1.0, 1.0};
1854         static float yellow[3] = {1.0, 1.0, 0.0};
1855         static float black[3] = {0.0, 0.0, 0.0};
1856         fz_context *ctx = doc->ctx;
1857         const fz_matrix *page_ctm = &annot->page->ctm;
1858         fz_display_list *dlist = NULL;
1859         fz_device *dev = NULL;
1860         fz_colorspace *cs = NULL;
1861         fz_path *path = NULL;
1862         fz_stroke_state *stroke = NULL;
1863
1864         fz_var(path);
1865         fz_var(stroke);
1866         fz_var(dlist);
1867         fz_var(dev);
1868         fz_var(cs);
1869         fz_try(ctx)
1870         {
1871                 fz_rect rect;
1872                 fz_rect bounds;
1873                 fz_matrix tm;
1874
1875                 pdf_to_rect(ctx, pdf_dict_gets(annot->obj, "Rect"), &rect);
1876                 dlist = fz_new_display_list(ctx);
1877                 dev = fz_new_list_device(ctx, dlist);
1878                 stroke = fz_new_stroke_state(ctx);
1879                 stroke->linewidth = outline_thickness;
1880                 stroke->linejoin = FZ_LINEJOIN_ROUND;
1881
1882                 path = fz_new_path(ctx);
1883                 draw_rounded_rect(ctx, path);
1884                 fz_bound_path(ctx, path, NULL, &fz_identity, &bounds);
1885                 fz_expand_rect(&bounds, outline_thickness);
1886                 center_rect_within_rect(&bounds, &rect, &tm);
1887                 fz_concat(&tm, &tm, page_ctm);
1888                 cs = fz_device_rgb(ctx);
1889                 fz_fill_path(dev, path, 0, &tm, cs, yellow, 1.0f);
1890                 fz_stroke_path(dev, path, stroke, &tm, cs, black, 1.0f);
1891                 fz_free_path(ctx, path);
1892                 path = NULL;
1893
1894                 path = fz_new_path(ctx);
1895                 draw_speech_bubble(ctx, path);
1896                 fz_fill_path(dev, path, 0, &tm, cs, white, 1.0f);
1897                 fz_stroke_path(dev, path, stroke, &tm, cs, black, 1.0f);
1898
1899                 fz_transform_rect(&rect, page_ctm);
1900                 pdf_set_annot_appearance(doc, annot, &rect, dlist);
1901
1902                 /* Drop the cached xobject from the annotation structure to
1903                  * force a redraw on next pdf_update_page call */
1904                 pdf_drop_xobject(ctx, annot->ap);
1905                 annot->ap = NULL;
1906         }
1907         fz_always(ctx)
1908         {
1909                 fz_free_device(dev);
1910                 fz_drop_display_list(ctx, dlist);
1911                 fz_drop_stroke_state(ctx, stroke);
1912                 fz_free_path(ctx, path);
1913                 fz_drop_colorspace(ctx, cs);
1914         }
1915         fz_catch(ctx)
1916         {
1917                 fz_rethrow(ctx);
1918         }
1919 }
1920
1921 void pdf_update_free_text_annot_appearance(pdf_document *doc, pdf_annot *annot)
1922 {
1923         fz_context *ctx = doc->ctx;
1924         const fz_matrix *page_ctm = &annot->page->ctm;
1925         pdf_obj *obj = annot->obj;
1926         pdf_obj *dr = pdf_dict_getp(annot->page->me, "Resources");
1927         fz_display_list *dlist = NULL;
1928         fz_device *dev = NULL;
1929         font_info font_rec;
1930         fz_text *text = NULL;
1931         fz_colorspace *cs = NULL;
1932
1933         memset(&font_rec, 0, sizeof(font_rec));
1934
1935         /* Set some sane defaults in case the parsing of the font_rec fails */
1936         font_rec.da_rec.col_size = 1; /* Default to greyscale */
1937         font_rec.da_rec.font_size = 12; /* Default to 12 point */
1938
1939         fz_var(dlist);
1940         fz_var(dev);
1941         fz_var(text);
1942         fz_var(cs);
1943         fz_try(ctx)
1944         {
1945                 char *contents = pdf_to_str_buf(pdf_dict_gets(obj, "Contents"));
1946                 char *da = pdf_to_str_buf(pdf_dict_gets(obj, "DA"));
1947                 fz_rect rect = annot->rect;
1948                 fz_point pos;
1949
1950                 get_font_info(doc, dr, da, &font_rec);
1951
1952                 switch (font_rec.da_rec.col_size)
1953                 {
1954                 default: cs = fz_device_gray(doc->ctx); break;
1955                 case 3: cs = fz_device_rgb(doc->ctx); break;
1956                 case 4: cs = fz_device_cmyk(doc->ctx); break;
1957                 }
1958
1959                 /* Adjust for the descender */
1960                 pos.x = rect.x0;
1961                 pos.y = rect.y0 - font_rec.font->descent * font_rec.da_rec.font_size / 1000.0f;
1962
1963                 text = layout_text(ctx, &font_rec, contents, pos.x, pos.y);
1964
1965                 dlist = fz_new_display_list(ctx);
1966                 dev = fz_new_list_device(ctx, dlist);
1967                 fz_fill_text(dev, text, page_ctm, cs, font_rec.da_rec.col, 1.0f);
1968
1969                 fz_transform_rect(&rect, page_ctm);
1970                 pdf_set_annot_appearance(doc, annot, &rect, dlist);
1971         }
1972         fz_always(ctx)
1973         {
1974                 fz_free_device(dev);
1975                 fz_drop_display_list(ctx, dlist);
1976                 font_info_fin(ctx, &font_rec);
1977                 fz_free_text(ctx, text);
1978                 fz_drop_colorspace(ctx, cs);
1979         }
1980         fz_catch(ctx)
1981         {
1982                 fz_rethrow(ctx);
1983         }
1984 }
1985
1986 static void draw_logo(fz_context *ctx, fz_path *path)
1987 {
1988         fz_moveto(ctx, path, 122.25f, 0.0f);
1989         fz_lineto(ctx, path, 122.25f, 14.249f);
1990         fz_curveto(ctx, path, 125.98f, 13.842f, 129.73f, 13.518f, 133.5f, 13.277f);
1991         fz_lineto(ctx, path, 133.5f, 0.0f);
1992         fz_lineto(ctx, path, 122.25f, 0.0f);
1993         fz_closepath(ctx, path);
1994         fz_moveto(ctx, path, 140.251f, 0.0f);
1995         fz_lineto(ctx, path, 140.251f, 12.935f);
1996         fz_curveto(ctx, path, 152.534f, 12.477f, 165.03f, 12.899f, 177.75f, 14.249f);
1997         fz_lineto(ctx, path, 177.75f, 21.749f);
1998         fz_curveto(ctx, path, 165.304f, 20.413f, 152.809f, 19.871f, 140.251f, 20.348f);
1999         fz_lineto(ctx, path, 140.251f, 39.0f);
2000         fz_lineto(ctx, path, 133.5f, 39.0f);
2001         fz_lineto(ctx, path, 133.5f, 20.704f);
2002         fz_curveto(ctx, path, 129.756f, 20.956f, 126.006f, 21.302f, 122.25f, 21.749f);
2003         fz_lineto(ctx, path, 122.25f, 50.999f);
2004         fz_lineto(ctx, path, 177.751f, 50.999f);
2005         fz_lineto(ctx, path, 177.751f, 0.0f);
2006         fz_lineto(ctx, path, 140.251f, 0.0f);
2007         fz_closepath(ctx, path);
2008         fz_moveto(ctx, path, 23.482f, 129.419f);
2009         fz_curveto(ctx, path, -20.999f, 199.258f, -0.418f, 292.039f, 69.42f, 336.519f);
2010         fz_curveto(ctx, path, 139.259f, 381.0f, 232.04f, 360.419f, 276.52f, 290.581f);
2011         fz_curveto(ctx, path, 321.001f, 220.742f, 300.42f, 127.961f, 230.582f, 83.481f);
2012         fz_curveto(ctx, path, 160.743f, 39.0f, 67.962f, 59.581f, 23.482f, 129.419f);
2013         fz_closepath(ctx, path);
2014         fz_moveto(ctx, path, 254.751f, 128.492f);
2015         fz_curveto(ctx, path, 303.074f, 182.82f, 295.364f, 263.762f, 237.541f, 309.165f);
2016         fz_curveto(ctx, path, 179.718f, 354.568f, 93.57f, 347.324f, 45.247f, 292.996f);
2017         fz_curveto(ctx, path, -3.076f, 238.668f, 4.634f, 157.726f, 62.457f, 112.323f);
2018         fz_curveto(ctx, path, 120.28f, 66.92f, 206.428f, 74.164f, 254.751f, 128.492f);
2019         fz_closepath(ctx, path);
2020         fz_moveto(ctx, path, 111.0f, 98.999f);
2021         fz_curveto(ctx, path, 87.424f, 106.253f, 68.25f, 122.249f, 51.75f, 144.749f);
2022         fz_lineto(ctx, path, 103.5f, 297.749f);
2023         fz_lineto(ctx, path, 213.75f, 298.499f);
2024         fz_curveto(ctx, path, 206.25f, 306.749f, 195.744f, 311.478f, 185.25f, 314.249f);
2025         fz_curveto(ctx, path, 164.22f, 319.802f, 141.22f, 319.775f, 120.0f, 314.999f);
2026         fz_curveto(ctx, path, 96.658f, 309.745f, 77.25f, 298.499f, 55.5f, 283.499f);
2027         fz_curveto(ctx, path, 69.75f, 299.249f, 84.617f, 311.546f, 102.75f, 319.499f);
2028         fz_curveto(ctx, path, 117.166f, 325.822f, 133.509f, 327.689f, 149.25f, 327.749f);
2029         fz_curveto(ctx, path, 164.21f, 327.806f, 179.924f, 326.532f, 193.5f, 320.249f);
2030         fz_curveto(ctx, path, 213.95f, 310.785f, 232.5f, 294.749f, 245.25f, 276.749f);
2031         fz_lineto(ctx, path, 227.25f, 276.749f);
2032         fz_curveto(ctx, path, 213.963f, 276.749f, 197.25f, 263.786f, 197.25f, 250.499f);
2033         fz_lineto(ctx, path, 197.25f, 112.499f);
2034         fz_curveto(ctx, path, 213.75f, 114.749f, 228.0f, 127.499f, 241.5f, 140.999f);
2035         fz_curveto(ctx, path, 231.75f, 121.499f, 215.175f, 109.723f, 197.25f, 101.249f);
2036         fz_curveto(ctx, path, 181.5f, 95.249f, 168.412f, 94.775f, 153.0f, 94.499f);
2037         fz_curveto(ctx, path, 139.42f, 94.256f, 120.75f, 95.999f, 111.0f, 98.999f);
2038         fz_closepath(ctx, path);
2039         fz_moveto(ctx, path, 125.25f, 105.749f);
2040         fz_lineto(ctx, path, 125.25f, 202.499f);
2041         fz_lineto(ctx, path, 95.25f, 117.749f);
2042         fz_curveto(ctx, path, 105.75f, 108.749f, 114.0f, 105.749f, 125.25f, 105.749f);
2043         fz_closepath(ctx, path);
2044 };
2045
2046 static void insert_signature_appearance_layers(pdf_document *doc, pdf_annot *annot)
2047 {
2048         fz_context *ctx = doc->ctx;
2049         pdf_obj *ap = pdf_dict_getp(annot->obj, "AP/N");
2050         pdf_obj *main_ap = NULL;
2051         pdf_obj *frm = NULL;
2052         pdf_obj *n0 = NULL;
2053         fz_rect bbox;
2054         fz_buffer *fzbuf = NULL;
2055
2056         pdf_to_rect(ctx, pdf_dict_gets(ap, "BBox"), &bbox);
2057
2058         fz_var(main_ap);
2059         fz_var(frm);
2060         fz_var(n0);
2061         fz_var(fzbuf);
2062         fz_try(ctx)
2063         {
2064                 main_ap = pdf_new_xobject(doc, &bbox, &fz_identity);
2065                 frm = pdf_new_xobject(doc, &bbox, &fz_identity);
2066                 n0 = pdf_new_xobject(doc, &bbox, &fz_identity);
2067
2068                 pdf_dict_putp(main_ap, "Resources/XObject/FRM", frm);
2069                 fzbuf = fz_new_buffer(ctx, 8);
2070                 fz_buffer_printf(ctx, fzbuf, "/FRM Do");
2071                 pdf_update_stream(doc, pdf_to_num(main_ap), fzbuf);
2072                 pdf_dict_puts_drop(main_ap, "Length", pdf_new_int(doc, fzbuf->len));
2073                 fz_drop_buffer(ctx, fzbuf);
2074                 fzbuf = NULL;
2075
2076                 pdf_dict_putp(frm, "Resources/XObject/n0", n0);
2077                 pdf_dict_putp(frm, "Resources/XObject/n2", ap);
2078                 fzbuf = fz_new_buffer(ctx, 8);
2079                 fz_buffer_printf(ctx, fzbuf, "q 1 0 0 1 0 0 cm /n0 Do Q q 1 0 0 1 0 0 cm /n2 Do Q");
2080                 pdf_update_stream(doc, pdf_to_num(frm), fzbuf);
2081                 pdf_dict_puts_drop(frm, "Length", pdf_new_int(doc, fzbuf->len));
2082                 fz_drop_buffer(ctx, fzbuf);
2083                 fzbuf = NULL;
2084
2085                 fzbuf = fz_new_buffer(ctx, 8);
2086                 fz_buffer_printf(ctx, fzbuf, "%% DSBlank");
2087                 pdf_update_stream(doc, pdf_to_num(n0), fzbuf);
2088                 pdf_dict_puts_drop(n0, "Length", pdf_new_int(doc, fzbuf->len));
2089                 fz_drop_buffer(ctx, fzbuf);
2090                 fzbuf = NULL;
2091
2092                 pdf_dict_putp(annot->obj, "AP/N", main_ap);
2093         }
2094         fz_always(ctx)
2095         {
2096                 pdf_drop_obj(main_ap);
2097                 pdf_drop_obj(frm);
2098                 pdf_drop_obj(n0);
2099         }
2100         fz_catch(ctx)
2101         {
2102                 fz_drop_buffer(ctx, fzbuf);
2103                 fz_rethrow(ctx);
2104         }
2105 }
2106
2107 /* MuPDF blue */
2108 static float logo_color[3] = {(float)0x25/(float)0xFF, (float)0x72/(float)0xFF, (float)0xAC/(float)0xFF};
2109
2110 void pdf_set_signature_appearance(pdf_document *doc, pdf_annot *annot, char *name, char *dn, char *date)
2111 {
2112         fz_context *ctx = doc->ctx;
2113         const fz_matrix *page_ctm = &annot->page->ctm;
2114         pdf_obj *obj = annot->obj;
2115         pdf_obj *dr = pdf_dict_getp(pdf_trailer(doc), "Root/AcroForm/DR");
2116         fz_display_list *dlist = NULL;
2117         fz_device *dev = NULL;
2118         font_info font_rec;
2119         fz_text *text = NULL;
2120         fz_colorspace *cs = NULL;
2121         fz_path *path = NULL;
2122         fz_buffer *fzbuf = NULL;
2123
2124         if (!dr)
2125                 pdf_dict_putp_drop(pdf_trailer(doc), "Root/AcroForm/DR", pdf_new_dict(doc, 1));
2126
2127         memset(&font_rec, 0, sizeof(font_rec));
2128
2129         fz_var(path);
2130         fz_var(dlist);
2131         fz_var(dev);
2132         fz_var(text);
2133         fz_var(cs);
2134         fz_var(fzbuf);
2135         fz_try(ctx)
2136         {
2137                 char *da = pdf_to_str_buf(pdf_dict_gets(obj, "DA"));
2138                 fz_rect rect = annot->rect;
2139                 fz_rect logo_bounds;
2140                 fz_matrix logo_tm;
2141                 unsigned char *bufstr;
2142
2143                 dlist = fz_new_display_list(ctx);
2144                 dev = fz_new_list_device(ctx, dlist);
2145
2146                 path = fz_new_path(ctx);
2147                 draw_logo(ctx, path);
2148                 fz_bound_path(ctx, path, NULL, &fz_identity, &logo_bounds);
2149                 center_rect_within_rect(&logo_bounds, &rect, &logo_tm);
2150                 fz_concat(&logo_tm, &logo_tm, page_ctm);
2151                 cs = fz_device_rgb(ctx);
2152                 fz_fill_path(dev, path, 0, &logo_tm, cs, logo_color, 1.0f);
2153                 fz_drop_colorspace(ctx, cs);
2154                 cs = NULL;
2155
2156                 get_font_info(doc, dr, da, &font_rec);
2157
2158                 switch (font_rec.da_rec.col_size)
2159                 {
2160                 case 1: cs = fz_device_gray(ctx); break;
2161                 case 3: cs = fz_device_rgb(ctx); break;
2162                 case 4: cs = fz_device_cmyk(ctx); break;
2163                 }
2164
2165                 /* Display the name in the left-hand half of the form field */
2166                 rect.x1 = (rect.x0 + rect.x1)/2.0f;
2167                 text = fit_text(ctx, &font_rec, name, &rect);
2168                 fz_fill_text(dev, text, page_ctm, cs, font_rec.da_rec.col, 1.0f);
2169                 fz_free_text(ctx, text);
2170                 text = NULL;
2171
2172                 /* Display the distinguished name in the right-hand half */
2173                 fzbuf = fz_new_buffer(ctx, 256);
2174                 fz_buffer_printf(ctx, fzbuf, "Digitally signed by %s", name);
2175                 fz_buffer_printf(ctx, fzbuf, "\nDN: %s", dn);
2176                 if (date)
2177                         fz_buffer_printf(ctx, fzbuf, "\nDate: %s", date);
2178                 (void)fz_buffer_storage(ctx, fzbuf, &bufstr);
2179                 rect = annot->rect;
2180                 rect.x0 = (rect.x0 + rect.x1)/2.0f;
2181                 text = fit_text(ctx, &font_rec, (char *)bufstr, &rect);
2182                 fz_fill_text(dev, text, page_ctm, cs, font_rec.da_rec.col, 1.0f);
2183
2184                 rect = annot->rect;
2185                 fz_transform_rect(&rect, page_ctm);
2186                 pdf_set_annot_appearance(doc, annot, &rect, dlist);
2187
2188                 /* Drop the cached xobject from the annotation structure to
2189                  * force a redraw on next pdf_update_page call */
2190                 pdf_drop_xobject(ctx, annot->ap);
2191                 annot->ap = NULL;
2192
2193                 insert_signature_appearance_layers(doc, annot);
2194         }
2195         fz_always(ctx)
2196         {
2197                 fz_free_device(dev);
2198                 fz_drop_display_list(ctx, dlist);
2199                 font_info_fin(ctx, &font_rec);
2200                 fz_free_path(ctx, path);
2201                 fz_free_text(ctx, text);
2202                 fz_drop_colorspace(ctx, cs);
2203                 fz_drop_buffer(ctx, fzbuf);
2204         }
2205         fz_catch(ctx)
2206         {
2207                 fz_rethrow(ctx);
2208         }
2209 }
2210
2211 void pdf_update_appearance(pdf_document *doc, pdf_annot *annot)
2212 {
2213         pdf_obj *obj = annot->obj;
2214         if (!pdf_dict_gets(obj, "AP") || pdf_obj_is_dirty(obj))
2215         {
2216                 fz_annot_type type = pdf_annot_obj_type(obj);
2217                 switch (type)
2218                 {
2219                 case FZ_ANNOT_WIDGET:
2220                         switch (pdf_field_type(doc, obj))
2221                         {
2222                         case PDF_WIDGET_TYPE_TEXT:
2223                                 {
2224                                         #if 0
2225                                         pdf_obj *formatting = pdf_dict_getp(obj, "AA/F");
2226                                         if (formatting && doc->js)
2227                                         {
2228                                                 /* Apply formatting */
2229                                                 pdf_js_event e;
2230                                                 fz_context *ctx = doc->ctx;
2231
2232                                                 e.target = obj;
2233                                                 e.value = pdf_field_value(doc, obj);
2234                                                 fz_try(ctx)
2235                                                 {
2236                                                         pdf_js_setup_event(doc->js, &e);
2237                                                 }
2238                                                 fz_always(ctx)
2239                                                 {
2240                                                         fz_free(ctx, e.value);
2241                                                 }
2242                                                 fz_catch(ctx)
2243                                                 {
2244                                                         fz_rethrow(ctx);
2245                                                 }
2246                                                 execute_action(doc, obj, formatting);
2247                                                 /* Update appearance from JS event.value */
2248                                                 pdf_update_text_appearance(doc, obj, pdf_js_get_event(doc->js)->value);
2249                                         }
2250                                         else
2251                                         #endif
2252                                         {
2253                                                 /* Update appearance from field value */
2254                                                 pdf_update_text_appearance(doc, obj, NULL);
2255                                         }
2256                                 }
2257                                 break;
2258                         case PDF_WIDGET_TYPE_PUSHBUTTON:
2259                                 pdf_update_pushbutton_appearance(doc, obj);
2260                                 break;
2261                         case PDF_WIDGET_TYPE_LISTBOX:
2262                         case PDF_WIDGET_TYPE_COMBOBOX:
2263                                 /* Treating listbox and combobox identically for now,
2264                                  * and the behaviour is most appropriate for a combobox */
2265                                 pdf_update_combobox_appearance(doc, obj);
2266                                 break;
2267                         }
2268                         break;
2269                 case FZ_ANNOT_TEXT:
2270                         pdf_update_text_annot_appearance(doc, annot);
2271                         break;
2272                 case FZ_ANNOT_FREETEXT:
2273                         pdf_update_free_text_annot_appearance(doc, annot);
2274                         break;
2275                 case FZ_ANNOT_STRIKEOUT:
2276                 case FZ_ANNOT_UNDERLINE:
2277                 case FZ_ANNOT_HIGHLIGHT:
2278                         pdf_update_text_markup_appearance(doc, annot, type);
2279                         break;
2280                 case FZ_ANNOT_INK:
2281                         pdf_update_ink_appearance(doc, annot);
2282                         break;
2283                 default:
2284                         break;
2285                 }
2286
2287                 pdf_clean_obj(obj);
2288         }
2289 }