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