Coverage Report

Created: 2025-06-10 07:27

/src/ghostpdl/pdf/pdf_annot.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (C) 2019-2025 Artifex Software, Inc.
2
   All Rights Reserved.
3
4
   This software is provided AS-IS with no warranty, either express or
5
   implied.
6
7
   This software is distributed under license and may not be copied,
8
   modified or distributed except as expressly authorized under the terms
9
   of the license contained in the file LICENSE in this distribution.
10
11
   Refer to licensing information at http://www.artifex.com or contact
12
   Artifex Software, Inc.,  39 Mesa Street, Suite 108A, San Francisco,
13
   CA 94129, USA, for further information.
14
*/
15
16
/* Annotation handling for the PDF interpreter */
17
18
#include "pdf_int.h"
19
#include "pdf_stack.h"
20
#include "pdf_annot.h"
21
#include "pdf_file.h"
22
#include "pdf_dict.h"
23
#include "pdf_array.h"
24
#include "pdf_loop_detect.h"
25
#include "pdf_colour.h"
26
#include "pdf_trans.h"
27
#include "pdf_font_types.h"
28
#include "pdf_gstate.h"
29
#include "pdf_misc.h"
30
#include "pdf_optcontent.h"
31
#include "pdf_annot.h"
32
#include "pdf_colour.h"
33
#include "pdf_deref.h"
34
#include "pdf_image.h"
35
#include "pdf_mark.h"
36
#include "pdf_font.h"
37
#include "pdf_text.h"
38
39
#include "gspath2.h"
40
#include "gxfarith.h"
41
#include "gxdevsop.h"               /* For special ops */
42
#include "gsstrtok.h"               /* For gs_strtok */
43
#include "gscoord.h"        /* for gs_concat() and others */
44
#include "gsline.h"         /* For gs_setlinejoin() and friends */
45
#include "gsutil.h"        /* For gs_next_ids() */
46
#include "gspaint.h"        /* For gs_fill() and friends */
47
48
/* Detect if there is a BOM at beginning of string */
49
15
#define IS_UTF8(str) (!strcmp((char *)(str), "\xef\xbb\xbf"))
50
2.73k
#define IS_UTF16(str) (!strcmp((char *)(str), "\xfe\xff"))
51
52
typedef int (*annot_func)(pdf_context *ctx, pdf_dict *annot, pdf_obj *NormAP, bool *render_done);
53
54
typedef struct {
55
    const char *subtype;
56
    annot_func func;
57
    bool simpleAP; /* Just Render AP if it exists? (false means more complicated) */
58
} annot_dispatch_t;
59
60
61
typedef int (*annot_LE_func)(pdf_context *ctx, pdf_dict *annot);
62
63
typedef struct {
64
    const char *name;
65
    annot_LE_func func;
66
} annot_LE_dispatch_t;
67
68
typedef int (*annot_preserve_func)(pdf_context *ctx, pdf_dict *annot, pdf_name *subtype);
69
70
typedef struct {
71
    const char *subtype;
72
    annot_preserve_func func;
73
} annot_preserve_dispatch_t;
74
75
static int pdfi_annot_start_transparency(pdf_context *ctx, pdf_dict *annot)
76
499
{
77
499
    int code;
78
79
499
    if (!ctx->page.has_transparency)
80
207
        return 0;
81
82
    /* TODO: ps code does something with /BM which is apparently the Blend Mode,
83
     * but I am not sure that the value is actually used properly.  Look into it.
84
     * (see pdf_draw.ps/startannottransparency)
85
     */
86
292
    code = gs_clippath(ctx->pgs);
87
292
    if (code < 0)
88
0
        return code;
89
292
    code = pdfi_trans_begin_simple_group(ctx, NULL, false, false, false);
90
292
    (void)gs_newpath(ctx->pgs);
91
292
    return code;
92
292
}
93
94
static int pdfi_annot_end_transparency(pdf_context *ctx, pdf_dict *annot)
95
499
{
96
499
    if (!ctx->page.has_transparency)
97
207
        return 0;
98
99
292
    return pdfi_trans_end_simple_group(ctx);
100
499
}
101
102
/* Get width from BS, if it exists
103
 * Default to 1.0 if not
104
 */
105
static int pdfi_annot_get_BS_width(pdf_context *ctx, pdf_dict *annot, double *width)
106
30
{
107
30
    int code;
108
30
    pdf_dict *BS = NULL;
109
110
30
    *width = 1.0;
111
112
30
    code = pdfi_dict_knownget_type(ctx, annot, "BS", PDF_DICT, (pdf_obj **)&BS);
113
30
    if (code <= 0)
114
0
        goto exit;
115
116
30
    code = pdfi_dict_knownget_number(ctx, BS, "W", width);
117
118
30
 exit:
119
30
    pdfi_countdown(BS);
120
30
    return code;
121
30
}
122
123
/* Set both stroke opacity and fill opacity to either CA or ca
124
 * For annotations, the one value seems to control both.
125
 */
126
static int pdfi_annot_opacity(pdf_context *ctx, pdf_dict *annot)
127
80
{
128
80
    int code = 0;
129
80
    double CA;
130
131
    /* CA -- opacity */
132
80
    code = pdfi_dict_knownget_number(ctx, annot, "CA", &CA);
133
80
    if (code > 0) {
134
45
        code = gs_setstrokeconstantalpha(ctx->pgs, CA);
135
45
        if (code < 0) goto exit;
136
45
        code = gs_setfillconstantalpha(ctx->pgs, CA);
137
45
        goto exit;
138
45
    }
139
    /* If CA not found, we also check for 'ca' even though it's not in the spec */
140
35
    code = pdfi_dict_knownget_number(ctx, annot, "ca", &CA);
141
35
    if (code > 0) {
142
0
        code = gs_setstrokeconstantalpha(ctx->pgs, CA);
143
0
        if (code < 0) goto exit;
144
0
        code = gs_setfillconstantalpha(ctx->pgs, CA);
145
0
    }
146
80
 exit:
147
80
    return code;
148
35
}
149
150
/* Apply RD to provided rect */
151
static int pdfi_annot_applyRD(pdf_context *ctx, pdf_dict *annot, gs_rect *rect)
152
151
{
153
151
    int code;
154
151
    pdf_array *RD = NULL;
155
151
    gs_rect rd;
156
157
151
    code = pdfi_dict_knownget_type(ctx, annot, "RD", PDF_ARRAY, (pdf_obj **)&RD);
158
151
    if (code <= 0) goto exit;
159
160
0
    code = pdfi_array_to_gs_rect(ctx, RD, &rd);
161
0
    if (code < 0) goto exit;
162
163
0
    rect->p.x += rd.p.x;
164
0
    rect->p.y += rd.p.y;
165
0
    rect->q.x -= rd.q.x;
166
0
    rect->q.y -= rd.q.y;
167
168
151
 exit:
169
151
    pdfi_countdown(RD);
170
151
    return code;
171
0
}
172
173
static int pdfi_annot_Rect(pdf_context *ctx, pdf_dict *annot, gs_rect *rect)
174
9.52k
{
175
9.52k
    int code;
176
9.52k
    pdf_array *Rect = NULL;
177
178
9.52k
    code = pdfi_dict_knownget_type(ctx, annot, "Rect", PDF_ARRAY, (pdf_obj **)&Rect);
179
9.52k
    if (code < 0) goto exit;
180
181
9.52k
    code = pdfi_array_to_gs_rect(ctx, Rect, rect);
182
9.52k
    if (code < 0) goto exit;
183
184
9.50k
    pdfi_normalize_rect(ctx, rect);
185
186
9.52k
 exit:
187
9.52k
    pdfi_countdown(Rect);
188
9.52k
    return code;
189
9.50k
}
190
191
static int pdfi_annot_position_AP(pdf_context *ctx, pdf_dict *annot, pdf_stream *AP)
192
4.81k
{
193
4.81k
    int code = 0;
194
4.81k
    gs_rect rect;
195
4.81k
    pdf_array *BBox = NULL;
196
4.81k
    gs_rect bbox;
197
4.81k
    pdf_array *Matrix = NULL;
198
4.81k
    gs_matrix matrix;
199
4.81k
    double xscale, yscale;
200
4.81k
    pdf_dict *Annot_dict;
201
202
4.81k
    code = pdfi_dict_from_obj(ctx, (pdf_obj *)AP, &Annot_dict);
203
4.81k
    if (code < 0)
204
0
        return code;
205
206
    /* TODO: graphicsbeginpage, textbeginpage
207
     * These should match what was done at the beginning of the page,
208
     * but current implementation has things kind of mixed around.
209
     * Need to figure out exactly what to do.  I'd like it to be one
210
     * function in both places.
211
     * see pdfi_page_render()
212
     */
213
    /* graphicsbeginpage() */
214
    /* textbeginpage() */
215
    /* code = gs_initgraphics(ctx->pgs); */
216
4.81k
    ctx->text.BlockDepth = 0;
217
    /* TODO: FIXME */
218
219
4.81k
    code = pdfi_annot_Rect(ctx, annot, &rect);
220
4.81k
    if (code < 0) goto exit;
221
222
4.80k
    code = pdfi_dict_knownget_type(ctx, Annot_dict, "BBox", PDF_ARRAY, (pdf_obj **)&BBox);
223
4.80k
    if (code < 0) goto exit;
224
4.80k
    code = pdfi_array_to_gs_rect(ctx, BBox, &bbox);
225
4.80k
    if (code < 0) goto exit;
226
227
4.80k
    if (bbox.q.x - bbox.p.x == 0.0 || bbox.q.y - bbox.p.y == 0.0) {
228
        /* If the Bounding Box has 0 width or height, then the scaling calculation below will
229
         * end up with a division by zero, causing the CTM to go infinite.
230
         * We handle the broken BBox elsewhere, so for now just discard it, preventing
231
         * us breaking the CTM.
232
         */
233
7
        pdfi_countdown(BBox);
234
7
        BBox = NULL;
235
7
    }
236
237
4.80k
    code = pdfi_dict_knownget_type(ctx, Annot_dict, "Matrix", PDF_ARRAY, (pdf_obj **)&Matrix);
238
4.80k
    if (code < 0) goto exit;
239
4.80k
    code = pdfi_array_to_gs_matrix(ctx, Matrix, &matrix);
240
4.80k
    if (code < 0) goto exit;
241
242
4.80k
    xscale = yscale = 1.0;
243
244
4.80k
    code = gs_translate(ctx->pgs, rect.p.x, rect.p.y);
245
4.80k
    if (code < 0) goto exit;
246
247
4.80k
    if (BBox != NULL) {
248
4.79k
        pdfi_bbox_transform(ctx, &bbox, &matrix);
249
250
        /* Calculate scale factor */
251
4.79k
        xscale = (rect.q.x - rect.p.x) / (bbox.q.x - bbox.p.x);
252
4.79k
        yscale = (rect.q.y - rect.p.y) / (bbox.q.y - bbox.p.y);
253
254
4.79k
        if (xscale * yscale <= 0) {
255
0
            code = pdfi_set_warning_stop(ctx, gs_note_error(gs_error_rangecheck), NULL, W_PDF_ZEROSCALE_ANNOT, "pdfi_annot_position_AP", "ANNOT: Ignoring annotation with scale factor of 0\n");
256
0
            goto exit;
257
0
        }
258
259
        /* Scale it */
260
4.79k
        code = gs_scale(ctx->pgs, xscale, yscale);
261
4.79k
        if (code < 0) goto exit;
262
263
        /* Compensate for non-zero origin of BBox */
264
4.79k
        code = gs_translate(ctx->pgs, -bbox.p.x, -bbox.p.y);
265
4.79k
        if (code < 0) goto exit;
266
4.79k
    }
267
268
4.81k
 exit:
269
4.81k
    pdfi_countdown(BBox);
270
4.81k
    pdfi_countdown(Matrix);
271
4.81k
    return code;
272
4.80k
}
273
274
/* See pdf_draw.ps/drawwidget (draws the AP for any type of thingy) */
275
static int pdfi_annot_draw_AP(pdf_context *ctx, pdf_dict *annot, pdf_obj *NormAP)
276
5.52k
{
277
5.52k
    int code = 0;
278
279
5.52k
    if (NormAP == NULL)
280
237
        return 0;
281
5.29k
    if (pdfi_type_of(NormAP) == PDF_NULL)
282
475
        return 0;
283
4.81k
    if (pdfi_type_of(NormAP) != PDF_STREAM)
284
0
        return pdfi_set_error_stop(ctx, gs_note_error(gs_error_typecheck), NULL, E_PDF_BAD_ANNOTATION, "pdfi_annot_draw_AP", "");
285
286
4.81k
    code = pdfi_op_q(ctx);
287
4.81k
    if (code < 0)
288
0
        return code;
289
290
4.81k
    code = pdfi_annot_position_AP(ctx, annot, (pdf_stream *)NormAP);
291
4.81k
    if (code < 0)
292
15
        goto exit;
293
294
    /* Render the annotation */
295
4.80k
    code = pdfi_do_image_or_form(ctx, NULL, ctx->page.CurrentPageDict, NormAP);
296
297
4.81k
exit:
298
4.81k
    (void)pdfi_op_Q(ctx);
299
4.81k
    return code;
300
4.80k
}
301
302
static int pdfi_annot_setcolor_key(pdf_context *ctx, pdf_dict *annot, const char *key,
303
                                   bool usedefault, bool *drawit)
304
5.40k
{
305
5.40k
    int code = 0;
306
5.40k
    pdf_array *C = NULL;
307
308
5.40k
    *drawit = true;
309
310
5.40k
    code = pdfi_dict_knownget_type(ctx, annot, key, PDF_ARRAY, (pdf_obj **)&C);
311
5.40k
    if (code < 0) goto exit;
312
313
5.40k
    if (code == 0) {
314
4.81k
        if (usedefault)
315
198
            code = pdfi_gs_setgray(ctx, 0);
316
4.61k
        else
317
4.61k
            *drawit = false;
318
4.81k
    } else {
319
593
        if (pdfi_array_size(C) == 0) {
320
2
            code = 0;
321
2
            *drawit = false;
322
591
        } else {
323
591
            code = pdfi_setcolor_from_array(ctx, C);
324
591
        }
325
593
    }
326
327
5.40k
 exit:
328
5.40k
    if (code < 0)
329
4
        *drawit = false;
330
5.40k
    pdfi_countdown(C);
331
5.40k
    return code;
332
5.40k
}
333
334
static int pdfi_annot_setinteriorcolor(pdf_context *ctx, pdf_dict *annot, bool usedefault, bool *drawit)
335
50
{
336
50
    return pdfi_annot_setcolor_key(ctx, annot, "IC", usedefault, drawit);
337
50
}
338
339
static int pdfi_annot_setcolor(pdf_context *ctx, pdf_dict *annot, bool usedefault, bool *drawit)
340
742
{
341
742
    return pdfi_annot_setcolor_key(ctx, annot, "C", usedefault, drawit);
342
742
}
343
344
/* Stroke border using current path */
345
static int pdfi_annot_strokeborderpath(pdf_context *ctx, pdf_dict *annot, double width, pdf_array *dash)
346
137
{
347
137
    int code = 0;
348
349
137
    if (width <= 0)
350
0
        return 0;
351
352
137
    code = pdfi_setdash_impl(ctx, dash, 0);
353
137
    if (code < 0) goto exit;
354
137
    code = gs_setlinewidth(ctx->pgs, width);
355
137
    if (code < 0) goto exit;
356
357
137
    code = gs_stroke(ctx->pgs);
358
359
137
 exit:
360
137
    return code;
361
137
}
362
363
/* Fill border path */
364
static int pdfi_annot_fillborderpath(pdf_context *ctx, pdf_dict *annot)
365
42
{
366
42
    int code;
367
42
    bool drawit;
368
369
42
    code = pdfi_gsave(ctx);
370
42
    if (code < 0) return code;
371
372
42
    code = pdfi_annot_opacity(ctx, annot);
373
42
    if (code < 0) goto exit;
374
42
    code = pdfi_annot_setinteriorcolor(ctx, annot, false, &drawit);
375
42
    if (code < 0) goto exit;
376
42
    if (drawit)
377
39
        code = gs_fill(ctx->pgs);
378
379
42
 exit:
380
42
    (void)pdfi_grestore(ctx);
381
42
    return code;
382
42
}
383
384
385
/* Draw a path from a rectangle */
386
static int pdfi_annot_rect_path(pdf_context *ctx, gs_rect *rect)
387
4
{
388
4
    int code;
389
390
4
    code = gs_moveto(ctx->pgs, rect->p.x, rect->p.y);
391
4
    if (code < 0) goto exit;
392
4
    code = gs_lineto(ctx->pgs, rect->q.x, rect->p.y);
393
4
    if (code < 0) goto exit;
394
4
    code = gs_lineto(ctx->pgs, rect->q.x, rect->q.y);
395
4
    if (code < 0) goto exit;
396
4
    code = gs_lineto(ctx->pgs, rect->p.x, rect->q.y);
397
4
    if (code < 0) goto exit;
398
4
    code = gs_closepath(ctx->pgs);
399
400
4
 exit:
401
4
    return code;
402
4
}
403
404
/* Make a rectangle path from Rect (see /re, /normal_re in pdf_ops.ps)
405
 * See also /Square
406
 * Not sure if I have to worry about the /inside_text_re here
407
 */
408
static int pdfi_annot_RectRD_path(pdf_context *ctx, pdf_dict *annot)
409
4
{
410
4
    gs_rect rect;
411
4
    int code;
412
413
4
    code = pdfi_annot_Rect(ctx, annot, &rect);
414
4
    if (code < 0) goto exit;
415
416
4
    code = pdfi_annot_applyRD(ctx, annot, &rect);
417
4
    if (code < 0) goto exit;
418
419
4
    code = pdfi_annot_rect_path(ctx, &rect);
420
421
4
 exit:
422
4
    return code;
423
4
}
424
425
/* Adjust rectangle by specified width */
426
static void pdfi_annot_rect_adjust(pdf_context *ctx, gs_rect *rect, double width)
427
127
{
428
127
    rect->p.x += width;
429
127
    rect->p.y += width;
430
127
    rect->q.x -= width;
431
127
    rect->q.y -= width;
432
127
}
433
434
/* Fill Rect with current color */
435
static int pdfi_annot_fillRect(pdf_context *ctx, pdf_dict *annot)
436
3
{
437
3
    int code;
438
3
    gs_rect rect;
439
440
3
    code = pdfi_gsave(ctx);
441
3
    if (code < 0) return code;
442
443
3
    code = pdfi_annot_Rect(ctx, annot, &rect);
444
3
    if (code < 0) goto exit;
445
446
3
    code = gs_rectclip(ctx->pgs, &rect, 1);
447
3
    if (code < 0) goto exit;
448
449
3
    code = pdfi_annot_applyRD(ctx, annot, &rect);
450
3
    if (code < 0) goto exit;
451
452
3
    code = gs_rectfill(ctx->pgs, &rect, 1);
453
3
    if (code < 0) goto exit;
454
455
3
 exit:
456
3
    (void)pdfi_grestore(ctx);
457
3
    return code;
458
3
}
459
460
/* Stroke border, drawing path from rect */
461
static int pdfi_annot_strokeborder(pdf_context *ctx, pdf_dict *annot, double width, pdf_array *dash)
462
330
{
463
330
    int code = 0;
464
330
    gs_rect rect;
465
466
330
    if (width <= 0)
467
203
        return 0;
468
469
127
    code = pdfi_gsave(ctx);
470
471
127
    code = pdfi_setdash_impl(ctx, dash, 0);
472
127
    if (code < 0) goto exit;
473
474
127
    code = gs_setlinewidth(ctx->pgs, width);
475
127
    if (code < 0) goto exit;
476
477
127
    code = pdfi_annot_Rect(ctx, annot, &rect);
478
127
    if (code < 0) goto exit;
479
480
127
    code = pdfi_annot_applyRD(ctx, annot, &rect);
481
127
    if (code < 0) goto exit;
482
483
484
    /* Stroke the rectangle */
485
    /* Adjust rectangle by the width */
486
127
    pdfi_annot_rect_adjust(ctx, &rect, width/2);
487
127
    code = gs_rectstroke(ctx->pgs, &rect, 1, NULL);
488
489
127
 exit:
490
127
    (void)pdfi_grestore(ctx);
491
127
    return code;
492
127
}
493
494
/* Draw border from a Border array
495
 * Note: Border can be null
496
 */
497
static int pdfi_annot_draw_Border(pdf_context *ctx, pdf_dict *annot, pdf_array *Border, bool usepath)
498
330
{
499
330
    pdf_array *dash = NULL;
500
330
    int code = 0;
501
330
    uint64_t size = 0;
502
330
    double width = 0;
503
504
330
    if (Border)
505
330
        size = pdfi_array_size(Border);
506
330
    if (Border &&  size < 3) {
507
0
        code = pdfi_set_warning_stop(ctx, gs_note_error(gs_error_rangecheck), NULL, W_PDF_ANNOT_ERROR, "pdfi_annot_draw_Border", "WARNING: Annotation Border array invalid");
508
0
        goto exit;
509
0
    }
510
330
    if (!Border) {
511
0
        code = pdfi_array_alloc(ctx, 0, &dash);
512
0
        if (code < 0) goto exit;
513
0
        pdfi_countup(dash);
514
0
        width = 1;
515
330
    } else {
516
330
        if (size > 3) {
517
0
            code = pdfi_array_get_type(ctx, Border, 3, PDF_ARRAY, (pdf_obj **)&dash);
518
0
            if (code < 0) {
519
0
                if ((code = pdfi_set_warning_stop(ctx, code, NULL, W_PDF_ANNOT_ERROR,
520
0
                                 "pdfi_annot_draw_Border", "WARNING: Annotation Border Dash array invalid")) < 0) {
521
0
                    goto exit;
522
0
                }
523
0
                code = pdfi_array_alloc(ctx, 0, &dash);
524
0
                if (code < 0) goto exit;
525
0
                pdfi_countup(dash);
526
0
            }
527
330
        } else {
528
330
            code = pdfi_array_alloc(ctx, 0, &dash);
529
330
            if (code < 0) goto exit;
530
330
            pdfi_countup(dash);
531
330
        }
532
330
        code = pdfi_array_get_number(ctx, Border, 2, &width);
533
330
        if (code < 0) goto exit;
534
330
    }
535
536
    /* At this point we have a dash array (which could be length 0) and a width */
537
330
    if (usepath)
538
12
        code = pdfi_annot_strokeborderpath(ctx, annot, width, dash);
539
318
    else
540
318
        code = pdfi_annot_strokeborder(ctx, annot, width, dash);
541
542
330
 exit:
543
330
    pdfi_countdown(dash);
544
330
    return code;
545
330
}
546
547
/* Draw border from a BS dict */
548
static int pdfi_annot_draw_BS(pdf_context *ctx, pdf_dict *annot, pdf_dict *BS, bool usepath)
549
137
{
550
137
    double W;
551
137
    int code = 0;
552
137
    pdf_name *S = NULL;
553
137
    pdf_array *dash = NULL;
554
555
    /* Get the width */
556
137
    code = pdfi_dict_knownget_number(ctx, BS, "W", &W);
557
137
    if (code < 0) goto exit;
558
137
    if (code == 0)
559
0
        W = 1; /* Default */
560
561
    /* TODO: Some junk about scaling to UserUnit */
562
    /* ... */
563
564
    /* Lookup border style */
565
137
    code = pdfi_dict_knownget_type(ctx, BS, "S", PDF_NAME, (pdf_obj **)&S);
566
137
    if (code < 0) goto exit;
567
568
137
    if (code > 0 && pdfi_name_is(S, "D")) {
569
        /* Handle Dash array */
570
0
        code = pdfi_dict_knownget_type(ctx, BS, "D", PDF_ARRAY, (pdf_obj **)&dash);
571
0
        if (code < 0) goto exit;
572
573
        /* If there is no Dash array, then create one containing a 3 (the default) */
574
0
        if (code == 0) {
575
0
            code = pdfi_array_alloc(ctx, 1, &dash);
576
0
            if (code < 0) goto exit;
577
0
            pdfi_countup(dash);
578
579
0
            code = pdfi_array_put_int(ctx, dash, 0, 3);
580
0
            if (code < 0) goto exit;
581
0
        }
582
137
    } else {
583
        /* Empty array */
584
137
        code = pdfi_array_alloc(ctx, 0, &dash);
585
137
        if (code < 0) goto exit;
586
137
        pdfi_countup(dash);
587
137
    }
588
589
    /* At this point we have a dash array (which could be length 0) and a width */
590
137
    if (usepath)
591
125
        code = pdfi_annot_strokeborderpath(ctx, annot, W, dash);
592
12
    else
593
12
        code = pdfi_annot_strokeborder(ctx, annot, W, dash);
594
595
137
 exit:
596
137
    pdfi_countdown(S);
597
137
    pdfi_countdown(dash);
598
137
    return code;
599
137
}
600
601
/* Draw the border
602
 * This spec seems to be scattered around.  There might be:
603
 *  /Border -- an array of 3-4 elements, where the optional 4th element is a "dash array"
604
 *   (default value: [0 0 1])
605
 *  /BS -- a Border Style dictionary
606
 *  Spec says /Border would be ignored in favor of /BS
607
 *
608
 * usepath -- indicates whether to stroke existing path, or use Rect to draw a path first
609
 */
610
static int pdfi_annot_draw_border(pdf_context *ctx, pdf_dict *annot, bool usepath)
611
467
{
612
467
    int code, code1;
613
467
    pdf_dict *BS = NULL;
614
467
    pdf_array *Border = NULL;
615
616
467
    code = pdfi_dict_knownget_type(ctx, annot, "BS", PDF_DICT, (pdf_obj **)&BS);
617
467
    if (code < 0) goto exit;
618
467
    code = pdfi_dict_knownget_type(ctx, annot, "Border", PDF_ARRAY, (pdf_obj **)&Border);
619
467
    if (code < 0) goto exit;
620
621
467
    code = pdfi_gsave(ctx);
622
467
    if (code < 0) goto exit;
623
624
467
    if (BS) {
625
137
        code = pdfi_annot_draw_BS(ctx, annot, BS, usepath);
626
330
    } else {
627
        /* Note: Border can be null */
628
330
        code = pdfi_annot_draw_Border(ctx, annot, Border, usepath);
629
330
    }
630
467
    code1 = pdfi_grestore(ctx);
631
467
    if (code == 0) code = code1;
632
633
467
 exit:
634
467
    pdfi_countdown(BS);
635
467
    pdfi_countdown(Border);
636
467
    return code;
637
467
}
638
639
/*********** BEGIN Font/Text ***************/
640
641
/* Set font */
642
static int
643
pdfi_annot_set_font(pdf_context *ctx, const char *font, double size)
644
29
{
645
29
    int code = 0;
646
647
29
    if (font != NULL) {
648
29
        code = pdfi_font_set_internal_string(ctx, font, size);
649
29
        if (code < 0) goto exit;
650
29
    }
651
652
29
 exit:
653
29
    return code;
654
29
}
655
656
/* Get value from a field, or its Parents (recursive)
657
 * If no Parent, will also check ctx->AcroForm
658
 * Returns <0 on error, 0 if not found, >0 if found
659
 */
660
static int pdfi_form_get_inheritable(pdf_context *ctx, pdf_dict *field, const char *Key,
661
                                     pdf_obj_type type, pdf_obj **o)
662
9.94k
{
663
9.94k
    int code = 0;
664
9.94k
    pdf_dict *Parent = NULL;
665
9.94k
    bool known = false;
666
667
    /* Check this field */
668
9.94k
    code = pdfi_dict_knownget_type(ctx, field, Key, type, o);
669
9.94k
    if (code != 0) goto exit1;
670
671
3.16k
    code = pdfi_loop_detector_mark(ctx);
672
3.16k
    if (code < 0)
673
0
        goto exit;
674
675
    /* Check for Parent. Do not store the dereferenced Parent back to the dictionary
676
     * as this can cause circular references.
677
     */
678
3.16k
    code = pdfi_dict_known(ctx, field, "Parent", &known);
679
3.16k
    if (code >= 0 && known == true)
680
2.24k
    {
681
2.24k
        code = pdfi_dict_get_no_store_R(ctx, field, "Parent", (pdf_obj **)&Parent);
682
2.24k
        if (code < 0)
683
585
            goto exit;
684
685
1.65k
        if (pdfi_type_of(Parent) != PDF_DICT) {
686
0
            if (pdfi_type_of(Parent) == PDF_INDIRECT) {
687
0
                pdf_indirect_ref *o = (pdf_indirect_ref *)Parent;
688
689
0
                code = pdfi_dereference(ctx, o->ref_object_num, o->ref_generation_num, (pdf_obj **)&Parent);
690
0
                pdfi_countdown(o);
691
0
                goto exit;
692
0
            } else {
693
0
                code = gs_note_error(gs_error_typecheck);
694
0
                goto exit;
695
0
            }
696
0
        }
697
1.65k
        code = pdfi_form_get_inheritable(ctx, Parent, Key, type, o);
698
1.65k
        if (code <= 0) {
699
1.65k
            if (ctx->AcroForm)
700
1.65k
                code = pdfi_dict_knownget_type(ctx, ctx->AcroForm, Key, type, o);
701
1.65k
        }
702
1.65k
    } else {
703
        /* No Parent, so check AcroForm, if any */
704
924
        if (ctx->AcroForm)
705
909
            code = pdfi_dict_knownget_type(ctx, ctx->AcroForm, Key, type, o);
706
924
    }
707
708
3.16k
exit:
709
3.16k
    (void)pdfi_loop_detector_cleartomark(ctx);
710
711
9.94k
exit1:
712
9.94k
    pdfi_countdown(Parent);
713
9.94k
    return code;
714
3.16k
}
715
716
/* Get int value from a field, or its Parents (recursive)
717
 * Returns <0 on error, 0 if not found, >0 if found
718
 */
719
static int pdfi_form_get_inheritable_int(pdf_context *ctx, pdf_dict *field, const char *Key,
720
                                         int64_t *val)
721
6.05k
{
722
6.05k
    int code = 0;
723
6.05k
    pdf_num *num = NULL;
724
725
6.05k
    *val = 0;
726
6.05k
    code = pdfi_form_get_inheritable(ctx, field, Key, PDF_INT, (pdf_obj **)&num);
727
6.05k
    if (code < 0) goto exit;
728
729
5.50k
    if (code > 0)
730
4.55k
        *val = num->value.i;
731
732
6.05k
 exit:
733
6.05k
    pdfi_countdown(num);
734
6.05k
    return code;
735
5.50k
}
736
737
/* Scan DA to see if font size is 0, if so make a new DA with font size scaled by gs_rect */
738
static int pdfi_form_modDA(pdf_context *ctx, pdf_string *DA, pdf_string **mod_DA, gs_rect *rect)
739
2.23k
{
740
2.23k
    char *token, *prev_token;
741
2.23k
    char *parse_str = NULL;
742
2.23k
    bool did_mod = false;
743
2.23k
    int code = 0;
744
2.23k
    double size;
745
2.23k
    char size_str[20];
746
2.23k
    char *last;
747
2.23k
    pdf_string *newDA = NULL;
748
749
    /* Make a copy of the string because we are going to destructively parse it */
750
2.23k
    parse_str = (char *)gs_alloc_bytes(ctx->memory, DA->length+1, "pdfi_annot_display_text(strbuf)");
751
2.23k
    if (parse_str == NULL) {
752
0
        code = gs_note_error(gs_error_VMerror);
753
0
        goto exit;
754
0
    }
755
2.23k
    memcpy(parse_str, DA->data, DA->length);
756
2.23k
    parse_str[DA->length] = 0; /* Null terminate */
757
758
    /* find the 'Tf' token, if any */
759
2.23k
    token = gs_strtok(parse_str, " ", &last);
760
2.23k
    prev_token = NULL;
761
6.69k
    while (token != NULL) {
762
6.69k
        if (!strcmp(token, "Tf"))
763
2.22k
            break;
764
4.46k
        prev_token = token;
765
4.46k
        token = gs_strtok(NULL, " ", &last);
766
4.46k
    }
767
768
    /* See if we found it */
769
2.23k
    if (!token)
770
2
        goto exit;
771
772
    /* See if there was a prev_token and it was "0" */
773
2.22k
    if (!(prev_token && !strcmp(prev_token, "0")))
774
2.22k
        goto exit;
775
776
    /* Case with '<font> 0 Tf', need to calculate correct size */
777
1
    size = (rect->q.y - rect->p.y) * .75; /* empirical from gs code make_tx_da */
778
779
1
    snprintf(size_str, sizeof(size_str), "%g ", size);
780
781
    /* Create a new DA and reassemble the old DA into it */
782
1
    code = pdfi_object_alloc(ctx, PDF_STRING, DA->length+strlen(size_str)+1, (pdf_obj **)&newDA);
783
1
    if (code < 0)
784
0
        goto exit;
785
1
    pdfi_countup(newDA);
786
787
1
    strncpy((char *)newDA->data, (char *)DA->data, prev_token-parse_str);
788
1
    strncpy((char *)newDA->data + (prev_token-parse_str), size_str, strlen(size_str)+1);
789
1
    strncpy((char *)newDA->data + strlen((char *)newDA->data), (char *)DA->data + (token - parse_str),
790
1
            DA->length-(token-parse_str));
791
1
    newDA->length = strlen((char *)newDA->data);
792
1
    did_mod = true;
793
794
2.23k
 exit:
795
    /* If didn't need to mod it, just copy the original and get a ref */
796
2.23k
    if (did_mod) {
797
1
        *mod_DA = newDA;
798
2.22k
    } else {
799
2.22k
        *mod_DA = DA;
800
2.22k
        pdfi_countup(DA);
801
2.22k
    }
802
803
2.23k
    if (parse_str)
804
2.23k
        gs_free_object(ctx->memory, parse_str, "pdfi_form_modDA(parse_str)");
805
806
2.23k
    return code;
807
1
}
808
809
/* Process the /DA string by either running it through the interpreter
810
 *  or providing a bunch of defaults
811
 *
812
 * According to the spec, if there is a "<fontname> <size> Tf" in the /DA,
813
 * and the <size> = 0, we autosize it using the annotation's rect.  (see PDF1.7, pages 678-679)
814
 *
815
 * rect -- the annotation's rect, for auto-sizing the font
816
 * is_form -- is this a form, so check inheritable?
817
 */
818
static int pdfi_annot_process_DA(pdf_context *ctx, pdf_dict *page_dict, pdf_dict *annot, gs_rect *rect,
819
                                 bool is_form)
820
2.23k
{
821
2.23k
    int code = 0;
822
2.23k
    pdf_string *DA = NULL;
823
2.23k
    pdf_string *mod_DA = NULL;
824
2.23k
    pdf_dict *resource_dict = annot;  /* dict to use for resources, alias no need to refcnt */
825
2.23k
    pdf_dict *DR = NULL;
826
2.23k
    bool known;
827
828
    /* TODO -- see gs code make_tx_da, need to handle case where we need to set a UTF-16 font */
829
830
2.23k
    if (!page_dict)
831
5
        page_dict = ctx->page.CurrentPageDict;
832
833
2.23k
    if (is_form) {
834
2.23k
        code = pdfi_dict_known(ctx, annot, "DR", &known);
835
2.23k
        if (code < 0) goto exit;
836
        /* If there is no "DR" and no Parent in the annotation, have it use the AcroForm instead
837
         * This is a hack.  May need to use fancier inheritance approach.
838
         */
839
2.23k
        if (!known) {
840
2.23k
            code = pdfi_dict_known(ctx, annot, "Parent", &known);
841
2.23k
            if (code < 0) goto exit;
842
2.23k
            if (!known && ctx->AcroForm != NULL)
843
56
                resource_dict = ctx->AcroForm;
844
2.23k
        }
845
2.23k
        code = pdfi_form_get_inheritable(ctx, annot, "DA", PDF_STRING, (pdf_obj **)&DA);
846
2.23k
    } else {
847
5
        code = pdfi_dict_knownget_type(ctx, annot, "DA", PDF_STRING, (pdf_obj **)&DA);
848
5
    }
849
2.23k
    if (code < 0) goto exit;
850
2.23k
    if (code > 0) {
851
2.23k
        if (is_form) {
852
2.23k
            code = pdfi_form_modDA(ctx, DA, &mod_DA, rect);
853
2.23k
            if (code < 0) goto exit;
854
2.23k
        } else {
855
5
            mod_DA = DA;
856
5
            pdfi_countup(mod_DA);
857
5
        }
858
859
2.23k
        code = pdfi_interpret_inner_content_string(ctx, mod_DA, resource_dict,
860
2.23k
                                                   page_dict, false, "DA");
861
2.23k
        if (code < 0) goto exit;
862
        /* If no font got set, set one */
863
2.23k
        if (pdfi_get_current_pdf_font(ctx) == NULL) {
864
7
            code = pdfi_annot_set_font(ctx, "Helvetica", 12.0);
865
7
            if (code < 0) goto exit;
866
7
        }
867
2.23k
    } else {
868
0
        code = pdfi_gs_setgray(ctx, 0);
869
0
        if (code < 0) goto exit;
870
0
        code = pdfi_annot_set_font(ctx, "Helvetica", 12.0);
871
0
        if (code < 0) goto exit;
872
0
    }
873
874
2.23k
 exit:
875
2.23k
    pdfi_countdown(DR);
876
2.23k
    pdfi_countdown(DA);
877
2.23k
    pdfi_countdown(mod_DA);
878
2.23k
    return code;
879
2.23k
}
880
881
/* Put a string into buffer as a hexstring and return ptr to end of it
882
 * Skip over any BOM that is present.
883
 */
884
static char *pdfi_get_hexstring(pdf_context *ctx, char *outbuf, byte *inbuf, int len)
885
1.35k
{
886
1.35k
    int i;
887
1.35k
    char *ptr;
888
889
1.35k
    i = 0;
890
    /* skip over BOM if it's there */
891
1.35k
    if (IS_UTF16(inbuf)) {
892
0
        i += 2; /* UTF-16 */
893
1.35k
    } else if (IS_UTF16(inbuf)) {
894
0
        i += 3; /* UTF-8 */
895
0
    }
896
897
1.35k
    ptr = outbuf;
898
1.35k
    *ptr++ = '<';
899
2.82k
    for ( ; i<len; i++) {
900
1.47k
        snprintf(ptr, 3, "%02X", *(inbuf+i));
901
1.47k
        ptr += 2;
902
1.47k
    }
903
904
1.35k
    *ptr++ = '>';
905
1.35k
    *ptr = 0;
906
907
1.35k
    return ptr;
908
1.35k
}
909
910
/* Display text at current position (inside BT/ET) */
911
static int
912
pdfi_annot_display_nexttext(pdf_context *ctx, pdf_dict *annot, pdf_string *text)
913
1.22k
{
914
1.22k
    char *strbuf = NULL;
915
1.22k
    size_t buflen = 50 + text->length*2; /* 50 to account for formatting, plus the text itself */
916
1.22k
    int code = 0;
917
1.22k
    char *ptr;
918
919
1.22k
    strbuf = (char *)gs_alloc_bytes(ctx->memory, buflen, "pdfi_annot_display_text(strbuf)");
920
1.22k
    if (strbuf == NULL)
921
0
        return_error(gs_error_VMerror);
922
1.22k
    ptr = pdfi_get_hexstring(ctx, strbuf, text->data, text->length);
923
1.22k
    strncpy(ptr, " Tj", buflen-strlen(strbuf));
924
925
1.22k
    code = pdfi_interpret_inner_content_c_string(ctx, strbuf, annot,
926
1.22k
                                               ctx->page.CurrentPageDict, false, "Annot text Tj");
927
1.22k
    if (code < 0) goto exit;
928
929
1.22k
 exit:
930
1.22k
    if (strbuf)
931
1.22k
        gs_free_object(ctx->memory, strbuf, "pdfi_annot_display_text(strbuf)");
932
1.22k
    return code;
933
1.22k
}
934
935
/* Display text with positioning (inside BT/ET) */
936
static int
937
pdfi_annot_display_text(pdf_context *ctx, pdf_dict *annot, double x, double y, pdf_string *text)
938
130
{
939
130
    char *strbuf = NULL;
940
130
    size_t buflen = 50 + text->length*2; /* 50 to account for formatting, plus the text itself */
941
130
    int code = 0;
942
130
    char *ptr;
943
944
130
    strbuf = (char *)gs_alloc_bytes(ctx->memory, buflen, "pdfi_annot_display_text(strbuf)");
945
130
    if (strbuf == NULL)
946
0
        return_error(gs_error_VMerror);
947
130
    snprintf(strbuf, buflen, "%g %g Td ", x, y);
948
130
    ptr = strbuf + strlen(strbuf);
949
130
    ptr = pdfi_get_hexstring(ctx, ptr, text->data, text->length);
950
130
    strncpy(ptr, " Tj", buflen-strlen(strbuf));
951
952
130
    code = pdfi_interpret_inner_content_c_string(ctx, strbuf, annot,
953
130
                                               ctx->page.CurrentPageDict, false, "Annot text Tj");
954
130
    if (code < 0) goto exit;
955
956
130
 exit:
957
130
    if (strbuf)
958
130
        gs_free_object(ctx->memory, strbuf, "pdfi_annot_display_text(strbuf)");
959
130
    return code;
960
130
}
961
962
/* Get Text height for current font (assumes font is already set, inside BT/ET)
963
 * TODO: See /Tform implementation that uses FontBBox and ScaleMatrix instead
964
 * Maybe use this technique as a backup if those entries aren't available?
965
 */
966
static int
967
pdfi_annot_get_text_height(pdf_context *ctx, double *height)
968
31
{
969
31
    int code;
970
31
    pdf_string *temp_string = NULL;
971
31
    gs_rect bbox;
972
31
    gs_point awidth;
973
974
31
    if (ctx->pgs->PDFfontsize == 0) {
975
0
        *height = 0;
976
0
        return 0;
977
0
    }
978
979
31
    code = pdfi_obj_charstr_to_string(ctx, "Hy", &temp_string);
980
31
    if (code < 0)
981
0
        goto exit;
982
983
    /* Find the bbox of the string "Hy" */
984
31
    code = pdfi_string_bbox(ctx, temp_string, &bbox, &awidth, false);
985
31
    if (code < 0)
986
0
        goto exit;
987
988
31
    *height = bbox.q.y - bbox.p.y;
989
990
31
 exit:
991
31
    pdfi_countdown(temp_string);
992
31
    return code;
993
31
}
994
995
/* Display a simple text string (outside BT/ET) */
996
static int
997
pdfi_annot_display_simple_text(pdf_context *ctx, pdf_dict *annot, double x, double y, pdf_string *text)
998
15
{
999
15
    int code = 0;
1000
15
    int code1 = 0;
1001
1002
15
    code = pdfi_BT(ctx);
1003
15
    if (code < 0)
1004
0
        return code;
1005
1006
15
    code = pdfi_annot_display_text(ctx, annot, x, y, text);
1007
15
    code1 = pdfi_ET(ctx);
1008
15
    if (code == 0) code = code1;
1009
1010
15
    return code;
1011
15
}
1012
1013
/* Display a centered text string (outside BT/ET) */
1014
static int
1015
pdfi_annot_display_centered_text(pdf_context *ctx, pdf_dict *annot, gs_rect *rect, pdf_string *text)
1016
11
{
1017
11
    int code = 0;
1018
11
    int code1 = 0;
1019
11
    gs_rect bbox;
1020
11
    gs_point awidth;
1021
11
    double x, y;
1022
1023
11
    code = pdfi_BT(ctx);
1024
11
    if (code < 0)
1025
0
        return code;
1026
1027
    /* Get width of the string */
1028
11
    code = pdfi_string_bbox(ctx, text, &bbox, &awidth, false);
1029
11
    if (code < 0) goto exit;
1030
1031
    /* Center the title in the box */
1032
11
    x = rect->p.x + ((rect->q.x - rect->p.x) - awidth.x) / 2;
1033
11
    y = rect->q.y - 11;
1034
1035
11
    code = pdfi_annot_display_text(ctx, annot, x, y, text);
1036
11
    if (code < 0) goto exit;
1037
1038
11
 exit:
1039
11
    code1 = pdfi_ET(ctx);
1040
11
    if (code == 0) code = code1;
1041
11
    return code;
1042
11
}
1043
1044
/* Display a string formatted to fit in rect (outside BT/ET)
1045
 *
1046
 * TODO: I am sharing code between the FreeText and field/Tx implementation.
1047
 * The gs code has completely different implementations for these.
1048
 * I am not sure if there are some different assumptions about font encodings?
1049
 * The Tx field can apparently be UTF8 or UTF16, whereas the FreeText implementation just assumed
1050
 * it was ASCII.
1051
 * If you see some weird behavior with character mappings or line breaks, then
1052
 * this might be something to revisit.
1053
 */
1054
static int
1055
pdfi_annot_display_formatted_text(pdf_context *ctx, pdf_dict *annot,
1056
                                  gs_rect *rect, pdf_string *text, bool is_UTF16)
1057
16
{
1058
16
    double x;
1059
16
    double lineheight = 0;
1060
16
    double y_start;
1061
16
    double x_start, x_max;
1062
16
    gs_rect bbox;
1063
16
    gs_point awidth; /* Advance width */
1064
16
    int code = 0;
1065
16
    int code1 = 0;
1066
16
    pdf_string *temp_string = NULL;
1067
16
    int i;
1068
16
    byte ch;
1069
16
    bool firstchar = true;
1070
16
    bool linestart = true;
1071
16
    int charlen;
1072
1073
16
    if (is_UTF16)
1074
0
        charlen = 2;
1075
16
    else
1076
16
        charlen = 1;
1077
1078
16
    if (ctx->pgs->PDFfontsize == 0)
1079
0
        return 0;
1080
1081
16
    code = pdfi_BT(ctx);
1082
16
    if (code < 0)
1083
0
        return code;
1084
1085
    /* Allocate a temp string to use, length 1 char */
1086
16
    code = pdfi_object_alloc(ctx, PDF_STRING, charlen, (pdf_obj **)&temp_string);
1087
16
    if (code < 0) goto exit;
1088
16
    pdfi_countup(temp_string);
1089
1090
16
    code = pdfi_annot_get_text_height(ctx, &lineheight);
1091
16
    if (code < 0) goto exit;
1092
1093
16
    y_start = rect->q.y - lineheight;
1094
16
    x_start = rect->p.x;
1095
16
    x_max = rect->q.x;
1096
16
    x = x_start;
1097
1098
1.35k
    for (i=0; i<text->length; i+=charlen) {
1099
1.34k
        int j;
1100
1101
1.34k
        if (linestart) {
1102
27
            x = x_start;
1103
27
        }
1104
1105
2.68k
        for (j = 0; j < charlen; j++) {
1106
1.34k
            ch = text->data[i+j];
1107
1.34k
            temp_string->data[j] = ch;
1108
1.34k
        }
1109
1110
        /* If EOL character encountered, move down to next line */
1111
1.34k
        if (charlen == 1) { /* Can only check this for ASCII font */
1112
1.34k
            if (ch == '\r' || ch == '\n') {
1113
11
                if (linestart == true) {
1114
0
                    pdf_string dummy;
1115
1116
0
                    dummy.length = 0;
1117
0
                    code = pdfi_annot_display_text(ctx, annot, 0, -lineheight, &dummy);
1118
0
                    if (code < 0) goto exit;
1119
0
                }
1120
11
                linestart = true;
1121
11
                continue;
1122
11
            }
1123
1.34k
        }
1124
1125
        /* get size of the character */
1126
1.33k
        code = pdfi_string_bbox(ctx, temp_string, &bbox, &awidth, false);
1127
1.33k
        if (code < 0) goto exit;
1128
1129
1.33k
        if (!linestart && ((x + awidth.x) > x_max)) {
1130
77
            x = x_start;
1131
77
            linestart = true;
1132
77
        }
1133
1134
        /* display the character */
1135
1.33k
        if (firstchar) {
1136
16
            code = pdfi_annot_display_text(ctx, annot, x, y_start, temp_string);
1137
16
            firstchar = false;
1138
1.31k
        } else {
1139
1.31k
            if (linestart)
1140
88
                code = pdfi_annot_display_text(ctx, annot, 0, -lineheight, temp_string);
1141
1.22k
            else
1142
1.22k
                code = pdfi_annot_display_nexttext(ctx, annot, temp_string);
1143
1.31k
        }
1144
1.33k
        if (code < 0) goto exit;
1145
1.33k
        x += awidth.x;
1146
1.33k
        linestart = false;
1147
1.33k
    }
1148
1149
16
 exit:
1150
16
    code1 = pdfi_ET(ctx);
1151
16
    if (code == 0) code = code1;
1152
16
    pdfi_countdown(temp_string);
1153
16
    return code;
1154
16
}
1155
1156
/*********** END Font/Text ***************/
1157
1158
1159
/*********** BEGIN /LE ***************/
1160
1161
/* Draw Butt LE */
1162
static int pdfi_annot_draw_LE_Butt(pdf_context *ctx, pdf_dict *annot)
1163
0
{
1164
0
    int code;
1165
0
    double width;
1166
0
    double seglength;
1167
1168
0
    code = pdfi_annot_get_BS_width(ctx, annot, &width);
1169
0
    if (code < 0) goto exit;
1170
1171
0
    seglength = 3*width;
1172
0
    code = gs_moveto(ctx->pgs, 0, -seglength);
1173
0
    if (code < 0) goto exit;
1174
0
    code = gs_lineto(ctx->pgs, 0, seglength);
1175
0
    if (code < 0) goto exit;
1176
0
    code = pdfi_annot_draw_border(ctx, annot, true);
1177
0
    if (code < 0) goto exit;
1178
1179
0
 exit:
1180
0
    return code;
1181
0
}
1182
1183
/* Draw Circle LE */
1184
static int pdfi_annot_draw_LE_Circle(pdf_context *ctx, pdf_dict *annot)
1185
0
{
1186
0
    double radius;
1187
0
    int code;
1188
0
    double width;
1189
1190
0
    code = pdfi_annot_get_BS_width(ctx, annot, &width);
1191
0
    if (code < 0) goto exit;
1192
1193
0
    code = pdfi_gsave(ctx);
1194
0
    if (code < 0) goto exit;
1195
1196
0
    radius = width * 2.5;
1197
0
    code = gs_moveto(ctx->pgs, radius, 0);
1198
0
    if (code < 0) goto exit_grestore;
1199
0
    code = gs_arc(ctx->pgs, 0, 0, radius, 0, 360);
1200
0
    if (code < 0) goto exit_grestore;
1201
0
    code = pdfi_annot_opacity(ctx, annot);
1202
0
    if (code < 0) goto exit_grestore;
1203
0
    code = pdfi_annot_fillborderpath(ctx, annot);
1204
0
    if (code < 0) goto exit_grestore;
1205
1206
0
    code = pdfi_grestore(ctx);
1207
0
    if (code < 0) goto exit;
1208
1209
0
    radius = width * 3;
1210
0
    code = gs_moveto(ctx->pgs, radius, 0);
1211
0
    if (code < 0) goto exit;
1212
0
    code = gs_arc(ctx->pgs, 0, 0, radius, 0, 360);
1213
0
    if (code < 0) goto exit;
1214
0
    code = pdfi_annot_draw_border(ctx, annot, true);
1215
0
    goto exit;
1216
1217
0
 exit_grestore:
1218
0
    (void)pdfi_grestore(ctx);
1219
0
 exit:
1220
0
    return code;
1221
0
}
1222
1223
/* Draw Diamond LE */
1224
static int pdfi_annot_draw_LE_Diamond(pdf_context *ctx, pdf_dict *annot)
1225
0
{
1226
0
    int code;
1227
0
    double width;
1228
0
    double seglength;
1229
1230
0
    code = pdfi_annot_get_BS_width(ctx, annot, &width);
1231
0
    if (code < 0) goto exit;
1232
1233
0
    code = pdfi_gsave(ctx);
1234
0
    if (code < 0) goto exit;
1235
1236
0
    seglength = width * 2.5;
1237
0
    code = gs_moveto(ctx->pgs, 0, -seglength);
1238
0
    if (code < 0) goto exit_grestore;
1239
0
    code = gs_lineto(ctx->pgs, -seglength, 0);
1240
0
    if (code < 0) goto exit_grestore;
1241
0
    code = gs_lineto(ctx->pgs, 0, seglength);
1242
0
    if (code < 0) goto exit_grestore;
1243
0
    code = gs_lineto(ctx->pgs, seglength, 0);
1244
0
    if (code < 0) goto exit_grestore;
1245
0
    code = gs_closepath(ctx->pgs);
1246
0
    if (code < 0) goto exit_grestore;
1247
0
    code = pdfi_annot_opacity(ctx, annot);
1248
0
    if (code < 0) goto exit_grestore;
1249
0
    code = pdfi_annot_fillborderpath(ctx, annot);
1250
0
    if (code < 0) goto exit_grestore;
1251
1252
0
    code = pdfi_grestore(ctx);
1253
0
    if (code < 0) goto exit;
1254
1255
0
    seglength = width * 3;
1256
0
    code = gs_moveto(ctx->pgs, 0, -seglength);
1257
0
    if (code < 0) goto exit;
1258
0
    code = gs_lineto(ctx->pgs, -seglength, 0);
1259
0
    if (code < 0) goto exit;
1260
0
    code = gs_lineto(ctx->pgs, 0, seglength);
1261
0
    if (code < 0) goto exit;
1262
0
    code = gs_lineto(ctx->pgs, seglength, 0);
1263
0
    if (code < 0) goto exit;
1264
0
    code = gs_closepath(ctx->pgs);
1265
0
    if (code < 0) goto exit;
1266
0
    code = pdfi_annot_draw_border(ctx, annot, true);
1267
0
    goto exit;
1268
1269
0
 exit_grestore:
1270
0
    (void)pdfi_grestore(ctx);
1271
0
 exit:
1272
0
    return code;
1273
0
}
1274
1275
/* Draw Square LE */
1276
static int pdfi_annot_draw_LE_Square(pdf_context *ctx, pdf_dict *annot)
1277
0
{
1278
0
    int code;
1279
0
    double width;
1280
0
    double seglength;
1281
1282
0
    code = pdfi_annot_get_BS_width(ctx, annot, &width);
1283
0
    if (code < 0) goto exit;
1284
1285
0
    code = pdfi_gsave(ctx);
1286
0
    if (code < 0) goto exit;
1287
1288
0
    seglength = width * 2.5;
1289
0
    code = gs_moveto(ctx->pgs, -seglength, -seglength);
1290
0
    if (code < 0) goto exit_grestore;
1291
0
    code = gs_lineto(ctx->pgs, -seglength, seglength);
1292
0
    if (code < 0) goto exit_grestore;
1293
0
    code = gs_lineto(ctx->pgs, seglength, seglength);
1294
0
    if (code < 0) goto exit_grestore;
1295
0
    code = gs_lineto(ctx->pgs, seglength, -seglength);
1296
0
    if (code < 0) goto exit_grestore;
1297
0
    code = gs_closepath(ctx->pgs);
1298
0
    if (code < 0) goto exit_grestore;
1299
0
    code = pdfi_annot_opacity(ctx, annot);
1300
0
    if (code < 0) goto exit_grestore;
1301
0
    code = pdfi_annot_fillborderpath(ctx, annot);
1302
0
    if (code < 0) goto exit_grestore;
1303
1304
0
    code = pdfi_grestore(ctx);
1305
0
    if (code < 0) goto exit;
1306
1307
0
    seglength = width * 3;
1308
0
    code = gs_moveto(ctx->pgs, -seglength, -seglength);
1309
0
    if (code < 0) goto exit;
1310
0
    code = gs_lineto(ctx->pgs, -seglength, seglength);
1311
0
    if (code < 0) goto exit;
1312
0
    code = gs_lineto(ctx->pgs, seglength, seglength);
1313
0
    if (code < 0) goto exit;
1314
0
    code = gs_lineto(ctx->pgs, seglength, -seglength);
1315
0
    if (code < 0) goto exit;
1316
0
    code = gs_closepath(ctx->pgs);
1317
0
    if (code < 0) goto exit;
1318
0
    code = pdfi_annot_draw_border(ctx, annot, true);
1319
0
    goto exit;
1320
1321
0
 exit_grestore:
1322
0
    (void)pdfi_grestore(ctx);
1323
0
 exit:
1324
0
    return code;
1325
0
}
1326
1327
/* Draw Slash LE */
1328
static int pdfi_annot_draw_LE_Slash(pdf_context *ctx, pdf_dict *annot)
1329
0
{
1330
0
    int code;
1331
0
    double width;
1332
0
    double seglength;
1333
1334
0
    code = pdfi_annot_get_BS_width(ctx, annot, &width);
1335
0
    if (code < 0) goto exit;
1336
1337
0
    seglength = 3*width;
1338
0
    code = gs_rotate(ctx->pgs, 330);
1339
0
    if (code < 0) goto exit;
1340
0
    code = gs_moveto(ctx->pgs, 0, -seglength);
1341
0
    if (code < 0) goto exit;
1342
0
    code = gs_lineto(ctx->pgs, 0, seglength);
1343
0
    if (code < 0) goto exit;
1344
0
    code = pdfi_annot_draw_border(ctx, annot, true);
1345
0
    if (code < 0) goto exit;
1346
1347
0
 exit:
1348
0
    return code;
1349
0
}
1350
1351
/* Draw ClosedArrow LE */
1352
static int pdfi_annot_draw_LE_ClosedArrow(pdf_context *ctx, pdf_dict *annot)
1353
30
{
1354
30
    int code;
1355
30
    double width;
1356
30
    double seglen;
1357
1358
30
    code = pdfi_annot_get_BS_width(ctx, annot, &width);
1359
30
    if (code < 0) goto exit;
1360
1361
30
    code = pdfi_gsave(ctx);
1362
30
    if (code < 0) goto exit_grestore;
1363
1364
30
    code = gs_setlinejoin(ctx->pgs, 0);
1365
30
    if (code < 0) goto exit_grestore;
1366
30
    code = gs_moveto(ctx->pgs, -width*6, -width*4);
1367
30
    if (code < 0) goto exit_grestore;
1368
30
    code = gs_lineto(ctx->pgs, -width/1.2, 0);
1369
30
    if (code < 0) goto exit_grestore;
1370
30
    code = gs_lineto(ctx->pgs, -width*6, width*4);
1371
30
    if (code < 0) goto exit_grestore;
1372
30
    code = gs_closepath(ctx->pgs);
1373
30
    if (code < 0) goto exit_grestore;
1374
30
    code = pdfi_annot_draw_border(ctx, annot, true);
1375
30
    if (code < 0) goto exit_grestore;
1376
1377
30
    code = pdfi_grestore(ctx);
1378
30
    if (code < 0) goto exit;
1379
1380
30
    code = gs_translate(ctx->pgs, -1.3*width, 0);
1381
30
    if (code < 0) goto exit;
1382
30
    seglen = width / 2;
1383
30
    code = gs_moveto(ctx->pgs, -seglen*8.4, -seglen*5.9);
1384
30
    if (code < 0) goto exit;
1385
30
    code = gs_lineto(ctx->pgs, -seglen/1.2, 0);
1386
30
    if (code < 0) goto exit;
1387
30
    code = gs_lineto(ctx->pgs, -seglen*8.4, seglen*5.9);
1388
30
    if (code < 0) goto exit;
1389
30
    code = gs_closepath(ctx->pgs);
1390
30
    if (code < 0) goto exit;
1391
30
    code = pdfi_annot_opacity(ctx, annot);
1392
30
    if (code < 0) goto exit;
1393
30
    code = pdfi_annot_fillborderpath(ctx, annot);
1394
30
    goto exit;
1395
1396
0
 exit_grestore:
1397
0
    (void)pdfi_grestore(ctx);
1398
30
 exit:
1399
30
    return code;
1400
0
}
1401
1402
/* Draw OpenArrow LE */
1403
static int pdfi_annot_draw_LE_OpenArrow(pdf_context *ctx, pdf_dict *annot)
1404
0
{
1405
0
    int code;
1406
0
    double width;
1407
1408
0
    code = pdfi_annot_get_BS_width(ctx, annot, &width);
1409
0
    if (code < 0) goto exit;
1410
1411
0
    code = gs_setlinejoin(ctx->pgs, 0);
1412
0
    if (code < 0) goto exit;
1413
1414
0
    code = gs_moveto(ctx->pgs, -width*6, -width*4);
1415
0
    if (code < 0) goto exit;
1416
0
    code = gs_lineto(ctx->pgs, -width/1.2, 0);
1417
0
    if (code < 0) goto exit;
1418
0
    code = gs_lineto(ctx->pgs, -width*6, width*4);
1419
0
    if (code < 0) goto exit;
1420
0
    code = pdfi_annot_draw_border(ctx, annot, true);
1421
0
    if (code < 0) goto exit;
1422
1423
0
 exit:
1424
0
    return code;
1425
0
}
1426
1427
/* Draw RClosedArrow LE */
1428
static int pdfi_annot_draw_LE_RClosedArrow(pdf_context *ctx, pdf_dict *annot)
1429
0
{
1430
0
    int code;
1431
1432
0
    code = gs_rotate(ctx->pgs, 180);
1433
0
    if (code < 0) goto exit;
1434
0
    code = pdfi_annot_draw_LE_ClosedArrow(ctx, annot);
1435
0
    if (code < 0) goto exit;
1436
1437
0
 exit:
1438
0
    return code;
1439
0
}
1440
1441
/* Draw ROpenArrow LE */
1442
static int pdfi_annot_draw_LE_ROpenArrow(pdf_context *ctx, pdf_dict *annot)
1443
0
{
1444
0
    int code;
1445
1446
0
    code = gs_rotate(ctx->pgs, 180);
1447
0
    if (code < 0) goto exit;
1448
0
    code = pdfi_annot_draw_LE_OpenArrow(ctx, annot);
1449
0
    if (code < 0) goto exit;
1450
1451
0
 exit:
1452
0
    return code;
1453
0
}
1454
1455
/* Draw None or null LE (do nothing) */
1456
static int pdfi_annot_draw_LE_None(pdf_context *ctx, pdf_dict *annot)
1457
89
{
1458
89
    return 0;
1459
89
}
1460
1461
annot_LE_dispatch_t annot_LE_dispatch[] = {
1462
    {"Butt", pdfi_annot_draw_LE_Butt},
1463
    {"Circle", pdfi_annot_draw_LE_Circle},
1464
    {"Diamond", pdfi_annot_draw_LE_Diamond},
1465
    {"Slash", pdfi_annot_draw_LE_Slash},
1466
    {"Square", pdfi_annot_draw_LE_Square},
1467
    {"ClosedArrow", pdfi_annot_draw_LE_ClosedArrow},
1468
    {"OpenArrow", pdfi_annot_draw_LE_OpenArrow},
1469
    {"RClosedArrow", pdfi_annot_draw_LE_RClosedArrow},
1470
    {"ROpenArrow", pdfi_annot_draw_LE_ROpenArrow},
1471
    {"None", pdfi_annot_draw_LE_None},
1472
    { NULL, NULL},
1473
};
1474
1475
/* Draw one line ending at (x,y) */
1476
static int pdfi_annot_draw_LE_one(pdf_context *ctx, pdf_dict *annot, pdf_name *LE,
1477
                                  double x, double y, double angle)
1478
122
{
1479
122
    int code;
1480
122
    int code1;
1481
122
    annot_LE_dispatch_t *dispatch_ptr;
1482
1483
122
    code = pdfi_gsave(ctx);
1484
122
    if (code < 0) goto exit1;
1485
1486
122
    code = gs_translate(ctx->pgs, x, y);
1487
122
    code = gs_moveto(ctx->pgs, 0, 0);
1488
122
    code = gs_rotate(ctx->pgs, angle);
1489
1490
    /* Draw the LE */
1491
1.10k
    for (dispatch_ptr = annot_LE_dispatch; dispatch_ptr->name; dispatch_ptr ++) {
1492
1.10k
        if (pdfi_name_is(LE, dispatch_ptr->name)) {
1493
119
            code = dispatch_ptr->func(ctx, annot);
1494
119
            break;
1495
119
        }
1496
1.10k
    }
1497
122
    if (!dispatch_ptr->name) {
1498
3
        char str[100];
1499
3
        memcpy(str, (const char *)LE->data, LE->length < 100 ? LE->length : 99);
1500
3
        str[LE->length < 100 ? LE->length : 99] = '\0';
1501
3
        dbgmprintf1(ctx->memory, "ANNOT: WARNING No handler for LE %s\n", str);
1502
3
    }
1503
1504
122
 exit1:
1505
122
    code1 = pdfi_grestore(ctx);
1506
122
    if (code < 0)
1507
0
        code = code1;
1508
122
    return code;
1509
122
}
1510
1511
/* Draw line endings using LE entry in annotation dictionary
1512
 * Draws one at (x1,y1) and one at (x2,y2)
1513
 * If LE is a name instead of an array, only draws at x2,y2 (but needs x1,y1 for angle)
1514
 *  (defaults to None if not there)
1515
 *
1516
 * which -- tells whether to draw both ends (0) or just the first one (1) or second one (2)
1517
 */
1518
static int pdfi_annot_draw_LE(pdf_context *ctx, pdf_dict *annot,
1519
                              double x1, double y1, double x2, double y2, int which)
1520
119
{
1521
119
    pdf_obj *LE = NULL;
1522
119
    pdf_name *LE1 = NULL;
1523
119
    pdf_name *LE2 = NULL;
1524
119
    double dx, dy;
1525
119
    double angle;
1526
119
    int code;
1527
119
    pdf_obj_type type;
1528
1529
119
    code = pdfi_dict_knownget(ctx, annot, "LE", (pdf_obj **)&LE);
1530
119
    if (code <= 0)
1531
57
        goto exit;
1532
62
    type = pdfi_type_of(LE);
1533
62
    if (type != PDF_ARRAY && type != PDF_NAME) {
1534
0
        code = gs_note_error(gs_error_typecheck);
1535
0
        goto exit;
1536
0
    }
1537
1538
62
    dx = x2 - x1;
1539
62
    dy = y2 - y1;
1540
62
    code = gs_atan2_degrees(dy, dx, &angle);
1541
62
    if (code < 0)
1542
0
        angle = 0;
1543
1544
62
    if (type == PDF_ARRAY) {
1545
62
        code = pdfi_array_get_type(ctx, (pdf_array *)LE, 0, PDF_NAME, (pdf_obj **)&LE1);
1546
62
        if (code < 0) goto exit;
1547
1548
62
        code = pdfi_array_get_type(ctx, (pdf_array *)LE, 1, PDF_NAME, (pdf_obj **)&LE2);
1549
62
        if (code < 0) goto exit;
1550
62
    } else {
1551
0
        LE1 = (pdf_name *)LE;
1552
0
        LE = NULL;
1553
0
    }
1554
62
    if (LE1 && (!which || which == 1)) {
1555
61
        code = pdfi_annot_draw_LE_one(ctx, annot, LE1, x1, y1, angle+180);
1556
61
        if (code < 0) goto exit;
1557
61
    }
1558
1559
62
    if (LE2 && (!which || which == 2)) {
1560
61
        code = pdfi_annot_draw_LE_one(ctx, annot, LE2, x2, y2, angle);
1561
61
        if (code < 0) goto exit;
1562
61
    }
1563
1564
119
 exit:
1565
119
    pdfi_countdown(LE);
1566
119
    pdfi_countdown(LE1);
1567
119
    pdfi_countdown(LE2);
1568
119
    return code;
1569
62
}
1570
/*********** END /LE ***************/
1571
1572
1573
/* Get the Normal AP dictionary/stream, if it exists
1574
 * Not an error if it doesn't exist, it will just be NULL
1575
 */
1576
static int pdfi_annot_get_NormAP(pdf_context *ctx, pdf_dict *annot, pdf_obj **NormAP)
1577
9.56k
{
1578
9.56k
    int code;
1579
9.56k
    pdf_dict *AP_dict = NULL;
1580
9.56k
    pdf_stream *AP = NULL;
1581
9.56k
    pdf_obj *baseAP = NULL;
1582
9.56k
    pdf_name *AS = NULL;
1583
1584
9.56k
    *NormAP = NULL;
1585
1586
9.56k
    code = pdfi_dict_knownget_type(ctx, annot, "AP", PDF_DICT, (pdf_obj **)&AP_dict);
1587
9.56k
    if (code <= 0) goto exit;
1588
1589
5.79k
    code = pdfi_dict_knownget(ctx, AP_dict, "N", (pdf_obj **)&baseAP);
1590
5.79k
    if (code < 0) goto exit;
1591
1592
    /* Look for /R and /D if there was no /N */
1593
5.56k
    if (code == 0) {
1594
3
        if ((code = pdfi_set_warning_stop(ctx, gs_note_error(gs_error_undefined), NULL, W_PDF_ANNOT_AP_ERROR, "pdfi_annot_get_NormAP", "*** Error: Annotation (AP) lacks the mandatory normal (N) appearance")) < 0)
1595
0
            goto exit;
1596
1597
3
        code = pdfi_dict_knownget(ctx, AP_dict, "R", (pdf_obj **)&baseAP);
1598
3
        if (code < 0) goto exit;
1599
1600
3
        if (code == 0) {
1601
3
            code = pdfi_dict_knownget(ctx, AP_dict, "D", (pdf_obj **)&baseAP);
1602
3
            if (code < 0) goto exit;
1603
3
        }
1604
3
    }
1605
1606
    /* Nothing found */
1607
5.56k
    if (code == 0) goto exit;
1608
1609
5.56k
    switch (pdfi_type_of(baseAP)) {
1610
4.64k
        case PDF_STREAM:
1611
            /* Use baseAP for the AP */
1612
4.64k
            AP = (pdf_stream *)baseAP;
1613
4.64k
            baseAP = NULL;
1614
4.64k
            break;
1615
917
        case PDF_DICT:
1616
917
            code = pdfi_dict_knownget_type(ctx, annot, "AS", PDF_NAME, (pdf_obj **)&AS);
1617
917
            if (code < 0) goto exit;
1618
917
            if (code == 0) {
1619
12
                code = pdfi_set_warning_stop(ctx, gs_note_error(gs_error_undefined), NULL, W_PDF_ANNOT_AP_ERROR, "pdfi_annot_get_NormAP", "WARNING Annotation has non-stream AP but no AS.  Don't know what to render");
1620
12
                goto exit;
1621
12
            }
1622
1623
            /* Lookup the AS in the NormAP and use that as the AP */
1624
905
            code = pdfi_dict_get_by_key(ctx, (pdf_dict *)baseAP, AS, (pdf_obj **)&AP);
1625
905
            if (code < 0) {
1626
                /* Apparently this is not an error, just silently don't have an AP */
1627
515
                *NormAP = (pdf_obj *)TOKEN_null;
1628
515
                code = 0;
1629
515
                goto exit;
1630
515
            }
1631
390
            if (pdfi_type_of(AP) != PDF_STREAM) {
1632
5
                code = gs_note_error(gs_error_typecheck);
1633
5
                goto exit;
1634
5
            }
1635
385
            break;
1636
385
        default:
1637
1
            code = gs_error_typecheck;
1638
1
            goto exit;
1639
5.56k
    }
1640
1641
5.02k
   *NormAP = (pdf_obj *)AP;
1642
5.02k
   pdfi_countup(AP);
1643
1644
9.56k
 exit:
1645
9.56k
    pdfi_countdown(AP_dict);
1646
9.56k
    pdfi_countdown(AP);
1647
9.56k
    pdfi_countdown(AS);
1648
9.56k
    pdfi_countdown(baseAP);
1649
9.56k
    return code;
1650
5.02k
}
1651
1652
static int pdfi_annot_draw_Link(pdf_context *ctx, pdf_dict *annot, pdf_obj *NormAP, bool *render_done)
1653
322
{
1654
322
    int code;
1655
322
    int code1;
1656
322
    bool drawit;
1657
1658
322
    dbgmprintf(ctx->memory, "ANNOT: Drawing Link\n");
1659
1660
322
    code = pdfi_annot_start_transparency(ctx, annot);
1661
322
    if (code < 0)
1662
0
        return code;
1663
1664
322
    code = pdfi_annot_setcolor(ctx, annot, true, &drawit);
1665
322
    if (code < 0) goto exit;
1666
322
    if (!drawit) goto exit;
1667
1668
322
    code = pdfi_annot_draw_border(ctx, annot, false);
1669
322
    if (code < 0) goto exit;
1670
1671
322
    code = pdfi_annot_draw_AP(ctx, annot, NormAP);
1672
1673
322
 exit:
1674
322
    code1 = pdfi_annot_end_transparency(ctx, annot);
1675
322
    if (code == 0) code = code1;
1676
1677
322
    *render_done = true;
1678
322
    return code;
1679
322
}
1680
1681
/* Draw a path from an InkList
1682
 * see zpdfops.c/zpdfinkpath()
1683
 *
1684
 * Construct a smooth path passing though a number of points  on the stack
1685
 * for PDF ink annotations. The program is based on a very simple method of
1686
 * smoothing polygons by Maxim Shemanarev.
1687
 * http://www.antigrain.com/research/bezier_interpolation/
1688
 *
1689
 * InkList is array of arrays, each representing a stroked path
1690
 */
1691
static int pdfi_annot_draw_InkList(pdf_context *ctx, pdf_dict *annot, pdf_array *InkList)
1692
43
{
1693
43
    int code = 0;
1694
43
    pdf_array *points = NULL;
1695
43
    int i;
1696
43
    int num_points;
1697
43
    double x1, y1, x2, y2;
1698
1699
79
    for (i=0; i<pdfi_array_size(InkList); i++) {
1700
43
        int j;
1701
1702
43
        code = pdfi_array_get_type(ctx, InkList, i, PDF_ARRAY, (pdf_obj **)&points);
1703
43
        if (code < 0)
1704
0
            goto exit;
1705
1706
43
        num_points = pdfi_array_size(points);
1707
43
        if (num_points < 2)
1708
0
            goto exit;
1709
1710
43
        code = pdfi_array_get_number(ctx, points, 0, &x1);
1711
43
        if (code < 0) goto exit;
1712
43
        code = pdfi_array_get_number(ctx, points, 1, &y1);
1713
43
        if (code < 0) goto exit;
1714
43
        code = gs_moveto(ctx->pgs, x1, y1);
1715
43
        if (code < 0) goto exit;
1716
43
        if (num_points == 2)
1717
0
            goto stroke;
1718
1719
1.22k
        for (j = 2; j < num_points; j += 2) {
1720
1.18k
            code = pdfi_array_get_number(ctx, points, j, &x2);
1721
1.18k
            if (code < 0) goto exit;
1722
1.18k
            code = pdfi_array_get_number(ctx, points, j+1, &y2);
1723
1.18k
            if (code < 0) goto exit;
1724
1.17k
            code = gs_lineto(ctx->pgs, x2, y2);
1725
1.17k
            if (code < 0) goto exit;
1726
1.17k
        }
1727
1728
36
    stroke:
1729
36
        code = gs_stroke(ctx->pgs);
1730
36
        if (code < 0) goto exit;
1731
36
        pdfi_countdown(points);
1732
36
        points = NULL;
1733
36
    }
1734
1735
43
 exit:
1736
43
    pdfi_countdown(points);
1737
43
    return code;
1738
43
}
1739
1740
/* Draw a path from a Path array (see 2.0 spec 12.5.6.13)
1741
 * Path is array of arrays.
1742
 * First array is 2 args for moveto
1743
 * The rest of the arrays are either 2 args for lineto or 6 args for curveto
1744
 */
1745
static int pdfi_annot_draw_Path(pdf_context *ctx, pdf_dict *annot, pdf_array *Path)
1746
0
{
1747
0
    int code = 0;
1748
0
    pdf_array *points = NULL;
1749
0
    double array[6];
1750
0
    int i;
1751
0
    int num_points;
1752
1753
0
    for (i=0; i<pdfi_array_size(Path); i++) {
1754
0
        code = pdfi_array_get_type(ctx, Path, i, PDF_ARRAY, (pdf_obj **)&points);
1755
0
        if (code < 0)
1756
0
            goto exit;
1757
1758
0
        num_points = pdfi_array_size(points);
1759
0
        if (num_points != 2 && num_points != 6) {
1760
0
            code = gs_note_error(gs_error_rangecheck);
1761
0
            goto exit;
1762
0
        }
1763
1764
0
        code = pdfi_array_to_num_array(ctx, points, array, 0, num_points);
1765
0
        if (code < 0) goto exit;
1766
1767
0
        if (i == 0) {
1768
0
            code = gs_moveto(ctx->pgs, array[0], array[1]);
1769
0
            if (code < 0) goto exit;
1770
0
        } else {
1771
0
            if (num_points == 2) {
1772
0
                code = gs_lineto(ctx->pgs, array[0], array[1]);
1773
0
                if (code < 0) goto exit;
1774
0
            } else {
1775
0
                code = gs_curveto(ctx->pgs, array[0], array[1],
1776
0
                                  array[2], array[3], array[4], array[5]);
1777
0
                if (code < 0) goto exit;
1778
0
            }
1779
0
        }
1780
1781
0
        pdfi_countdown(points);
1782
0
        points = NULL;
1783
0
    }
1784
1785
0
    code = pdfi_annot_draw_border(ctx, annot, true);
1786
1787
0
 exit:
1788
0
    pdfi_countdown(points);
1789
0
    return code;
1790
0
}
1791
1792
/* Generate appearance (see pdf_draw.ps/Ink
1793
 *
1794
 * If there was an AP it was already handled.
1795
 */
1796
static int pdfi_annot_draw_Ink(pdf_context *ctx, pdf_dict *annot, pdf_obj *NormAP, bool *render_done)
1797
46
{
1798
46
    int code = 0;
1799
46
    int code1 = 0;
1800
46
    bool drawit;
1801
46
    pdf_array *array = NULL;
1802
1803
46
    code = pdfi_annot_setcolor(ctx, annot, false, &drawit);
1804
46
    if (code <0 || !drawit)
1805
1
        goto exit1;
1806
1807
45
    code = pdfi_annot_start_transparency(ctx, annot);
1808
45
    if (code < 0) goto exit1;
1809
1810
45
    code = gs_setlinewidth(ctx->pgs, 1);
1811
45
    if (code < 0) goto exit;
1812
45
    code = gs_setlinecap(ctx->pgs, 1);
1813
45
    if (code < 0) goto exit;
1814
45
    code = gs_setlinejoin(ctx->pgs, 1);
1815
45
    if (code < 0) goto exit;
1816
1817
45
    code = pdfi_dict_knownget_type(ctx, annot, "InkList", PDF_ARRAY, (pdf_obj **)&array);
1818
45
    if (code < 0) goto exit;
1819
45
    if (code > 0) {
1820
43
        code = pdfi_annot_draw_InkList(ctx, annot, array);
1821
43
        goto exit;
1822
43
    }
1823
2
    code = pdfi_dict_knownget_type(ctx, annot, "Path", PDF_ARRAY, (pdf_obj **)&array);
1824
2
    if (code < 0) goto exit;
1825
2
    if (code > 0) {
1826
0
        code = pdfi_annot_draw_Path(ctx, annot, array);
1827
0
        goto exit;
1828
0
    }
1829
1830
45
 exit:
1831
45
    code1 = pdfi_annot_end_transparency(ctx, annot);
1832
45
    if (code >= 0)
1833
38
        code = code1;
1834
46
 exit1:
1835
46
    pdfi_countdown(array);
1836
46
    *render_done = true;
1837
46
    return code;
1838
45
}
1839
1840
/* from pdf_draw.ps/drawellipse
1841
 * "Don Lancaster's code for drawing an ellipse"
1842
 * https://www.tinaja.com/glib/ellipse4.pdf
1843
 *
1844
/magic 0.55228475 0.00045 sub store  % improved value
1845
/drawellipse {
1846
2 div /yrad exch store
1847
2 div /xrad exch store
1848
/xmag xrad magic mul store
1849
/ymag yrad magic mul store xrad
1850
neg 0 moveto
1851
xrad neg ymag xmag neg yrad 0 yrad curveto
1852
xmag  yrad xrad ymag xrad 0 curveto
1853
xrad ymag neg  xmag yrad neg 0  yrad neg curveto
1854
xmag neg yrad neg xrad neg ymag neg xrad neg  0 curveto
1855
} bind def
1856
1857
 */
1858
static int pdfi_annot_drawellipse(pdf_context *ctx, double width, double height)
1859
12
{
1860
12
    int code = 0;
1861
12
    double magic = .55228475 - .00045; /* Magic value */
1862
12
    double xrad = width / 2;
1863
12
    double yrad = height / 2;
1864
12
    double xmag = xrad * magic;
1865
12
    double ymag = yrad * magic;
1866
1867
12
    code = gs_moveto(ctx->pgs, -xrad, 0);
1868
12
    if (code < 0) return code;
1869
12
    code = gs_curveto(ctx->pgs, -xrad, ymag, -xmag, yrad, 0, yrad);
1870
12
    if (code < 0) return code;
1871
12
    code = gs_curveto(ctx->pgs, xmag, yrad, xrad, ymag, xrad, 0);
1872
12
    if (code < 0) return code;
1873
12
    code = gs_curveto(ctx->pgs, xrad, -ymag, xmag, -yrad, 0, -yrad);
1874
12
    if (code < 0) return code;
1875
12
    code = gs_curveto(ctx->pgs, -xmag, -yrad, -xrad, -ymag, -xrad, 0);
1876
1877
12
    return code;
1878
12
}
1879
1880
/* Generate appearance (see pdf_draw.ps/Circle)
1881
 *
1882
 * If there was an AP it was already handled.
1883
 */
1884
static int pdfi_annot_draw_Circle(pdf_context *ctx, pdf_dict *annot, pdf_obj *NormAP, bool *render_done)
1885
12
{
1886
12
    int code = 0;
1887
12
    int code1 = 0;
1888
12
    gs_rect rect;
1889
12
    bool drawit;
1890
12
    double width, height;
1891
1892
12
    code = pdfi_annot_start_transparency(ctx, annot);
1893
12
    if (code < 0) goto exit1;
1894
1895
12
    code = pdfi_annot_Rect(ctx, annot, &rect);
1896
12
    if (code < 0) goto exit;
1897
1898
12
    code = pdfi_annot_applyRD(ctx, annot, &rect);
1899
12
    if (code < 0) goto exit;
1900
1901
    /* Translate to the center of the ellipse */
1902
12
    width = rect.q.x - rect.p.x;
1903
12
    height = rect.q.y - rect.p.y;
1904
12
    code = gs_translate(ctx->pgs, rect.p.x + width/2, rect.p.y + height/2);
1905
12
    if (code < 0) goto exit;
1906
1907
    /* Draw the ellipse */
1908
12
    code = pdfi_annot_drawellipse(ctx, width, height);
1909
12
    if (code < 0) goto exit;
1910
1911
12
    code = pdfi_annot_fillborderpath(ctx, annot);
1912
12
    if (code < 0) goto exit;
1913
1914
12
    code = pdfi_annot_setcolor(ctx, annot, false, &drawit);
1915
12
    if (code < 0) goto exit;
1916
1917
12
    if (drawit) {
1918
12
        code = pdfi_annot_draw_border(ctx, annot, true);
1919
12
        if (code < 0) goto exit;
1920
12
    }
1921
1922
12
 exit:
1923
12
    code1 = pdfi_annot_end_transparency(ctx, annot);
1924
12
    if (code >= 0)
1925
12
        code = code1;
1926
12
 exit1:
1927
12
    *render_done = true;
1928
12
    return code;
1929
12
}
1930
1931
1932
/*********** BEGIN /Stamp ***************/
1933
typedef struct {
1934
    double r,g,b;
1935
} pdfi_annot_stamp_rgb_t;
1936
1937
#define STAMP_RED {0xef/255.,0x40/255.,0x23/255.}
1938
#define STAMP_GREEN {0x2f/255.,0xae/255.,0x49/255.}
1939
#define STAMP_BLUE {0x00/255.,0x72/255.,0xbc/255.}
1940
1941
typedef struct {
1942
    const char *text;
1943
    double y;
1944
    double h;
1945
} pdfi_annot_stamp_text_t;
1946
1947
typedef struct {
1948
    const char *type;
1949
    pdfi_annot_stamp_rgb_t rgb;
1950
    pdfi_annot_stamp_text_t text[2];
1951
} pdfi_annot_stamp_type_t;
1952
1953
#define STAMP_1LINE(text,y,h) {{text,y,h},{0}}
1954
#define STAMP_2LINE(text,y,h,text2,y2,h2) {{text,y,h},{text2,y2,h2}}
1955
1956
static pdfi_annot_stamp_type_t pdfi_annot_stamp_types[] = {
1957
    {"Approved", STAMP_GREEN, STAMP_1LINE("APPROVED", 13, 30)},
1958
    {"AsIs", STAMP_RED, STAMP_1LINE("AS IS", 13, 30)},
1959
    {"Confidential", STAMP_RED, STAMP_1LINE("CONFIDENTIAL", 17, 20)},
1960
    {"Departmental", STAMP_BLUE, STAMP_1LINE("DEPARTMENTAL", 17, 20)},
1961
    {"Draft", STAMP_RED, STAMP_1LINE("DRAFT", 13, 30)},
1962
    {"Experimental", STAMP_BLUE, STAMP_1LINE("EXPERIMENTAL", 17, 20)},
1963
    {"Expired", STAMP_RED, STAMP_1LINE("EXPIRED", 13, 30)},
1964
    {"Final", STAMP_RED, STAMP_1LINE("FINAL", 13, 30)},
1965
    {"ForComment", STAMP_GREEN, STAMP_1LINE("FOR COMMENT", 17, 20)},
1966
    {"ForPublicRelease", STAMP_GREEN, STAMP_2LINE("FOR PUBLIC",26,18,"RELEASE",8.5,18)},
1967
    {"NotApproved", STAMP_RED, STAMP_1LINE("NOT APPROVED", 17, 20)},
1968
    {"NotForPublicRelease", STAMP_RED, STAMP_2LINE("NOT FOR",26,18,"PUBLIC RELEASE",8.5,18)},
1969
    {"Sold", STAMP_BLUE, STAMP_1LINE("SOLD", 13, 30)},
1970
    {"TopSecret", STAMP_RED, STAMP_1LINE("TOP SECRET", 14, 26)},
1971
    {0}
1972
};
1973
/* Use "Draft" if not found, make sure this value is correct if above array changes */
1974
#define STAMP_DRAFT_INDEX 4
1975
1976
/* Draw the frame for the stamp.  This is taken straight form the PS code. */
1977
static int pdfi_annot_draw_stamp_frame(pdf_context *ctx)
1978
0
{
1979
0
    int code;
1980
1981
0
    code = gs_moveto(ctx->pgs, 6.0, 0.0);
1982
0
    if (code < 0) goto exit;
1983
0
    code = gs_arcto(ctx->pgs, 190, 0, 190, 6, 6, 0);
1984
0
    if (code < 0) goto exit;
1985
0
    code = gs_arcto(ctx->pgs, 190, 47, 184, 47, 6, 0);
1986
0
    if (code < 0) goto exit;
1987
0
    code = gs_arcto(ctx->pgs, 0, 47, 0, 41, 6, 0);
1988
0
    if (code < 0) goto exit;
1989
0
    code = gs_arcto(ctx->pgs, 0, 0, 6, 0, 6, 0);
1990
0
    if (code < 0) goto exit;
1991
0
    code = gs_closepath(ctx->pgs);
1992
0
    if (code < 0) goto exit;
1993
1994
0
    code = gs_moveto(ctx->pgs, 10, 4);
1995
0
    if (code < 0) goto exit;
1996
0
    code = gs_arcto(ctx->pgs, 185, 4, 185, 9, 5, 0);
1997
0
    if (code < 0) goto exit;
1998
0
    code = gs_arcto(ctx->pgs, 185, 43, 180, 43, 5, 0);
1999
0
    if (code < 0) goto exit;
2000
0
    code = gs_arcto(ctx->pgs, 5, 43, 5, 38, 5, 0);
2001
0
    if (code < 0) goto exit;
2002
0
    code = gs_arcto(ctx->pgs, 5, 4, 9, 4, 5, 0);
2003
0
    if (code < 0) goto exit;
2004
0
    code = gs_closepath(ctx->pgs);
2005
0
    if (code < 0) goto exit;
2006
0
    code = gs_eofill(ctx->pgs);
2007
0
    if (code < 0) goto exit;
2008
2009
0
 exit:
2010
0
    return code;
2011
0
}
2012
2013
/* draw text for stamp */
2014
static int pdfi_annot_draw_stamp_text(pdf_context *ctx, pdf_dict *annot, gs_rect *rect,
2015
                                      pdfi_annot_stamp_text_t *text)
2016
0
{
2017
0
    int code = 0;
2018
0
    int code1 = 0;
2019
0
    pdf_string *string = NULL;
2020
0
    gs_rect bbox;
2021
0
    gs_point awidth;
2022
0
    double x,y;
2023
2024
0
    if (!text->text)
2025
0
        return 0;
2026
2027
0
    code = pdfi_BT(ctx);
2028
0
    if (code < 0)
2029
0
        return code;
2030
2031
0
    code = pdfi_annot_set_font(ctx, "Times-Bold", text->h);
2032
0
    if (code < 0) goto exit;
2033
2034
0
    code = pdfi_obj_charstr_to_string(ctx, text->text, &string);
2035
0
    if (code < 0) goto exit;
2036
2037
2038
    /* At this point the graphics state is slightly rotated and current position
2039
     * is at the lower left corner of the stamp's box.
2040
     * The math here matches what the gs code does, so it's kind of confusing.
2041
     */
2042
    /* Get width of the string */
2043
0
    code = pdfi_string_bbox(ctx, string, &bbox, &awidth, false);
2044
0
    if (code < 0) goto exit;
2045
2046
    /* Calculate offset from corner (95 is hard-coded value based on size of the stamp I think) */
2047
0
    x = 95 - (bbox.q.x-bbox.p.x)/2; /* hard-coded value taken from gs code */
2048
0
    y = text->y;
2049
2050
0
    code = pdfi_gsave(ctx);
2051
0
    code = pdfi_gs_setgray(ctx, .75);
2052
0
    code = pdfi_annot_display_simple_text(ctx, annot, x+1, y-1, string);
2053
0
    if (code < 0) goto exit;
2054
0
    code = pdfi_grestore(ctx);
2055
2056
0
    code = pdfi_annot_display_simple_text(ctx, annot, x, y, string);
2057
0
    if (code < 0) goto exit;
2058
2059
0
 exit:
2060
0
    code1 = pdfi_ET(ctx);
2061
0
    if (code == 0) code = code1;
2062
0
    pdfi_countdown(string);
2063
0
    return 0;
2064
0
}
2065
2066
/* Generate appearance (see pdf_draw.ps/Stamp)
2067
 *
2068
 * If there was an AP it was already handled.
2069
 * For testing, see tests_private/pdf/PDF_1.7_FTS/fts_32_3204.pdf
2070
 */
2071
static int pdfi_annot_draw_Stamp(pdf_context *ctx, pdf_dict *annot, pdf_obj *NormAP, bool *render_done)
2072
0
{
2073
0
    int code = 0;
2074
0
    int code1 = 0;
2075
0
    gs_rect rect;
2076
0
    double width, height;
2077
0
    pdfi_annot_stamp_type_t *stamp_type;
2078
0
    pdf_name *Name = NULL;
2079
0
    double xscale, yscale;
2080
0
    double angle;
2081
0
    int i;
2082
2083
0
    code = pdfi_annot_start_transparency(ctx, annot);
2084
0
    if (code < 0) goto exit1;
2085
2086
0
    code = pdfi_annot_Rect(ctx, annot, &rect);
2087
0
    if (code < 0) goto exit;
2088
2089
0
    code = pdfi_dict_knownget_type(ctx, annot, "Name", PDF_NAME, (pdf_obj **)&Name);
2090
0
    if (code <= 0) goto exit;
2091
2092
    /* Translate to the center of Rect */
2093
0
    width = rect.q.x - rect.p.x;
2094
0
    height = rect.q.y - rect.p.y;
2095
0
    code = gs_translate(ctx->pgs, rect.p.x + width/2, rect.p.y + height/2);
2096
0
    if (code < 0) goto exit;
2097
2098
    /* This math comes from the PS code.  I assume the values are related to
2099
       the h and y values in the various Stamp type data.
2100
    */
2101
    /* Scale by the minimum of the two ratios, but make sure it's not 0 */
2102
0
    yscale = height / 50.;
2103
0
    xscale = width / 190.;
2104
0
    if (xscale < yscale)
2105
0
        yscale = xscale;
2106
0
    if (yscale <= 0.0)
2107
0
        yscale = 1.0;
2108
0
    code = gs_scale(ctx->pgs, yscale, yscale);
2109
0
    if (code < 0) goto exit;
2110
2111
    /* Search through types and find a match, if no match use Draft */
2112
0
    for (stamp_type = pdfi_annot_stamp_types; stamp_type->type; stamp_type ++) {
2113
0
        if (Name && pdfi_name_is(Name, stamp_type->type))
2114
0
            break;
2115
0
    }
2116
2117
    /* TODO: There are a bunch of Stamp Names built into AR that are not part of
2118
     * the spec.  Since we don't know about them, we will just display nothing.
2119
     * Example: the "Faces" stamps, such as FacesZippy
2120
     * See file tests_private/pdf/uploads/annots-noAP.pdf
2121
     */
2122
0
    if (!stamp_type->type) {
2123
0
        code = pdfi_set_warning_stop(ctx, gs_note_error(gs_error_undefined), NULL, W_PDF_ANNOT_ERROR, "pdfi_annot_draw_Stamp", "WARNING: Annotation: No AP, unknown Stamp Name, omitting");
2124
0
        goto exit;
2125
0
    }
2126
2127
    /* Draw the frame */
2128
0
    code = pdfi_gs_setrgbcolor(ctx, stamp_type->rgb.r, stamp_type->rgb.g,
2129
0
                               stamp_type->rgb.b);
2130
0
    if (code < 0) goto exit;
2131
2132
0
    code = gs_translate(ctx->pgs, -95, -25);
2133
0
    if (code < 0) goto exit;
2134
0
    code = gs_atan2_degrees(2.0, 190.0, &angle);
2135
0
    if (code < 0) goto exit;
2136
0
    code = gs_rotate(ctx->pgs, angle);
2137
0
    if (code < 0) goto exit;
2138
2139
    /* Draw frame in gray */
2140
0
    code = pdfi_gsave(ctx);
2141
0
    code = gs_translate(ctx->pgs, 1, -1);
2142
0
    code = pdfi_gs_setgray(ctx, 0.75);
2143
0
    code = pdfi_annot_draw_stamp_frame(ctx);
2144
0
    code = pdfi_grestore(ctx);
2145
2146
    /* Draw frame colored */
2147
0
    code = pdfi_annot_draw_stamp_frame(ctx);
2148
2149
    /* Draw the text */
2150
0
    for (i=0; i<2; i++) {
2151
0
        code = pdfi_annot_draw_stamp_text(ctx, annot, &rect, &stamp_type->text[i]);
2152
0
        if (code < 0) goto exit;
2153
0
    }
2154
2155
0
 exit:
2156
0
    code1 = pdfi_annot_end_transparency(ctx, annot);
2157
0
    if (code >= 0)
2158
0
        code = code1;
2159
0
 exit1:
2160
0
    pdfi_countdown(Name);
2161
0
    *render_done = true;
2162
0
    return code;
2163
0
}
2164
/*********** END /Stamp ***************/
2165
2166
2167
/* Draw Callout Line (CL) */
2168
static int pdfi_annot_draw_CL(pdf_context *ctx, pdf_dict *annot)
2169
5
{
2170
5
    int code;
2171
5
    pdf_array *CL = NULL;
2172
5
    double array[6];
2173
5
    int length;
2174
2175
5
    code = pdfi_dict_knownget_type(ctx, annot, "CL", PDF_ARRAY, (pdf_obj **)&CL);
2176
5
    if (code <= 0) goto exit;
2177
2178
0
    length = pdfi_array_size(CL);
2179
2180
0
    if (length != 4 && length != 6) {
2181
0
        code = gs_note_error(gs_error_syntaxerror);
2182
0
        goto exit;
2183
0
    }
2184
2185
0
    code = pdfi_array_to_num_array(ctx, CL, array, 0, length);
2186
0
    if (code < 0) goto exit;
2187
2188
    /* Draw the line */
2189
0
    code = gs_moveto(ctx->pgs, array[0], array[1]);
2190
0
    if (code < 0) goto exit;
2191
0
    code = gs_lineto(ctx->pgs, array[2], array[3]);
2192
0
    if (code < 0) goto exit;
2193
0
    if (length == 6) {
2194
0
        code = gs_lineto(ctx->pgs, array[4], array[5]);
2195
0
        if (code < 0) goto exit;
2196
0
    }
2197
0
    code = gs_stroke(ctx->pgs);
2198
2199
    /* Line ending
2200
     * NOTE: This renders a different arrow than the gs code, but I used the
2201
     * same LE code I used elsewhere, which is clearly better.  So there will be diffs...
2202
     */
2203
0
    code = pdfi_annot_draw_LE(ctx, annot, array[0], array[1], array[2], array[3], 1);
2204
0
    if (code < 0) goto exit;
2205
2206
5
 exit:
2207
5
    pdfi_countdown(CL);
2208
5
    return code;
2209
0
}
2210
2211
/* FreeText -- Draw text with border around it.
2212
 *
2213
 * See pdf_draw.ps/FreeText
2214
 * If there was an AP it was already handled
2215
 */
2216
static int pdfi_annot_draw_FreeText(pdf_context *ctx, pdf_dict *annot, pdf_obj *NormAP, bool *render_done)
2217
5
{
2218
5
    int code = 0;
2219
5
    int code1 = 0;
2220
5
    bool drawbackground;
2221
5
    pdf_dict *BS = NULL;
2222
5
    pdf_string *Contents = NULL;
2223
5
    gs_rect annotrect, modrect;
2224
5
    int64_t Rotate;
2225
2226
5
    *render_done = false;
2227
2228
5
    code = pdfi_annot_start_transparency(ctx, annot);
2229
5
    if (code < 0) goto exit1;
2230
2231
5
    code = pdfi_annot_opacity(ctx, annot);
2232
5
    if (code < 0) goto exit;
2233
2234
5
    code = pdfi_annot_Rect(ctx, annot, &annotrect);
2235
5
    if (code < 0) goto exit;
2236
2237
    /* Set the (background?) color if applicable */
2238
5
    code = pdfi_annot_setcolor(ctx, annot, false, &drawbackground);
2239
5
    if (code < 0) goto exit;
2240
2241
    /* Only draw rectangle if a color was specified */
2242
5
    if (drawbackground) {
2243
0
        code = pdfi_annot_fillRect(ctx, annot);
2244
0
        if (code < 0) goto exit;
2245
0
    }
2246
2247
    /* Set DA (Default Appearance)
2248
     * This will generally set a color and font.
2249
     *
2250
     * TODO: Unclear if this is supposed to also determine the color
2251
     * of the border box, but it seems like it does in gs, so we set the DA
2252
     * before drawing the border, like gs does.
2253
     */
2254
5
    code = pdfi_annot_process_DA(ctx, NULL, annot, &annotrect, false);
2255
5
    if (code < 0) goto exit;
2256
2257
    /* Draw border around text */
2258
    /* TODO: gs-compatible implementation is commented out.  Would rather just delete it... */
2259
5
#if 1
2260
    /* TODO: Prefer to do it this way! */
2261
5
    code = pdfi_annot_draw_border(ctx, annot, false);
2262
5
    if (code < 0) goto exit;
2263
#else
2264
    /* NOTE: I would really like to just call pdfi_annot_draw_border()
2265
     * without checking for BS, but the implementation in gs is subtly different (see below)
2266
     * and I want to make it possible to bmpcmp.
2267
     */
2268
    code = pdfi_dict_knownget_type(ctx, annot, "BS", PDF_DICT, (pdf_obj **)&BS);
2269
    if (code < 0) goto exit;
2270
    if (BS) {
2271
        code = pdfi_annot_draw_border(ctx, annot, false);
2272
        if (code < 0) goto exit;
2273
    } else {
2274
        gs_rect rect;
2275
2276
        /* Draw our own border */
2277
        code = pdfi_gs_setgray(ctx, 0);
2278
        if (code < 0) goto exit;
2279
2280
        /* NOTE: This is almost identical to pdfi_annot_strokeborder() with a width=1.0
2281
         * except it adjusts the rectangle by 1.0 instead of 0.5.
2282
         * Should probably just call that function, but I want to match gs implementation
2283
         * exactly for bmpcmp purposes.
2284
         */
2285
        code = gs_setlinewidth(ctx->pgs, 1);
2286
        if (code < 0) goto exit;
2287
2288
        code = pdfi_annot_Rect(ctx, annot, &rect);
2289
        if (code < 0) goto exit;
2290
2291
        pdfi_annot_rect_adjust(ctx, &rect, 1);
2292
        code = gs_rectstroke(ctx->pgs, &rect, 1, NULL);
2293
        if (code < 0) goto exit;
2294
    }
2295
#endif
2296
2297
5
    code = pdfi_annot_draw_CL(ctx, annot);
2298
5
    if (code < 0) goto exit;
2299
2300
    /* Modify rect by RD if applicable */
2301
5
    code = pdfi_annot_applyRD(ctx, annot, &annotrect);
2302
5
    if (code < 0) goto exit;
2303
2304
    /* /Rotate */
2305
5
    code = pdfi_dict_get_int_def(ctx, annot, "Rotate", &Rotate, 0);
2306
5
    if (code < 0) goto exit;
2307
2308
5
    modrect = annotrect; /* structure copy */
2309
    /* TODO: I think this can all be done in a generic way using gs_transform() but it
2310
     * makes my head explode...
2311
     */
2312
5
    switch(Rotate) {
2313
0
    case 270:
2314
0
        code = gs_moveto(ctx->pgs, modrect.q.y, modrect.q.x);
2315
0
        if (code < 0) goto exit;
2316
0
        code = gs_rotate(ctx->pgs, 270.0);
2317
0
        modrect.p.x = -annotrect.q.y;
2318
0
        modrect.q.x = -annotrect.p.y;
2319
0
        modrect.p.y = annotrect.p.x;
2320
0
        modrect.q.y = annotrect.q.x;
2321
0
        break;
2322
0
    case 180:
2323
0
        code = gs_moveto(ctx->pgs, modrect.q.x, modrect.p.y);
2324
0
        if (code < 0) goto exit;
2325
0
        code = gs_rotate(ctx->pgs, 180.0);
2326
0
        modrect.p.x = -annotrect.q.x;
2327
0
        modrect.q.x = -annotrect.p.x;
2328
0
        modrect.p.y = -annotrect.q.y;
2329
0
        modrect.q.y = -annotrect.p.y;
2330
0
        break;
2331
0
        break;
2332
0
    case 90:
2333
0
        code = gs_moveto(ctx->pgs, modrect.p.y, modrect.p.x);
2334
0
        if (code < 0) goto exit;
2335
0
        code = gs_rotate(ctx->pgs, 90.0);
2336
0
        modrect.p.x = annotrect.p.y;
2337
0
        modrect.q.x = annotrect.q.y;
2338
0
        modrect.p.y = -annotrect.q.x;
2339
0
        modrect.q.y = -annotrect.p.x;
2340
0
        break;
2341
5
    case 0:
2342
5
    default:
2343
5
        modrect.p.x += 2;
2344
5
        modrect.p.y += 2;
2345
5
        modrect.q.x -= 2;
2346
5
        modrect.q.y -= 2;
2347
2348
        /* Move to initial position (slightly offset from the corner of the rect) */
2349
5
        code = gs_moveto(ctx->pgs, modrect.p.x, modrect.q.y);
2350
5
        if (code < 0) goto exit;
2351
5
        break;
2352
5
    }
2353
2354
    /* /Contents */
2355
5
    code = pdfi_dict_knownget_type(ctx, annot, "Contents", PDF_STRING, (pdf_obj **)&Contents);
2356
5
    if (code < 0) goto exit;
2357
5
    if (code > 0) {
2358
5
        code = pdfi_annot_display_formatted_text(ctx, annot, &modrect, Contents, false);
2359
5
        if (code < 0) goto exit;
2360
5
    }
2361
2362
5
 exit:
2363
5
    code1 = pdfi_annot_end_transparency(ctx, annot);
2364
5
    if (code >= 0)
2365
5
        code = code1;
2366
5
 exit1:
2367
5
    *render_done = true;
2368
5
    pdfi_countdown(BS);
2369
5
    pdfi_countdown(Contents);
2370
5
    return code;
2371
5
}
2372
2373
/* Generate appearance (see pdf_draw.ps/Text)
2374
 * This draws the default "Note" thingy which looks like a little page.
2375
 * TODO: Spec lists a whole bunch of different /Name with different appearances that we don't handle.
2376
 * See page 1 of tests_private/pdf/sumatra/annotations_without_appearance_streams.pdf
2377
 *
2378
 * If there was an AP it was already handled.
2379
 */
2380
static int pdfi_annot_draw_Text(pdf_context *ctx, pdf_dict *annot, pdf_obj *NormAP, bool *render_done)
2381
2
{
2382
2
    int code = 0;
2383
2
    int code1 = 0;
2384
2
    gs_rect rect;
2385
2386
2
    code = pdfi_annot_start_transparency(ctx, annot);
2387
2
    if (code < 0) goto exit1;
2388
2389
2
    code = pdfi_annot_Rect(ctx, annot, &rect);
2390
2
    if (code < 0) goto exit;
2391
2
    code = gs_translate(ctx->pgs, rect.p.x, rect.p.y);
2392
2
    if (code < 0) goto exit;
2393
2394
    /* Draw a page icon (/Note) */
2395
    /* TODO: All the other /Name appearances */
2396
2
    code = gs_translate(ctx->pgs, 0.5, (rect.q.y-rect.p.y)-18.5);
2397
2
    if (code < 0) goto exit;
2398
2
    code = gs_setlinewidth(ctx->pgs, 1.0);
2399
2
    if (code < 0) goto exit;
2400
2
    code = pdfi_gs_setgray(ctx, 0.75);
2401
2
    if (code < 0) goto exit;
2402
2
    code = gs_moveto(ctx->pgs, 0.5, -1);
2403
2
    if (code < 0) goto exit;
2404
2
    code = gs_lineto(ctx->pgs, 10, -1);
2405
2
    if (code < 0) goto exit;
2406
2
    code = gs_lineto(ctx->pgs, 15, 4);
2407
2
    if (code < 0) goto exit;
2408
2
    code = gs_lineto(ctx->pgs, 15, 17.5);
2409
2
    if (code < 0) goto exit;
2410
2
    code = gs_stroke(ctx->pgs);
2411
2
    if (code < 0) goto exit;
2412
2413
2
    code = gs_moveto(ctx->pgs, 0, 0);
2414
2
    if (code < 0) goto exit;
2415
2
    code = gs_lineto(ctx->pgs, 9, 0);
2416
2
    if (code < 0) goto exit;
2417
2
    code = gs_lineto(ctx->pgs, 14, 5);
2418
2
    if (code < 0) goto exit;
2419
2
    code = gs_lineto(ctx->pgs, 14, 18);
2420
2
    if (code < 0) goto exit;
2421
2
    code = gs_lineto(ctx->pgs, 0, 18);
2422
2
    if (code < 0) goto exit;
2423
2
    code = gs_closepath(ctx->pgs);
2424
2
    if (code < 0) goto exit;
2425
2426
2
    code = pdfi_gsave(ctx);
2427
2
    if (code >= 0) {
2428
2
        code = pdfi_gs_setgray(ctx, 0.5);
2429
2
        code = gs_fill(ctx->pgs);
2430
2
        code = pdfi_grestore(ctx);
2431
2
    } else
2432
0
        goto exit;
2433
2434
2
    code = pdfi_gs_setgray(ctx, 0);
2435
2
    if (code < 0) goto exit;
2436
2
    code = gs_stroke(ctx->pgs);
2437
2
    if (code < 0) goto exit;
2438
2439
2
    code = gs_moveto(ctx->pgs, 3, 8);
2440
2
    if (code < 0) goto exit;
2441
2
    code = gs_lineto(ctx->pgs, 7.5, 8);
2442
2
    if (code < 0) goto exit;
2443
2
    code = gs_moveto(ctx->pgs, 3, 11);
2444
2
    if (code < 0) goto exit;
2445
2
    code = gs_lineto(ctx->pgs, 10, 11);
2446
2
    if (code < 0) goto exit;
2447
2
    code = gs_moveto(ctx->pgs, 3, 14);
2448
2
    if (code < 0) goto exit;
2449
2
    code = gs_lineto(ctx->pgs, 10, 14);
2450
2
    if (code < 0) goto exit;
2451
2
    code = gs_moveto(ctx->pgs, 9, 0);
2452
2
    if (code < 0) goto exit;
2453
2
    code = gs_lineto(ctx->pgs, 9, 5);
2454
2
    if (code < 0) goto exit;
2455
2
    code = gs_lineto(ctx->pgs, 14, 5);
2456
2
    if (code < 0) goto exit;
2457
2
    code = gs_stroke(ctx->pgs);
2458
2
    if (code < 0) goto exit;
2459
2460
2
 exit:
2461
2
    code1 = pdfi_annot_end_transparency(ctx, annot);
2462
2
    if (code >= 0)
2463
2
        code = code1;
2464
2
 exit1:
2465
2
    *render_done = true;
2466
2
    return code;
2467
2
}
2468
2469
/* Converts array of 4 points to a basis and 2 dimensions (6 values altogether)
2470
 *    x0 y0 x1 y1 x2 y2 x3 y3 -> x0 y0 x1-x0 y1-y0 x2-x0 y2-y0
2471
 *
2472
 *  ^
2473
 *  |
2474
 *  * (x2,y2)    * (x3,y3)
2475
 *  |
2476
 *  |
2477
 *  *------------*->
2478
 *  (x0,y0)      (x1,y1)
2479
 */
2480
static void pdfi_annot_quadpoints2basis(pdf_context *ctx, double *array,
2481
                                        double *px0, double *py0,
2482
                                        double *dx1, double *dy1,
2483
                                        double *dx2, double *dy2)
2484
627
{
2485
627
    double x0,x1,x2,x3,y0,y1,y2,y3;
2486
627
    double minx, miny;
2487
627
    int i, mindex;
2488
2489
    /* comment from the PS code
2490
    % The text is oriented with respect to the vertex with the smallest
2491
    % y value (or the leftmost of those, if there are two such vertices)
2492
    % (x0, y0) and the next vertex in a counterclockwise direction
2493
    % (x1, y1), regardless of whether these are the first two points in
2494
    % the QuadPoints array.
2495
    */
2496
2497
    /* starting min point */
2498
627
    minx = array[0];
2499
627
    miny = array[1];
2500
627
    mindex = 0;
2501
    /* This finds the index of the point with miny and if there were two points with same
2502
     * miny, it finds the one with minx
2503
     */
2504
2.50k
    for (i=2; i<8; i+=2) {
2505
1.88k
        if ((array[i+1] == miny && array[i] < minx) ||  (array[i+1] < miny)) {
2506
            /* Set new min point */
2507
181
            miny = array[i+1];
2508
181
            minx = array[i];
2509
181
            mindex = i;
2510
181
        }
2511
1.88k
    }
2512
2513
    /* Pull out the points such that x0 = minx, y0 = miny, etc */
2514
627
    i = mindex;
2515
627
    x0 = array[i];
2516
627
    y0 = array[i+1];
2517
627
    i += 2;
2518
627
    if (i == 8) i = 0;
2519
627
    x1 = array[i];
2520
627
    y1 = array[i+1];
2521
627
    i += 2;
2522
627
    if (i == 8) i = 0;
2523
627
    x2 = array[i];
2524
627
    y2 = array[i+1];
2525
627
    i += 2;
2526
627
    if (i == 8) i = 0;
2527
627
    x3 = array[i];
2528
627
    y3 = array[i+1];
2529
2530
    /* Make it go counterclockwise by picking lower of these 2 points */
2531
627
    if (y3 < y1) {
2532
35
        y1 = y3;
2533
35
        x1 = x3;
2534
35
    }
2535
2536
    /* Convert into a starting point and two sets of lengths */
2537
627
    *px0 = x0;
2538
627
    *py0 = y0;
2539
627
    *dx1 = x1 - x0;
2540
627
    *dy1 = y1 - y0;
2541
627
    *dx2 = x2 - x0;
2542
627
    *dy2 = y2 - y0;
2543
627
}
2544
2545
/* Generate appearance for Underline or StrikeOut (see pdf_draw.ps/Underline and Strikeout)
2546
 *
2547
 * offset -- how far above the QuadPoints box bottom to draw the line
2548
 *
2549
 */
2550
static int pdfi_annot_draw_line_offset(pdf_context *ctx, pdf_dict *annot, double offset)
2551
41
{
2552
41
    int code = 0;
2553
41
    bool drawit;
2554
41
    pdf_array *QuadPoints = NULL;
2555
41
    double array[8];
2556
41
    int size;
2557
41
    int num_quads;
2558
41
    int i;
2559
41
    double x0, y0, dx1, dy1, dx2, dy2;
2560
41
    double linewidth;
2561
2562
41
    code = pdfi_annot_setcolor(ctx, annot, false, &drawit);
2563
41
    if (code <0 || !drawit)
2564
2
        goto exit;
2565
2566
39
    code = pdfi_dict_knownget_type(ctx, annot, "QuadPoints", PDF_ARRAY, (pdf_obj **)&QuadPoints);
2567
39
    if (code <= 0) goto exit;
2568
2569
39
    size = pdfi_array_size(QuadPoints);
2570
39
    num_quads = size / 8;
2571
2572
534
    for (i=0; i<num_quads; i++) {
2573
495
        code = pdfi_array_to_num_array(ctx, QuadPoints, array, i*8, 8);
2574
495
        if (code < 0) goto exit;
2575
2576
495
        pdfi_annot_quadpoints2basis(ctx, array, &x0, &y0, &dx1, &dy1, &dx2, &dy2);
2577
2578
        /* Acrobat draws the line at offset-ratio of the box width from the bottom of the box
2579
         * and 1/16 thick of the box width.  /Rect is ignored.
2580
         */
2581
495
        linewidth = sqrt((dx2*dx2) + (dy2*dy2)) / 16.;
2582
495
        code = gs_setlinewidth(ctx->pgs, linewidth);
2583
495
        if (code < 0) goto exit;
2584
495
        code = gs_moveto(ctx->pgs, dx2*offset + x0 , dy2*offset + y0);
2585
495
        if (code < 0) goto exit;
2586
495
        code = gs_lineto(ctx->pgs, dx2*offset + x0 + dx1, dy2*offset + y0 + dy1);
2587
495
        if (code < 0) goto exit;
2588
495
        code = gs_stroke(ctx->pgs);
2589
495
        if (code < 0) goto exit;
2590
495
    }
2591
2592
41
 exit:
2593
41
    pdfi_countdown(QuadPoints);
2594
41
    return code;
2595
39
}
2596
2597
/* Generate appearance (see pdf_draw.ps/StrikeOut)
2598
 *
2599
 * If there was an AP it was already handled.
2600
 */
2601
static int pdfi_annot_draw_StrikeOut(pdf_context *ctx, pdf_dict *annot, pdf_obj *NormAP, bool *render_done)
2602
37
{
2603
37
    *render_done = true;
2604
37
    return pdfi_annot_draw_line_offset(ctx, annot, 3/7.);
2605
37
}
2606
2607
/* Generate appearance (see pdf_draw.ps/Underline)
2608
 *
2609
 * If there was an AP it was already handled.
2610
 */
2611
static int pdfi_annot_draw_Underline(pdf_context *ctx, pdf_dict *annot, pdf_obj *NormAP, bool *render_done)
2612
4
{
2613
4
    *render_done = true;
2614
4
    return pdfi_annot_draw_line_offset(ctx, annot, 1/7.);
2615
4
}
2616
2617
/* Connect 2 points with an arc that has max distance from the line segment to the arc
2618
 * equal to 1/4 of the radius.
2619
 */
2620
static int pdfi_annot_highlight_arc(pdf_context *ctx, double x0, double y0,
2621
                                    double x1, double y1)
2622
1.34k
{
2623
1.34k
    int code = 0;
2624
1.34k
    double dx, dy;
2625
1.34k
    double xc, yc, r, a1, a2;
2626
2627
1.34k
    dx = x1 - x0;
2628
1.34k
    dy = y1 - y0;
2629
2630
1.34k
    yc = (y1+y0)/2. - 15./16.*dx;
2631
1.34k
    xc = (x1+x0)/2. + 15./16.*dy;
2632
1.34k
    r = sqrt((y1-yc)*(y1-yc) + (x1-xc)*(x1-xc));
2633
1.34k
    code = gs_atan2_degrees(y1-yc, x1-xc, &a1);
2634
1.34k
    if (code < 0)
2635
8
        a1 = 0;
2636
1.34k
    code = gs_atan2_degrees(y0-yc, x0-xc, &a2);
2637
1.34k
    if (code < 0)
2638
8
        a2 = 0;
2639
1.34k
    code = gs_arcn(ctx->pgs, xc, yc, r, a2, a1);
2640
2641
1.34k
    return code;
2642
1.34k
}
2643
2644
/* Generate appearance (see pdf_draw.ps/Highlight)
2645
 *
2646
 * If there was an AP it was already handled.
2647
 */
2648
static int pdfi_annot_draw_Highlight(pdf_context *ctx, pdf_dict *annot, pdf_obj *NormAP, bool *render_done)
2649
153
{
2650
153
    int code = 0;
2651
153
    bool drawit;
2652
153
    pdf_array *QuadPoints = NULL;
2653
153
    double array[8];
2654
153
    int size;
2655
153
    int num_quads;
2656
153
    int i;
2657
2658
153
    code = pdfi_annot_setcolor(ctx, annot, false, &drawit);
2659
153
    if (code <0 || !drawit)
2660
4
        goto exit;
2661
2662
149
    code = gs_setlinecap(ctx->pgs, 1);
2663
149
    if (code < 0) goto exit;
2664
2665
149
    code = pdfi_dict_knownget_type(ctx, annot, "QuadPoints", PDF_ARRAY, (pdf_obj **)&QuadPoints);
2666
149
    if (code <= 0) goto exit;
2667
2668
147
    size = pdfi_array_size(QuadPoints);
2669
147
    num_quads = size / 8;
2670
2671
810
    for (i=0; i<num_quads; i++) {
2672
679
        code = pdfi_array_to_num_array(ctx, QuadPoints, array, i*8, 8);
2673
679
        if (code < 0) goto exit;
2674
2675
674
        code = gs_moveto(ctx->pgs, array[2], array[3]);
2676
674
        if (code < 0) goto exit;
2677
2678
674
        code = pdfi_annot_highlight_arc(ctx, array[2], array[3], array[6], array[7]);
2679
674
        if (code < 0) goto exit;
2680
2681
670
        code = gs_lineto(ctx->pgs, array[4], array[5]);
2682
670
        if (code < 0) goto exit;
2683
2684
670
        code = pdfi_annot_highlight_arc(ctx, array[4], array[5], array[0], array[1]);
2685
670
        if (code < 0) goto exit;
2686
2687
663
        code = gs_closepath(ctx->pgs);
2688
663
        if (code < 0) goto exit;
2689
2690
663
        if (ctx->page.has_transparency) {
2691
663
            code = pdfi_trans_begin_simple_group(ctx, NULL, false, false, false);
2692
663
            if (code < 0) goto exit;
2693
2694
663
            code = gs_setblendmode(ctx->pgs, BLEND_MODE_Multiply);
2695
663
            if (code < 0) {
2696
0
                (void)pdfi_trans_end_simple_group(ctx);
2697
0
                goto exit;
2698
0
            }
2699
663
            code = gs_fill(ctx->pgs);
2700
663
            (void)pdfi_trans_end_simple_group(ctx);
2701
663
            if (code < 0) goto exit;
2702
663
        } else {
2703
0
            code = gs_stroke(ctx->pgs);
2704
0
            if (code < 0) goto exit;
2705
2706
0
            code = gs_newpath(ctx->pgs);
2707
0
            if (code < 0) goto exit;
2708
0
        }
2709
663
    }
2710
2711
153
 exit:
2712
153
    pdfi_countdown(QuadPoints);
2713
153
    return code;
2714
147
}
2715
2716
/* Generate appearance (see pdf_draw.ps/Squiggly)
2717
 *
2718
 * If there was an AP it was already handled.
2719
 */
2720
static int pdfi_annot_draw_Squiggly(pdf_context *ctx, pdf_dict *annot, pdf_obj *NormAP, bool *render_done)
2721
53
{
2722
53
    int code = 0;
2723
53
    bool drawit;
2724
53
    pdf_array *QuadPoints = NULL;
2725
53
    double array[8];
2726
53
    int size;
2727
53
    int num_quads;
2728
53
    int i;
2729
53
    double x0, y0, dx1, dy1, dx2, dy2;
2730
53
    double p1, p2, p12;
2731
53
    int count;
2732
53
    bool need_grestore = false;
2733
53
    gs_matrix matrix;
2734
2735
53
    code = pdfi_annot_setcolor(ctx, annot, false, &drawit);
2736
53
    if (code <0 || !drawit)
2737
2
        goto exit;
2738
2739
51
    code = gs_setlinecap(ctx->pgs, gs_cap_round);
2740
51
    if (code < 0) goto exit;
2741
51
    code = gs_setlinejoin(ctx->pgs, gs_join_round);
2742
51
    if (code < 0) goto exit;
2743
51
    code = gs_setlinewidth(ctx->pgs, 1.0);
2744
51
    if (code < 0) goto exit;
2745
2746
51
    code = pdfi_dict_knownget_type(ctx, annot, "QuadPoints", PDF_ARRAY, (pdf_obj **)&QuadPoints);
2747
51
    if (code <= 0) goto exit;
2748
2749
50
    size = pdfi_array_size(QuadPoints);
2750
50
    num_quads = size / 8;
2751
2752
182
    for (i=0; i<num_quads; i++) {
2753
132
        code = pdfi_array_to_num_array(ctx, QuadPoints, array, i*8, 8);
2754
132
        if (code < 0) goto exit;
2755
2756
132
        code = pdfi_gsave(ctx);
2757
132
        if (code < 0) goto exit;
2758
132
        need_grestore = true;
2759
2760
        /* Make clipping region */
2761
132
        code = gs_moveto(ctx->pgs, array[6], array[7]);
2762
132
        if (code < 0) goto exit;
2763
132
        code = gs_lineto(ctx->pgs, array[4], array[5]);
2764
132
        if (code < 0) goto exit;
2765
132
        code = gs_lineto(ctx->pgs, array[0], array[1]);
2766
132
        if (code < 0) goto exit;
2767
132
        code = gs_lineto(ctx->pgs, array[2], array[3]);
2768
132
        if (code < 0) goto exit;
2769
132
        code = gs_closepath(ctx->pgs);
2770
132
        if (code < 0) goto exit;
2771
132
        code = gs_clip(ctx->pgs);
2772
132
        if (code < 0) goto exit;
2773
132
        code = gs_newpath(ctx->pgs);
2774
132
        if (code < 0) goto exit;
2775
2776
132
        pdfi_annot_quadpoints2basis(ctx, array, &x0, &y0, &dx1, &dy1, &dx2, &dy2);
2777
2778
132
        code = gs_translate(ctx->pgs, x0+dx2/56+dx2/72, y0+dy2/56+dy2/72);
2779
132
        if (code < 0) goto exit;
2780
2781
132
        p2 = sqrt(dx2*dx2 + dy2*dy2);
2782
132
        p1 = sqrt(dx1*dx1 + dy1*dy1);
2783
2784
132
        if (p2 > 0 && p1 > 0) {
2785
131
            p12 = p2 / p1;
2786
2787
131
            count = (int)((1/p12) * 4 + 1);
2788
2789
131
            matrix.xx = dx1 * p12;
2790
131
            matrix.xy = dy1 * p12;
2791
131
            matrix.yx = dx2;
2792
131
            matrix.yy = dy2;
2793
131
            matrix.tx = 0.0;
2794
131
            matrix.ty = 0.0;
2795
131
            code = gs_concat(ctx->pgs, &matrix);
2796
131
            if (code < 0) goto exit;
2797
2798
131
            code = gs_scale(ctx->pgs, 1/40., 1/72.);
2799
131
            if (code < 0) goto exit;
2800
2801
131
            code = gs_moveto(ctx->pgs, 0, 0);
2802
131
            if (code < 0) goto exit;
2803
2804
15.3k
            while (count >= 0) {
2805
15.2k
                code = gs_lineto(ctx->pgs, 5, 10);
2806
15.2k
                if (code < 0) goto exit;
2807
15.2k
                code = gs_lineto(ctx->pgs, 10, 0);
2808
15.2k
                if (code < 0) goto exit;
2809
15.2k
                code = gs_translate(ctx->pgs, 10, 0);
2810
15.2k
                if (code < 0) goto exit;
2811
2812
15.2k
                count --;
2813
15.2k
            }
2814
131
            code = gs_stroke(ctx->pgs);
2815
131
            if (code < 0) goto exit;
2816
131
        }
2817
2818
132
        need_grestore = false;
2819
132
        code = pdfi_grestore(ctx);
2820
132
        if (code < 0) goto exit;
2821
132
    }
2822
2823
53
 exit:
2824
53
    if (need_grestore)
2825
0
        (void)pdfi_grestore(ctx);
2826
53
    pdfi_countdown(QuadPoints);
2827
53
    *render_done = true;
2828
53
    return code;
2829
50
}
2830
2831
/* Generate appearance (see pdf_draw.ps/Redact)
2832
 *
2833
 * If there was an AP it was already handled.
2834
 */
2835
static int pdfi_annot_draw_Redact(pdf_context *ctx, pdf_dict *annot, pdf_obj *NormAP, bool *render_done)
2836
0
{
2837
    /* comment from PS code:
2838
    %% Redact annotations are part of a process, a Redact annotation is only present
2839
    %% until the content is removed, before that the content should be present and
2840
    %% I believe we should print it. So take no action for Redact annotations if they
2841
    %% have no appearance.
2842
    */
2843
    /* Render nothing, don't print warning */
2844
0
    *render_done = true;
2845
0
    return 0;
2846
0
}
2847
2848
/* Handle PopUp (see pdf_draw.ps/Popup)
2849
 * Renders only if /Open=true
2850
 *
2851
 * If there's an AP, caller will handle it on return
2852
 */
2853
static int pdfi_annot_draw_Popup(pdf_context *ctx, pdf_dict *annot, pdf_obj *NormAP, bool *render_done)
2854
776
{
2855
776
    int code = 0, code1 = 0;
2856
776
    bool Open = false;
2857
776
    pdf_dict *Parent = NULL;
2858
776
    pdf_string *Contents = NULL;
2859
776
    pdf_string *T = NULL;
2860
776
    bool has_color;
2861
776
    gs_rect rect, rect2;
2862
776
    bool need_grestore = false;
2863
2864
    /* Render only if open */
2865
776
    code = pdfi_dict_get_bool(ctx, annot, "Open", &Open);
2866
776
    if (code < 0 && (code != gs_error_undefined))
2867
0
        goto exit1;
2868
2869
776
    code = 0;
2870
2871
    /* Don't render if not Open */
2872
776
    if (!Open) {
2873
764
        *render_done = true;
2874
764
        goto exit1;
2875
764
    }
2876
2877
    /* Let caller render if there is an AP */
2878
12
    if (NormAP) {
2879
0
        *render_done = false;
2880
0
        goto exit1;
2881
0
    }
2882
2883
    /* regardless what happens next, tell caller we rendered it */
2884
12
    *render_done = true;
2885
2886
12
    code = pdfi_annot_start_transparency(ctx, annot);
2887
12
    if (code < 0) goto exit1;
2888
2889
12
    code = gs_setlinewidth(ctx->pgs, 0.05);
2890
2891
12
    code = pdfi_loop_detector_mark(ctx);
2892
12
    if (code < 0)
2893
0
        goto exit;
2894
2895
    /* Use Parent to get color */
2896
    /* We must not store the derferenced Parent in the Annot, because the Parent is (or might be)
2897
     * the Page dictionary, which contains the Annots array. If we replace the indirect reference
2898
     * to Parent in the Annot then we end up with a circular reference and leak both objects.
2899
     */
2900
12
    code = pdfi_dict_knownget_type_no_store_R(ctx, annot, "Parent", PDF_DICT, (pdf_obj **)&Parent);
2901
12
    if (code < 0) goto exit;
2902
11
    if (code == 0) {
2903
0
        code = pdfi_dict_knownget_type_no_store_R(ctx, annot, "P", PDF_DICT, (pdf_obj **)&Parent);
2904
0
        if (code < 0) goto exit;
2905
0
    }
2906
11
    (void)pdfi_loop_detector_cleartomark(ctx);
2907
2908
11
    if (code == 0) {
2909
        /* If no parent, we will use the annotation itself */
2910
0
        Parent = annot;
2911
0
        pdfi_countup(Parent);
2912
0
    }
2913
2914
    /* Set color if there is one specified */
2915
11
    code = pdfi_annot_setcolor(ctx, Parent, false, &has_color);
2916
11
    if (code < 0) goto exit;
2917
2918
    /* Set a default color if nothing specified */
2919
11
    if (!has_color) {
2920
0
        code = pdfi_gs_setrgbcolor(ctx, 1.0, 1.0, 0);
2921
0
        if (code < 0) goto exit;
2922
0
    }
2923
2924
11
    code = pdfi_annot_Rect(ctx, annot, &rect);
2925
11
    if (code < 0) goto exit;
2926
2927
    /* Draw big box (the frame around the /Contents) */
2928
11
    code = pdfi_gsave(ctx);
2929
11
    if (code < 0) goto exit;
2930
11
    need_grestore = true;
2931
11
    code = pdfi_gs_setgray(ctx, 1);
2932
11
    if (code < 0) goto exit;
2933
11
    code = gs_rectfill(ctx->pgs, &rect, 1);
2934
11
    if (code < 0) goto exit;
2935
11
    code = pdfi_gs_setgray(ctx, 0);
2936
11
    if (code < 0) goto exit;
2937
11
    code = gs_rectstroke(ctx->pgs, &rect, 1, NULL);
2938
11
    if (code < 0) goto exit;
2939
11
    code = pdfi_grestore(ctx);
2940
11
    need_grestore = false;
2941
11
    if (code < 0) goto exit;
2942
2943
    /* Display /Contents in Helvetica */
2944
11
    code = pdfi_dict_knownget_type(ctx, Parent, "Contents", PDF_STRING, (pdf_obj **)&Contents);
2945
11
    if (code < 0) goto exit;
2946
11
    if (code > 0) {
2947
11
        gs_rect text_rect;
2948
2949
11
        code = pdfi_gsave(ctx);
2950
11
        if (code < 0) goto exit;
2951
11
        need_grestore = true;
2952
11
        code = pdfi_gs_setgray(ctx, 0);
2953
11
        if (code < 0) goto exit;
2954
2955
11
        text_rect.p.x = rect.p.x + 3;
2956
11
        text_rect.q.x = rect.q.x - 3;
2957
11
        text_rect.p.y = rect.p.y + 3;
2958
11
        text_rect.q.y = rect.q.y - 18;
2959
2960
11
        code = pdfi_annot_set_font(ctx, "Helvetica", 9);
2961
11
        if (code < 0) goto exit;
2962
2963
11
        code = pdfi_annot_display_formatted_text(ctx, annot, &text_rect, Contents, false);
2964
11
        if (code < 0) goto exit;
2965
2966
11
        code = pdfi_grestore(ctx);
2967
11
        need_grestore = false;
2968
11
        if (code < 0) goto exit;
2969
11
    }
2970
2971
    /* Draw small, thin box (the frame around the top /T text) */
2972
11
    rect2.p.x = rect.p.x;
2973
11
    rect2.p.y = rect.q.y - 15;
2974
11
    rect2.q.x = rect.q.x;
2975
11
    rect2.q.y = rect.p.y + (rect.q.y - rect.p.y);
2976
2977
11
    code = gs_rectfill(ctx->pgs, &rect2, 1);
2978
11
    if (code < 0) goto exit;
2979
11
    code = pdfi_gs_setgray(ctx, 0);
2980
11
    if (code < 0) goto exit;
2981
11
    code = gs_rectstroke(ctx->pgs, &rect2, 1, NULL);
2982
11
    if (code < 0) goto exit;
2983
2984
    /* Display /T in Helvetica */
2985
11
    code = pdfi_dict_knownget_type(ctx, Parent, "T", PDF_STRING, (pdf_obj **)&T);
2986
11
    if (code < 0) goto exit;
2987
11
    if (code > 0) {
2988
11
        code = pdfi_annot_set_font(ctx, "Helvetica", 9);
2989
11
        if (code < 0) goto exit;
2990
2991
11
        code = pdfi_annot_display_centered_text(ctx, annot, &rect, T);
2992
11
        if (code < 0) goto exit;
2993
2994
11
    }
2995
2996
12
 exit:
2997
12
    if (need_grestore) {
2998
0
        code1= pdfi_grestore(ctx);
2999
0
        if (code == 0) code = code1;
3000
0
    }
3001
12
    code1 = pdfi_annot_end_transparency(ctx, annot);
3002
12
    if (code == 0) code = code1;
3003
776
 exit1:
3004
776
    pdfi_countdown(Parent);
3005
776
    pdfi_countdown(Contents);
3006
776
    pdfi_countdown(T);
3007
776
    return code;
3008
12
}
3009
3010
/* Generate appearance (see pdf_draw.ps/Line)
3011
 *
3012
 * If there was an AP it was already handled.
3013
 */
3014
static int pdfi_annot_draw_Line(pdf_context *ctx, pdf_dict *annot, pdf_obj *NormAP, bool *render_done)
3015
63
{
3016
63
    int code = 0;
3017
63
    int code1 = 0;
3018
63
    gs_rect lrect;
3019
63
    pdf_array *L = NULL;
3020
63
    bool drawit;
3021
3022
63
    code = pdfi_annot_start_transparency(ctx, annot);
3023
63
    if (code < 0) goto exit1;
3024
3025
63
    code = pdfi_dict_knownget_type(ctx, annot, "L", PDF_ARRAY, (pdf_obj **)&L);
3026
63
    if (code < 0) goto exit;
3027
3028
63
    code = pdfi_array_to_gs_rect(ctx, L, &lrect);
3029
63
    if (code < 0) goto exit;
3030
3031
63
    code = pdfi_annot_setcolor(ctx, annot, false, &drawit);
3032
63
    if (code < 0) goto exit;
3033
3034
    /* Handle LE */
3035
63
    code = pdfi_annot_draw_LE(ctx, annot, lrect.p.x, lrect.p.y, lrect.q.x, lrect.q.y, 0);
3036
63
    if (code < 0) goto exit;
3037
3038
    /* Draw the actual line */
3039
63
    code = gs_moveto(ctx->pgs, lrect.p.x, lrect.p.y);
3040
63
    if (code < 0) goto exit;
3041
63
    code = gs_lineto(ctx->pgs, lrect.q.x, lrect.q.y);
3042
63
    if (code < 0) goto exit;
3043
63
    code = pdfi_annot_draw_border(ctx, annot, true);
3044
63
    if (code < 0) goto exit;
3045
3046
63
 exit:
3047
63
    code1 = pdfi_annot_end_transparency(ctx, annot);
3048
63
    if (code >= 0)
3049
63
        code = code1;
3050
63
 exit1:
3051
63
    *render_done = true;
3052
63
    pdfi_countdown(L);
3053
63
    return code;
3054
63
}
3055
3056
/* Create a path from an array of points */
3057
static int pdfi_annot_path_array(pdf_context *ctx, pdf_dict *annot, pdf_array *Vertices)
3058
29
{
3059
29
    int code = 0;
3060
29
    int i;
3061
3062
160
    for (i=0; i<pdfi_array_size(Vertices); i+=2) {
3063
131
        double x,y;
3064
3065
131
        code = pdfi_array_get_number(ctx, Vertices, i, &x);
3066
131
        if (code < 0) goto exit;
3067
131
        code = pdfi_array_get_number(ctx, Vertices, i+1, &y);
3068
131
        if (code < 0) goto exit;
3069
3070
131
        if (i == 0) {
3071
29
            code = gs_moveto(ctx->pgs, x, y);
3072
29
            if (code < 0) goto exit;
3073
102
        } else {
3074
102
            code = gs_lineto(ctx->pgs, x, y);
3075
102
            if (code < 0) goto exit;
3076
102
        }
3077
131
    }
3078
3079
29
 exit:
3080
29
    return code;
3081
29
}
3082
3083
/* Generate appearance (see pdf_draw.ps/PolyLine)
3084
 * NOTE: as of 5-7-20, the gs implementation of this is actually broken
3085
 *
3086
 * If there was an AP it was already handled.
3087
 */
3088
static int pdfi_annot_draw_PolyLine(pdf_context *ctx, pdf_dict *annot, pdf_obj *NormAP, bool *render_done)
3089
30
{
3090
30
    int code = 0;
3091
30
    int code1 = 0;
3092
30
    pdf_array *Vertices = NULL, *Path = NULL, *Line = NULL;
3093
30
    bool drawit;
3094
30
    int size;
3095
30
    double x1, y1, x2, y2, x3, y3, ends[8];
3096
30
    bool Vertices_known = false, Path_known = false;
3097
3098
30
    code = pdfi_annot_start_transparency(ctx, annot);
3099
30
    if (code < 0) goto exit1;
3100
3101
30
    code = pdfi_dict_known(ctx, annot, "Vertices", &Vertices_known);
3102
30
    if (code < 0) goto exit1;
3103
30
    code = pdfi_dict_known(ctx, annot, "Path", &Path_known);
3104
30
    if (code < 0) goto exit1;
3105
3106
30
    code = pdfi_gsave(ctx);
3107
30
    if (code < 0) goto exit1;
3108
3109
    /* If both are known, or neither are known, then there is a problem */
3110
30
    if (Vertices_known == Path_known) {
3111
2
        code = gs_note_error(gs_error_undefined);
3112
2
        goto exit;
3113
2
    }
3114
3115
28
    if (Vertices_known) {
3116
28
        code = pdfi_dict_get_type(ctx, annot, "Vertices", PDF_ARRAY, (pdf_obj **)&Vertices);
3117
28
        if (code < 0) goto exit;
3118
3119
28
        size = pdfi_array_size(Vertices);
3120
28
        if (size == 0) {
3121
0
            code = 0;
3122
0
            goto exit;
3123
0
        }
3124
28
        code = pdfi_annot_path_array(ctx, annot, Vertices);
3125
28
        if (code < 0) goto exit1;
3126
3127
28
        code = pdfi_annot_setcolor(ctx, annot, false, &drawit);
3128
28
        if (code < 0) goto exit;
3129
3130
28
        code = pdfi_annot_draw_border(ctx, annot, true);
3131
28
        if (code < 0) goto exit;
3132
3133
28
        if (size >= 4) {
3134
28
            code = pdfi_array_get_number(ctx, Vertices, 0, &x1);
3135
28
            if (code < 0) goto exit;
3136
28
            code = pdfi_array_get_number(ctx, Vertices, 1, &y1);
3137
28
            if (code < 0) goto exit;
3138
28
            code = pdfi_array_get_number(ctx, Vertices, 2, &x2);
3139
28
            if (code < 0) goto exit;
3140
28
            code = pdfi_array_get_number(ctx, Vertices, 3, &y2);
3141
28
            if (code < 0) goto exit;
3142
28
            code = pdfi_annot_draw_LE(ctx, annot, x1, y1, x2, y2, 1);
3143
28
            if (code < 0) goto exit;
3144
3145
28
            code = pdfi_array_get_number(ctx, Vertices, size-4, &x1);
3146
28
            if (code < 0) goto exit;
3147
28
            code = pdfi_array_get_number(ctx, Vertices, size-3, &y1);
3148
28
            if (code < 0) goto exit;
3149
28
            code = pdfi_array_get_number(ctx, Vertices, size-2, &x2);
3150
28
            if (code < 0) goto exit;
3151
28
            code = pdfi_array_get_number(ctx, Vertices, size-1, &y2);
3152
28
            if (code < 0) goto exit;
3153
28
            code = pdfi_annot_draw_LE(ctx, annot, x1, y1, x2, y2, 2);
3154
28
            if (code < 0) goto exit;
3155
28
        }
3156
28
    } else {
3157
0
        int i = 0;
3158
0
        int state = 0;
3159
3160
0
        memset(ends, 0x00, 8 * sizeof(double));
3161
0
        code = pdfi_dict_get_type(ctx, annot, "Path", PDF_ARRAY, (pdf_obj **)&Path);
3162
0
        if (code < 0) goto exit;
3163
3164
0
        if (pdfi_array_size(Path) < 2)
3165
0
            goto exit;
3166
3167
0
        code = pdfi_annot_setcolor(ctx, annot, false, &drawit);
3168
0
        if (code < 0) goto exit;
3169
3170
0
        code = pdfi_annot_draw_border(ctx, annot, true);
3171
0
        if (code < 0) goto exit;
3172
3173
0
        for (i = 0; i < pdfi_array_size(Path); i++) {
3174
0
            code = pdfi_array_get_type(ctx, Path, i, PDF_ARRAY, (pdf_obj **)&Line);
3175
0
            if (code < 0)
3176
0
                goto exit;
3177
0
            switch(pdfi_array_size(Line)) {
3178
0
                case 2:
3179
0
                    code = pdfi_array_get_number(ctx, Line, 0, &x1);
3180
0
                    if (code < 0)
3181
0
                        goto exit;
3182
0
                    code = pdfi_array_get_number(ctx, Line, 1, &y1);
3183
0
                    if (code < 0)
3184
0
                        goto exit;
3185
0
                    if (state == 0) {
3186
0
                        state = 1;
3187
0
                        code = gs_moveto(ctx->pgs, x1, y1);
3188
0
                        ends[0] = ends[4] = x1;
3189
0
                        ends[1] = ends[5] = y1;
3190
0
                    } else {
3191
0
                        if (state == 1) {
3192
0
                            ends[2] = ends[6] = x1;
3193
0
                            ends[3] = ends[7] = y1;
3194
0
                            state = 2;
3195
0
                        } else {
3196
0
                            ends[4] = ends[6];
3197
0
                            ends[5] = ends[7];
3198
0
                            ends[6] = x1;
3199
0
                            ends[7] = y1;
3200
0
                        }
3201
0
                        code = gs_lineto(ctx->pgs, x1, y1);
3202
0
                    }
3203
0
                    pdfi_countdown(Line);
3204
0
                    Line = NULL;
3205
0
                    break;
3206
0
                case 6:
3207
0
                    code = pdfi_array_get_number(ctx, Line, 0, &x1);
3208
0
                    if (code < 0)
3209
0
                        goto exit;
3210
0
                    code = pdfi_array_get_number(ctx, Line, 1, &y1);
3211
0
                    if (code < 0)
3212
0
                        goto exit;
3213
0
                    code = pdfi_array_get_number(ctx, Line, 2, &x2);
3214
0
                    if (code < 0)
3215
0
                        goto exit;
3216
0
                    code = pdfi_array_get_number(ctx, Line, 3, &y2);
3217
0
                    if (code < 0)
3218
0
                        goto exit;
3219
0
                    code = pdfi_array_get_number(ctx, Line, 4, &x3);
3220
0
                    if (code < 0)
3221
0
                        goto exit;
3222
0
                    code = pdfi_array_get_number(ctx, Line, 5, &y3);
3223
0
                    if (code < 0)
3224
0
                        goto exit;
3225
0
                    if (state == 1) {
3226
0
                        ends[2] = x1;
3227
0
                        ends[3] = y1;
3228
0
                        ends[4] = x2;
3229
0
                        ends[5] = y2;
3230
0
                        ends[6] = x3;
3231
0
                        ends[7] = y3;
3232
0
                        state = 2;
3233
0
                    }
3234
0
                    ends[4] = x2;
3235
0
                    ends[5] = y2;
3236
0
                    ends[6] = x3;
3237
0
                    ends[7] = y3;
3238
0
                    code = gs_curveto(ctx->pgs, x1, y1, x2, y2, x3, y3);
3239
0
                    pdfi_countdown(Line);
3240
0
                    Line = NULL;
3241
0
                    break;
3242
0
                default:
3243
0
                    pdfi_countdown(Line);
3244
0
                    Line = NULL;
3245
0
                    code = gs_note_error(gs_error_rangecheck);
3246
0
                    break;
3247
0
            }
3248
0
            if (code < 0)
3249
0
                break;
3250
0
        }
3251
0
        if (code < 0)
3252
0
            goto exit;
3253
3254
0
        code = gs_stroke(ctx->pgs);
3255
0
        if (code < 0)
3256
0
            goto exit;
3257
3258
0
        code = pdfi_annot_draw_LE(ctx, annot, ends[0], ends[1], ends[2], ends[3], 1);
3259
0
        if (code < 0)
3260
0
            goto exit;
3261
0
        code = pdfi_annot_draw_LE(ctx, annot, ends[4], ends[5], ends[6], ends[7], 2);
3262
0
    }
3263
3264
30
 exit:
3265
30
    code1 = pdfi_annot_end_transparency(ctx, annot);
3266
30
    if (code >= 0)
3267
28
        code = code1;
3268
30
    code1 = pdfi_grestore(ctx);
3269
30
    if (code >= 0) code = code1;
3270
3271
30
 exit1:
3272
30
    *render_done = true;
3273
30
    pdfi_countdown(Line);
3274
30
    pdfi_countdown(Vertices);
3275
30
    pdfi_countdown(Path);
3276
30
    return code;
3277
30
}
3278
3279
/* Generate appearance (see pdf_draw.ps/Polygon)
3280
 *
3281
 * If there was an AP it was already handled.
3282
 */
3283
static int pdfi_annot_draw_Polygon(pdf_context *ctx, pdf_dict *annot, pdf_obj *NormAP, bool *render_done)
3284
1
{
3285
1
    int code = 0;
3286
1
    int code1 = 0;
3287
1
    pdf_array *Vertices = NULL;
3288
1
    bool drawit;
3289
3290
1
    code = pdfi_annot_start_transparency(ctx, annot);
3291
1
    if (code < 0) goto exit1;
3292
3293
1
    code = pdfi_dict_knownget_type(ctx, annot, "Vertices", PDF_ARRAY, (pdf_obj **)&Vertices);
3294
1
    if (code <= 0) goto exit;
3295
3296
1
    code = pdfi_annot_path_array(ctx, annot, Vertices);
3297
1
    if (code < 0) goto exit1;
3298
3299
1
    code = gs_closepath(ctx->pgs);
3300
3301
    /* NOTE: The logic here seems a bit wonky.  Why only set opacity if there was a fill?
3302
     * Anyway, it is based on the ps code (pdf_draw.ps/Polygon).
3303
     * Maybe can be improved.
3304
     */
3305
1
    code = pdfi_annot_setinteriorcolor(ctx, annot, false, &drawit);
3306
1
    if (code < 0) goto exit;
3307
1
    if (drawit) {
3308
0
        code = pdfi_annot_fillborderpath(ctx, annot);
3309
0
        if (code < 0) goto exit;
3310
0
        code = pdfi_annot_opacity(ctx, annot); /* TODO: Why only on this path? */
3311
0
        if (code < 0) goto exit;
3312
0
    }
3313
1
    code = pdfi_annot_setcolor(ctx, annot, false, &drawit);
3314
1
    if (code < 0) goto exit;
3315
1
    if (drawit) {
3316
1
        code = pdfi_annot_draw_border(ctx, annot, true);
3317
1
        if (code < 0) goto exit;
3318
1
    }
3319
3320
1
 exit:
3321
1
    code1 = pdfi_annot_end_transparency(ctx, annot);
3322
1
    if (code >= 0)
3323
1
        code = code1;
3324
1
 exit1:
3325
1
    *render_done = true;
3326
1
    pdfi_countdown(Vertices);
3327
1
    return code;
3328
1
}
3329
3330
/* Generate appearance (see pdf_draw.ps/Square)
3331
 *
3332
 * If there was an AP it was already handled.
3333
 */
3334
static int pdfi_annot_draw_Square(pdf_context *ctx, pdf_dict *annot, pdf_obj *NormAP, bool *render_done)
3335
7
{
3336
7
    int code = 0;
3337
7
    int code1 = 0;
3338
7
    bool drawit;
3339
3340
7
    code = pdfi_annot_start_transparency(ctx, annot);
3341
7
    if (code < 0) goto exit1;
3342
3343
7
    code = pdfi_annot_setinteriorcolor(ctx, annot, false, &drawit);
3344
7
    if (code < 0) goto exit;
3345
7
    if (drawit) {
3346
3
        code = pdfi_annot_opacity(ctx, annot);
3347
3
        if (code < 0) goto exit;
3348
3
        code = pdfi_annot_fillRect(ctx, annot);
3349
3
        if (code < 0) goto exit;
3350
3351
3
        code = pdfi_annot_setcolor(ctx, annot, false, &drawit);
3352
3
        if (code < 0) goto exit;
3353
3354
3
        if (drawit) {
3355
3
            code = pdfi_annot_draw_border(ctx, annot, false);
3356
3
            if (code < 0) goto exit;
3357
3
        }
3358
4
    } else {
3359
4
        code = pdfi_annot_RectRD_path(ctx, annot);
3360
4
        if (code < 0) goto exit;
3361
3362
4
        code = pdfi_annot_setcolor(ctx, annot, false, &drawit);
3363
4
        if (code < 0) goto exit;
3364
3365
3
        if (drawit) {
3366
3
            code = pdfi_annot_draw_border(ctx, annot, true);
3367
3
            if (code < 0) goto exit;
3368
3
        }
3369
3
    }
3370
3371
7
 exit:
3372
7
    code1 = pdfi_annot_end_transparency(ctx, annot);
3373
7
    if (code >= 0)
3374
6
        code = code1;
3375
7
 exit1:
3376
7
    *render_done = true;
3377
7
    return code;
3378
7
}
3379
3380
static int pdfi_annot_render_MK_box(pdf_context *ctx, pdf_dict *annot, pdf_dict *MK)
3381
2.31k
{
3382
2.31k
    int code = 0;
3383
2.31k
    bool drawit = false;
3384
2.31k
    gs_rect rect;
3385
2.31k
    bool need_grestore = false;
3386
3387
    /* Basically just render the rectangle around the widget for now */
3388
2.31k
    code = pdfi_annot_Rect(ctx, annot, &rect);
3389
2.31k
    if (code < 0) goto exit;
3390
3391
2.30k
    code = pdfi_gsave(ctx);
3392
2.30k
    if (code < 0) goto exit;
3393
2.30k
    need_grestore = true;
3394
2.30k
    code = pdfi_annot_setcolor_key(ctx, MK, "BG", false, &drawit);
3395
2.30k
    if (code < 0) goto exit;
3396
2.30k
    if (drawit)
3397
8
        code = gs_rectfill(ctx->pgs, &rect, 1);
3398
2.30k
    if (code < 0) goto exit;
3399
2.30k
    need_grestore = false;
3400
2.30k
    code = pdfi_grestore(ctx);
3401
2.30k
    if (code < 0) goto exit;
3402
3403
2.30k
    code = pdfi_gsave(ctx);
3404
2.30k
    if (code < 0) goto exit;
3405
2.30k
    need_grestore = true;
3406
2.30k
    code = pdfi_annot_setcolor_key(ctx, MK, "BC", false, &drawit);
3407
2.30k
    if (code < 0) goto exit;
3408
2.30k
    if (drawit)
3409
8
        code = gs_rectstroke(ctx->pgs, &rect, 1, NULL);
3410
2.30k
    if (code < 0) goto exit;
3411
2.30k
    need_grestore = false;
3412
2.30k
    code = pdfi_grestore(ctx);
3413
2.30k
    if (code < 0) goto exit;
3414
3415
    /* TODO: Stuff with can-regenerate-ap ?? */
3416
3417
2.31k
 exit:
3418
2.31k
    if (need_grestore)
3419
0
        (void)pdfi_grestore(ctx);
3420
2.31k
    return code;
3421
2.30k
}
3422
3423
typedef enum {
3424
    PDFI_FORM_FF_READONLY = 1, /* Bit 1 */
3425
    PDFI_FORM_FF_REQUIRED = 1 << 1, /* Bit 2 */
3426
    PDFI_FORM_FF_NOEXPORT = 1 << 2, /* Bit 3 */
3427
    PDFI_FORM_FF_MULTILINE = 1 << 12, /* Bit 13 */
3428
    PDFI_FORM_FF_PASSWORD = 1 << 13, /* Bit 14 */
3429
    PDFI_FORM_FF_NOTOGGLETOOFF = 1 << 14, /* Bit 15 */
3430
    PDFI_FORM_FF_RADIO = 1 << 15, /* Bit 16 */
3431
    PDFI_FORM_FF_PUSHBUTTON = 1 << 16, /* Bit 17 */
3432
    PDFI_FORM_FF_COMBO = 1 << 17, /* Bit 18 */
3433
    PDFI_FORM_FF_EDIT = 1 << 18, /* Bit 19 */
3434
    PDFI_FORM_FF_SORT = 1 << 19, /* Bit 20 */
3435
    PDFI_FORM_FF_FILESELECT = 1 << 20, /* Bit 21 */
3436
    PDFI_FORM_FF_MULTISELECT = 1 << 21, /* Bit 22 */
3437
    PDFI_FORM_FF_DONOTSPELLCHECK = 1 << 22, /* Bit 23 */
3438
    PDFI_FORM_FF_DONOTSCROLL = 1 << 23, /* Bit 24 */
3439
    PDFI_FORM_FF_COMB = 1 << 24, /* Bit 25 */
3440
    PDFI_FORM_FF_RICHTEXT = 1 << 25, /* Bit 26 */ /* also PDFI_FORM_FF_RADIOSINUNISON */
3441
    PDFI_FORM_FF_COMMITONSELCHANGE = 1 << 26, /* Bit 27 */
3442
} pdfi_form_Ff;
3443
3444
/* draw field Btn */
3445
static int pdfi_form_draw_Btn(pdf_context *ctx, pdf_dict *field, pdf_obj *AP)
3446
3
{
3447
3
    int code;
3448
#ifdef DEBUG
3449
    bool Radio = false;
3450
    bool Pushbutton = false;
3451
#endif
3452
3
    int64_t value;
3453
3454
    /* TODO: There can be Kids array. I think it should be handled in caller.
3455
     * Need an example...
3456
     */
3457
3
    if (AP != NULL) {
3458
0
        code = pdfi_annot_draw_AP(ctx, field, AP);
3459
0
        goto exit;
3460
0
    }
3461
3462
    /* Get Ff field (default is 0) */
3463
3
    code = pdfi_form_get_inheritable_int(ctx, field, "Ff", &value);
3464
3
    if (code < 0) goto exit;
3465
3466
#ifdef DEBUG
3467
    Radio = value & PDFI_FORM_FF_RADIO;
3468
    Pushbutton = value & PDFI_FORM_FF_PUSHBUTTON;
3469
3470
    dbgprintf("WARNING: AcroForm field 'Btn' with no AP not implemented.\n");
3471
    dbgprintf2("       : Radio = %s, Pushbutton = %s.\n",
3472
              Radio ? "TRUE" : "FALSE", Pushbutton ? "TRUE" : "FALSE");
3473
#endif
3474
3475
3
 exit:
3476
3
    return 0;
3477
3
}
3478
3479
/* Modify V from Tx widget to skip past the BOM and indicate if UTF-16 was found */
3480
static int pdfi_form_modV(pdf_context *ctx, pdf_string *V, pdf_string **mod_V, bool *is_UTF16)
3481
15
{
3482
15
    bool did_mod = false;
3483
15
    int code = 0;
3484
15
    int skip = 0;
3485
15
    pdf_string *newV = NULL;
3486
3487
15
    *is_UTF16 = false;
3488
3489
15
    if (IS_UTF8(V->data)) {
3490
0
        skip = 3;
3491
15
    } else if (IS_UTF16(V->data)) {
3492
0
        skip = 2;
3493
0
        *is_UTF16 = true;
3494
0
    }
3495
3496
15
    if (skip == 0)
3497
15
        goto exit;
3498
3499
    /* Create a new V that skips over the BOM */
3500
0
    code = pdfi_object_alloc(ctx, PDF_STRING, V->length-skip, (pdf_obj **)&newV);
3501
0
    if (code < 0)
3502
0
        goto exit;
3503
0
    pdfi_countup(newV);
3504
3505
0
    memcpy(newV->data, V->data+skip, newV->length);
3506
0
    did_mod = true;
3507
3508
15
 exit:
3509
    /* If didn't need to mod it, just copy the original and get a ref */
3510
15
    if (did_mod) {
3511
0
        *mod_V = newV;
3512
15
    } else {
3513
15
        *mod_V = V;
3514
15
        pdfi_countup(V);
3515
15
    }
3516
15
    return code;
3517
0
}
3518
3519
/* Display simple text (no multiline or comb) */
3520
static int pdfi_form_Tx_simple(pdf_context *ctx, pdf_dict *annot, gs_rect *rect, pdf_string *V,
3521
                               int64_t Ff, int64_t Q, bool is_UTF16)
3522
15
{
3523
15
    int code = 0;
3524
15
    gs_rect modrect;
3525
15
    double lineheight = 0;
3526
15
    gs_rect bbox;
3527
15
    gs_point awidth;
3528
15
    double y_adjust = 0.0f, x_adjust = 0.0f;
3529
3530
15
    modrect = *rect; /* structure copy */
3531
3532
15
    code = pdfi_annot_get_text_height(ctx, &lineheight);
3533
15
    if (code < 0) goto exit;
3534
3535
    /* text placement adjustments */
3536
15
    switch (Q) {
3537
15
    case 0: /* Left-justified */
3538
15
        x_adjust = 2; /* empirical value */
3539
15
        break;
3540
0
    case 1: /* Centered */
3541
        /* Get width of the string */
3542
0
        code = pdfi_string_bbox(ctx, V, &bbox, &awidth, false);
3543
0
        if (code < 0) goto exit;
3544
0
        x_adjust = ((rect->q.x - rect->p.x) - awidth.x) / 2;
3545
0
        break;
3546
0
    case 2: /* Right-justified */
3547
        /* Get width of the string */
3548
0
        code = pdfi_string_bbox(ctx, V, &bbox, &awidth, false);
3549
0
        if (code < 0) goto exit;
3550
0
        x_adjust = rect->q.x - awidth.x;
3551
0
        break;
3552
15
    }
3553
3554
    /* Center vertically */
3555
15
    y_adjust = ((rect->q.y - rect->p.y) - lineheight) / 2;
3556
3557
15
    modrect.p.x += x_adjust;
3558
15
    modrect.p.y += y_adjust;
3559
15
    modrect.p.y += (lineheight + 6.) / 10.; /* empirical */
3560
3561
15
    code = pdfi_annot_display_simple_text(ctx, annot, modrect.p.x, modrect.p.y, V);
3562
15
    if (code < 0) goto exit;
3563
15
 exit:
3564
15
    return code;
3565
15
}
3566
3567
/* Display text from Tx MULTILINE */
3568
static int pdfi_form_Tx_multiline(pdf_context *ctx, pdf_dict *annot, gs_rect *rect, pdf_string *V,
3569
                                  int64_t Ff, int64_t Q, bool is_UTF16)
3570
0
{
3571
0
    int code = 0;
3572
0
    gs_rect modrect;
3573
3574
0
    modrect = *rect; /* structure copy */
3575
    /* empirical tweaks */
3576
0
    modrect.p.x += 2;
3577
0
    modrect.p.y += 2;
3578
0
    modrect.q.x -= 2;
3579
0
    modrect.q.y -= 2;
3580
3581
0
    code = pdfi_annot_display_formatted_text(ctx, annot, &modrect, V, is_UTF16);
3582
0
    if (code < 0) goto exit;
3583
3584
0
 exit:
3585
0
    return code;
3586
0
}
3587
3588
3589
/* Display text from Tx COMB */
3590
static int pdfi_form_Tx_comb(pdf_context *ctx, pdf_dict *annot, gs_rect *rect, pdf_string *V,
3591
                             int64_t Ff, int64_t Q, int64_t MaxLen, bool is_UTF16)
3592
0
{
3593
0
    int code = 0;
3594
3595
    /* TODO: Implement... Need a sample that uses COMB! */
3596
0
    code = pdfi_form_Tx_simple(ctx, annot, rect, V, Ff, Q, is_UTF16);
3597
0
    return code;
3598
0
}
3599
3600
3601
/* Display text from Tx in a field or widget */
3602
static int pdfi_form_display_Tx(pdf_context *ctx, pdf_dict *annot, gs_rect *rect, pdf_string *V,
3603
                                  int64_t Ff, int64_t Q, int64_t MaxLen)
3604
15
{
3605
15
    int code = 0;
3606
15
    pdf_string *modV = NULL;
3607
15
    bool is_UTF16;
3608
3609
    /* TODO: If we wanted to preserve this for pdfwrite, we would have to generate
3610
     * a stream for the AP.  See PDF1.7 spec, Variable Text (page 677).
3611
     */
3612
3613
    /* Get modified V that skips the BOM, if any */
3614
15
    code = pdfi_form_modV(ctx, V, &modV, &is_UTF16);
3615
15
    if (code < 0) goto exit;
3616
3617
15
    if (Ff & PDFI_FORM_FF_MULTILINE) {
3618
0
        code = pdfi_form_Tx_multiline(ctx, annot, rect, modV, Ff, Q, is_UTF16);
3619
15
    } else if (Ff & PDFI_FORM_FF_COMB) {
3620
0
        code = pdfi_form_Tx_comb(ctx, annot, rect, modV, Ff, Q, MaxLen, is_UTF16);
3621
15
    } else {
3622
15
        code = pdfi_form_Tx_simple(ctx, annot, rect, modV, Ff, Q, is_UTF16);
3623
15
    }
3624
3625
15
 exit:
3626
15
    pdfi_countdown(modV);
3627
15
    return code;
3628
15
}
3629
3630
3631
/* At least for now, Tx and Ch are handled the same */
3632
static int pdfi_form_draw_Tx_Ch(pdf_context *ctx, pdf_dict *annot)
3633
2.23k
{
3634
2.23k
    int code = 0;
3635
2.23k
    pdf_string *V = NULL;
3636
2.23k
    gs_rect annotrect;
3637
2.23k
    int64_t Ff;
3638
2.23k
    int64_t MaxLen;
3639
2.23k
    int64_t Q;
3640
3641
2.23k
    code = pdfi_annot_Rect(ctx, annot, &annotrect);
3642
2.23k
    if (code < 0) goto exit;
3643
3644
    /* Set DA (Default Appearance)
3645
     * This will generally set a color and font.
3646
     */
3647
2.23k
    code = pdfi_annot_process_DA(ctx, annot, annot, &annotrect, true);
3648
2.23k
    if (code < 0) goto exit;
3649
3650
2.23k
    code = pdfi_form_get_inheritable_int(ctx, annot, "Ff", &Ff);
3651
2.23k
    if (code < 0) goto exit;
3652
3653
2.15k
    code = pdfi_form_get_inheritable_int(ctx, annot, "MaxLen", &MaxLen);
3654
2.15k
    if (code < 0) goto exit;
3655
3656
1.67k
    code = pdfi_form_get_inheritable_int(ctx, annot, "Q", &Q);
3657
1.67k
    if (code < 0) goto exit;
3658
3659
1.67k
    code = pdfi_dict_knownget_type(ctx, annot, "V", PDF_STRING, (pdf_obj **)&V);
3660
1.67k
    if (code < 0) goto exit;
3661
1.67k
    if (code > 0) {
3662
15
        code = pdfi_form_display_Tx(ctx, annot, &annotrect, V, Ff, Q, MaxLen);
3663
15
        if (code < 0) goto exit;
3664
15
    }
3665
3666
2.23k
 exit:
3667
2.23k
    pdfi_countdown(V);
3668
2.23k
    return code;
3669
1.67k
}
3670
3671
/* draw field Tx */
3672
static int pdfi_form_draw_Tx(pdf_context *ctx, pdf_dict *annot, pdf_obj *AP)
3673
2.21k
{
3674
2.21k
    if (AP != NULL)
3675
0
        return pdfi_annot_draw_AP(ctx, annot, AP);
3676
3677
2.21k
    return pdfi_form_draw_Tx_Ch(ctx, annot);
3678
2.21k
}
3679
3680
/* draw field Ch */
3681
static int pdfi_form_draw_Ch(pdf_context *ctx, pdf_dict *field, pdf_obj *AP)
3682
12
{
3683
12
    if (!ctx->NeedAppearances && AP != NULL)
3684
0
        return pdfi_annot_draw_AP(ctx, field, AP);
3685
3686
12
    return pdfi_form_draw_Tx_Ch(ctx, field);
3687
12
}
3688
3689
/* draw field Sig */
3690
static int pdfi_form_draw_Sig(pdf_context *ctx, pdf_dict *field, pdf_obj *AP)
3691
0
{
3692
0
    int code = 0;
3693
3694
0
    if (!ctx->NeedAppearances && AP != NULL)
3695
0
        return pdfi_annot_draw_AP(ctx, field, AP);
3696
3697
0
    dbgprintf("WARNING: AcroForm field 'Sig' with no AP not implemented.\n");
3698
3699
0
    return code;
3700
0
}
3701
3702
/* TODO: The spec handles "regenerate appearance", which it does by generating
3703
 * a new AP stream for the field, and then rendering it as a form.
3704
 * The gs code does try to handle this case.
3705
 * For now I am just going to generate the data inline, without building it as a Form/Stream.
3706
 * If we have want to preserve the AP (i.e. for pdfwrite device, generate the AP and include
3707
 * it in the output, rather than just rendering it into the output) then this needs to change.
3708
 * Currently gs doesn't try to preserve AcroForms, so we aren't losing any functionality by
3709
 * not doing this.  But it is a place for a future enhancement.
3710
 *
3711
 * See also, the comment on pdfi_annot_preserve_Widget().
3712
 */
3713
static int pdfi_annot_render_field(pdf_context *ctx, pdf_dict *field, pdf_name *FT, pdf_obj *AP)
3714
2.23k
{
3715
2.23k
    int code;
3716
3717
2.23k
    if (pdfi_name_is(FT, "Btn")) {
3718
3
        code = pdfi_form_draw_Btn(ctx, field, AP);
3719
2.23k
    } else if (pdfi_name_is(FT, "Tx")) {
3720
2.21k
        code = pdfi_form_draw_Tx(ctx, field, AP);
3721
2.21k
    } else if (pdfi_name_is(FT, "Ch")) {
3722
12
        code = pdfi_form_draw_Ch(ctx, field, AP);
3723
12
    } else if (pdfi_name_is(FT, "Sig")) {
3724
0
        code = pdfi_form_draw_Sig(ctx, field, AP);
3725
0
    } else {
3726
0
        errprintf(ctx->memory, "*** WARNING unknown field FT ignored\n");
3727
        /* TODO: Handle warning better */
3728
0
        code = 0;
3729
0
    }
3730
3731
2.23k
    return code;
3732
2.23k
}
3733
3734
/* Render Widget with no AP
3735
 */
3736
static int pdfi_annot_render_Widget(pdf_context *ctx, pdf_dict *annot)
3737
2.31k
{
3738
2.31k
    int code = 0;
3739
2.31k
    pdf_dict *MK = NULL;
3740
2.31k
    pdf_name *FT = NULL;
3741
3742
    /* Render box around the widget using MK */
3743
2.31k
    code = pdfi_dict_knownget_type(ctx, annot, "MK", PDF_DICT, (pdf_obj **)&MK);
3744
2.31k
    if (code < 0) goto exit;
3745
2.31k
    if (code > 0) {
3746
2.31k
        code = pdfi_annot_render_MK_box(ctx, annot, MK);
3747
2.31k
        if (code < 0) goto exit;
3748
2.31k
    }
3749
3750
    /* Render the other contents */
3751
2.30k
    code = pdfi_dict_knownget_type(ctx, annot, "FT", PDF_NAME, (pdf_obj **)&FT);
3752
2.30k
    if (code < 0) goto exit;
3753
2.30k
    if (code > 0) {
3754
2.23k
        code = pdfi_annot_render_field(ctx, annot, FT, NULL);
3755
2.23k
        if (code < 0) goto exit;
3756
2.23k
    }
3757
3758
2.31k
 exit:
3759
2.31k
    pdfi_countdown(FT);
3760
2.31k
    pdfi_countdown(MK);
3761
2.31k
    return code;
3762
2.30k
}
3763
3764
/* Draws a thing of type /Widget */
3765
static int pdfi_annot_draw_Widget(pdf_context *ctx, pdf_dict *annot, pdf_obj *NormAP, bool *render_done)
3766
6.18k
{
3767
6.18k
    int code = 0;
3768
6.18k
    bool found_T = false;
3769
6.18k
    bool found_FT = false, known = false;
3770
6.18k
    pdf_obj *T = NULL;
3771
6.18k
    pdf_obj *FT = NULL;
3772
6.18k
    pdf_dict *Parent = NULL;
3773
6.18k
    pdf_dict *currdict = NULL;
3774
3775
    /* From pdf_draw.ps/drawwidget:
3776
  % Acrobat doesn't draw Widget annotations unles they have both /FT
3777
  % (which is defined as required) and /T keys present. Annoyingly
3778
  % these can either be inherited from the Form Definition Field
3779
  % dictionary (via the AcroForm tree) or present directly in the
3780
  % annotation, so we need to check the annotation to make sure its
3781
  % a Widget, then follow any /Parent key up to the root node
3782
  % extracting and storing any FT or T keys as we go (we only care if
3783
  % these are present, their value is immaterial). If after all that
3784
  % both keys are not present, then we don't draw the annotation.
3785
    */
3786
3787
    /* TODO: See top part of pdf_draw.ps/drawwidget
3788
     * check for /FT and /T and stuff
3789
     */
3790
6.18k
    code = pdfi_loop_detector_mark(ctx);
3791
6.18k
    if (code < 0)
3792
0
        goto exit;
3793
3794
6.18k
    currdict = annot;
3795
6.18k
    pdfi_countup(currdict);
3796
6.90k
    while (true) {
3797
6.90k
        if (currdict->object_num != 0) {
3798
6.90k
            code = pdfi_loop_detector_add_object(ctx, currdict->object_num);
3799
6.90k
            if (code < 0)
3800
0
                break;
3801
6.90k
        }
3802
3803
6.90k
        code = pdfi_dict_knownget(ctx, currdict, "T", &T);
3804
6.90k
        if (code < 0) goto exit;
3805
6.90k
        if (code > 0) {
3806
5.92k
            found_T = true;
3807
5.92k
            pdfi_countdown(T);
3808
5.92k
            T = NULL;
3809
5.92k
            if (found_FT)
3810
39
                break;
3811
5.92k
        }
3812
6.86k
        code = pdfi_dict_knownget(ctx, currdict, "FT", &FT);
3813
6.86k
        if (code < 0) goto exit;
3814
6.86k
        if (code > 0) {
3815
5.91k
            found_FT = true;
3816
5.91k
            pdfi_countdown(FT);
3817
5.91k
            FT = NULL;
3818
5.91k
            if (found_T)
3819
5.88k
                break;
3820
5.91k
        }
3821
        /* Check for Parent. Do not store the dereferenced Parent back to the dictionary
3822
         * as this can cause circular references.
3823
         */
3824
987
        code = pdfi_dict_known(ctx, currdict, "Parent", &known);
3825
987
        if (code >= 0 && known == true)
3826
821
        {
3827
821
            code = pdfi_dict_get_no_store_R(ctx, currdict, "Parent", (pdf_obj **)&Parent);
3828
821
            if (code < 0) {
3829
95
                (void)pdfi_loop_detector_cleartomark(ctx);
3830
95
                goto exit;
3831
95
            }
3832
726
            if (pdfi_type_of(Parent) != PDF_DICT) {
3833
4
                if (pdfi_type_of(Parent) == PDF_INDIRECT) {
3834
0
                    pdf_indirect_ref *o = (pdf_indirect_ref *)Parent;
3835
3836
0
                    code = pdfi_dereference(ctx, o->ref_object_num, o->ref_generation_num, (pdf_obj **)&Parent);
3837
0
                    pdfi_countdown(o);
3838
0
                    if (code < 0)
3839
0
                        break;
3840
4
                } else {
3841
4
                    break;
3842
4
                }
3843
4
            }
3844
722
            pdfi_countdown(currdict);
3845
722
            currdict = Parent;
3846
722
            Parent = NULL;
3847
722
        } else
3848
166
            break;
3849
987
    }
3850
3851
6.08k
    (void)pdfi_loop_detector_cleartomark(ctx);
3852
3853
6.08k
    code = 0;
3854
6.08k
    if (!found_T || !found_FT) {
3855
170
        *render_done = true;
3856
170
        outprintf(ctx->memory, "**** Warning: A Widget annotation dictionary lacks either the FT or T key.\n");
3857
170
        outprintf(ctx->memory, "              Acrobat ignores such annoataions, annotation will not be rendered.\n");
3858
170
        outprintf(ctx->memory, "              Output may not be as expected.\n");
3859
170
        goto exit;
3860
170
    }
3861
3862
5.91k
    if (NormAP) {
3863
        /* Let caller render it */
3864
3.60k
        *render_done = false;
3865
3.60k
        goto exit;
3866
3.60k
    }
3867
3868
    /* No AP, try to render the Widget ourselves */
3869
2.31k
    code = pdfi_annot_render_Widget(ctx, annot);
3870
2.31k
    *render_done = true;
3871
3872
6.18k
 exit:
3873
6.18k
    pdfi_countdown(Parent);
3874
6.18k
    pdfi_countdown(currdict);
3875
6.18k
    return code;
3876
2.31k
}
3877
3878
/* Handle Annotations that are not implemented */
3879
static int pdfi_annot_draw_NotImplemented(pdf_context *ctx, pdf_dict *annot, pdf_obj *NormAP, bool *render_done)
3880
0
{
3881
0
    int code = 0;
3882
0
    pdf_name *Subtype = NULL;
3883
0
    char str[100];
3884
3885
0
    code = pdfi_dict_get_type(ctx, annot, "Subtype", PDF_NAME, (pdf_obj **)&Subtype);
3886
0
    if (code < 0) goto exit;
3887
3888
0
    memcpy(str, (const char *)Subtype->data, Subtype->length < 100 ? Subtype->length : 99);
3889
0
    str[Subtype->length < 100 ? Subtype->length : 99] = '\0';
3890
0
    dbgmprintf1(ctx->memory, "ANNOT: No AP, default appearance for Subtype %s Not Implemented\n", str);
3891
3892
0
 exit:
3893
0
    *render_done = false;
3894
0
    pdfi_countdown(Subtype);
3895
0
    return code;
3896
0
}
3897
3898
annot_dispatch_t annot_dispatch[] = {
3899
    /* These are in the same order as the PDF 2.0 spec 12.5.6.1 */
3900
    {"Text", pdfi_annot_draw_Text, true},
3901
    {"Link", pdfi_annot_draw_Link, false},
3902
    {"FreeText", pdfi_annot_draw_FreeText, true},
3903
    {"Line", pdfi_annot_draw_Line, true},
3904
    {"Square", pdfi_annot_draw_Square, true},
3905
    {"Circle", pdfi_annot_draw_Circle, true},
3906
    {"Polygon", pdfi_annot_draw_Polygon, true},
3907
    {"PolyLine", pdfi_annot_draw_PolyLine, true},
3908
    {"Highlight", pdfi_annot_draw_Highlight, true},
3909
    {"Underline", pdfi_annot_draw_Underline, true},
3910
    {"Squiggly", pdfi_annot_draw_Squiggly, true},
3911
    {"StrikeOut", pdfi_annot_draw_StrikeOut, true},
3912
    {"Caret", pdfi_annot_draw_NotImplemented, true},
3913
    {"Stamp", pdfi_annot_draw_Stamp, true},
3914
    {"Ink", pdfi_annot_draw_Ink, true},
3915
    {"Popup", pdfi_annot_draw_Popup, false},
3916
    {"FileAttachment", pdfi_annot_draw_NotImplemented, true},
3917
    {"Sound", pdfi_annot_draw_NotImplemented, true},
3918
    {"Movie", pdfi_annot_draw_NotImplemented, true},
3919
    {"Screen", pdfi_annot_draw_NotImplemented, true},
3920
    {"Widget", pdfi_annot_draw_Widget, false},
3921
    {"PrinterMark", pdfi_annot_draw_NotImplemented, true},
3922
    {"TrapNet", pdfi_annot_draw_NotImplemented, true},
3923
    {"Watermark", pdfi_annot_draw_NotImplemented, true},
3924
    {"3D", pdfi_annot_draw_NotImplemented, true},
3925
    {"Redact", pdfi_annot_draw_Redact, true},
3926
    {"Projection", pdfi_annot_draw_NotImplemented, true},
3927
    {"RichMedia", pdfi_annot_draw_NotImplemented, true},
3928
    { NULL, NULL},
3929
};
3930
3931
/* Check if annotation is visible
3932
 * (from ps code:)
3933
%
3934
%   The PDF annotation F (flags) integer is bit encoded.
3935
%   Bit 1 (LSB) Invisible:  1 --> Do not display if no handler.
3936
%         Note:  We have no handlers but we ignore this bit.
3937
%   Bit 2 Hidden:  1 --> Do not display.  We will not display if this bit is set.
3938
%   Bit 3 Print:  1 --> Display if printing.  We will display if this bit set
3939
%         (and not hidden) and Printed is true
3940
%   Bit 4 NoZoom:  1 --> Do not zoom annotation even if image is zoomed.
3941
%   Bit 5 NoRotate:  1 --> Do not rotate annotation even if image is rotated.
3942
%   Bit 6 NoView:  0 --> Display if this is a 'viewer'.  We will display
3943
%         if this bit is not set (and not hidden) and Printed is false
3944
%   Bit 7 Read Only - 1 --> No interaction.  We ignore this bit
3945
%
3946
%   AR8 and AR9 print 3D annotations even if Print flag is off. Bug 691486.
3947
%
3948
*/
3949
static bool pdfi_annot_visible(pdf_context *ctx, pdf_dict *annot, pdf_name *subtype)
3950
11.1k
{
3951
11.1k
    int code;
3952
11.1k
    bool is_3D = false;
3953
11.1k
    bool is_visible = true;
3954
11.1k
    int64_t F;
3955
3956
11.1k
    if (pdfi_name_is(subtype, "3D"))
3957
0
        is_3D = true;
3958
3959
11.1k
    code = pdfi_dict_get_int(ctx, annot, "F", &F);
3960
11.1k
    if (code < 0)
3961
1.00k
        F = 0;
3962
3963
11.1k
    if ((F & 0x2) != 0) { /* Bit 2 -- Hidden */
3964
518
        is_visible = false; /* Hidden */
3965
518
        goto exit;
3966
518
    }
3967
3968
10.5k
    if (ctx->args.printed) {
3969
        /* Even if Print flag (bit 3) is off, will print if 3D */
3970
10.5k
        is_visible = ((F & 0x4) != 0) || is_3D;
3971
10.5k
    } else {
3972
        /* Not NoView (bit 6) */
3973
0
        is_visible = (F & 0x20) == 0;
3974
0
    }
3975
3976
11.1k
 exit:
3977
11.1k
    return is_visible;
3978
10.5k
}
3979
3980
/* Check to see if subtype is on the ShowAnnotTypes list
3981
 * Defaults to true if there is no list
3982
 */
3983
static bool pdfi_annot_check_type(pdf_context *ctx, pdf_name *subtype)
3984
9.56k
{
3985
9.56k
    char **ptr;
3986
3987
    /* True if no list */
3988
9.56k
    if (!ctx->args.showannottypes)
3989
9.56k
        return true;
3990
3991
    /* Null terminated list, return true if subtype is on the list */
3992
0
    ptr = ctx->args.showannottypes;
3993
0
    while (*ptr) {
3994
0
        char *str = *ptr;
3995
0
        if (pdfi_name_is(subtype, str))
3996
0
            return true;
3997
0
        ptr ++;
3998
0
    }
3999
    /* List exists, but subtype is not on it */
4000
0
    return false;
4001
0
}
4002
4003
/* Check to see if subtype is on the PreserveAnnotTypes list
4004
 * Defaults to true if there is no list
4005
 */
4006
static bool pdfi_annot_preserve_type(pdf_context *ctx, pdf_name *subtype)
4007
0
{
4008
0
    char **ptr;
4009
4010
    /* True if no list */
4011
0
    if (!ctx->args.preserveannottypes)
4012
0
        return true;
4013
4014
    /* Null terminated list, return true if subtype is on the list */
4015
0
    ptr = ctx->args.preserveannottypes;
4016
0
    while (*ptr) {
4017
0
        char *str = *ptr;
4018
0
        if (pdfi_name_is(subtype, str))
4019
0
            return true;
4020
0
        ptr ++;
4021
0
    }
4022
    /* List exists, but subtype is not on it */
4023
0
    return false;
4024
0
}
4025
4026
static int pdfi_annot_draw(pdf_context *ctx, pdf_dict *annot, pdf_name *subtype)
4027
11.1k
{
4028
11.1k
    pdf_obj *NormAP = NULL;
4029
11.1k
    int code = 0;
4030
11.1k
    annot_dispatch_t *dispatch_ptr;
4031
11.1k
    bool render_done = true;
4032
4033
    /* See if annotation is visible */
4034
11.1k
    if (!pdfi_annot_visible(ctx, annot, subtype))
4035
1.53k
        goto exit;
4036
4037
    /* See if we are rendering this type of annotation */
4038
9.56k
    if (!pdfi_annot_check_type(ctx, subtype))
4039
0
        goto exit;
4040
4041
    /* Get the Normal AP, if it exists */
4042
9.56k
    code = pdfi_annot_get_NormAP(ctx, annot, &NormAP);
4043
9.56k
    if (code < 0) goto exit;
4044
4045
9.29k
    code = pdfi_gsave(ctx);
4046
9.29k
    if (code < 0) goto exit;
4047
4048
    /* Draw the annotation */
4049
156k
    for (dispatch_ptr = annot_dispatch; dispatch_ptr->subtype; dispatch_ptr ++) {
4050
156k
        if (pdfi_name_is(subtype, dispatch_ptr->subtype)) {
4051
9.29k
            if (NormAP && dispatch_ptr->simpleAP)
4052
1.59k
                render_done = false;
4053
7.69k
            else
4054
7.69k
                code = dispatch_ptr->func(ctx, annot, NormAP, &render_done);
4055
9.29k
            break;
4056
9.29k
        }
4057
156k
    }
4058
9.29k
    if (!dispatch_ptr->subtype) {
4059
6
        char str[100];
4060
6
        memcpy(str, (const char *)subtype->data, subtype->length < 100 ? subtype->length : 99);
4061
6
        str[subtype->length < 100 ? subtype->length : 99] = '\0';
4062
6
        dbgmprintf1(ctx->memory, "ANNOT: No handler for subtype %s\n", str);
4063
4064
        /* Not necessarily an error? We can just render the AP if there is one */
4065
6
        render_done = false;
4066
6
    }
4067
4068
9.29k
    if (!render_done)
4069
5.20k
        code = pdfi_annot_draw_AP(ctx, annot, NormAP);
4070
4071
9.29k
    (void)pdfi_grestore(ctx);
4072
4073
11.1k
 exit:
4074
11.1k
    pdfi_countdown(NormAP);
4075
11.1k
    return code;
4076
9.29k
}
4077
4078
/* Create a string containing form label
4079
 * I don't think the format actually matters, though it probably needs to be unique
4080
 * Just use a counter to ensure uniqueness
4081
 *
4082
 * Format: {FormName%d}
4083
 */
4084
static int pdfi_annot_preserve_nextformlabel(pdf_context *ctx, byte **data, int *len)
4085
0
{
4086
0
    int size = 40;
4087
0
    char *buf;
4088
0
    gs_id counter = gs_next_ids(ctx->memory, 1);
4089
4090
0
    buf = (char *)gs_alloc_bytes(ctx->memory, size, "pdfi_annot_preserve_nextformlabel(buf)");
4091
0
    if (buf == NULL)
4092
0
        return_error(gs_error_VMerror);
4093
0
    snprintf(buf, size, "{FN%ld}", counter);
4094
0
    *len = strlen(buf);
4095
0
    *data = (byte *)buf;
4096
0
    return 0;
4097
0
}
4098
4099
/* Modify the QuadPoints array to correct for some BS in the pdfwrite driver.
4100
 * comment from gs code (pdf_draw.ps/ApplyCMTToQuadPoints()):
4101
  %% Nasty hackery here really. We need to undo the HWResolution scaling which
4102
  %% is done by pdfwrite. Default is 720 dpi, so 0.1. We also need to make
4103
  %% sure that any translation of the page (because its been rotated for example)
4104
  %% is also modified by the requisite amount. SO we ned to calculate a matrix
4105
  %% which does the scaling and concatenate it with the current matrix.
4106
  %% Do this inside a gsave/grestore pair to avoid side effects!
4107
 *
4108
 * TODO: In practice, when I was debugging this code I found that it does nothing
4109
 * useful, since the matrice it calculates is essentially the identity matrix.
4110
 * I am not sure under what circumstance it is needed?
4111
 */
4112
static int pdfi_annot_preserve_modQP(pdf_context *ctx, pdf_dict *annot, pdf_name *QP_key)
4113
0
{
4114
0
    int code = 0;
4115
0
    pdf_array *QP = NULL;
4116
0
    gx_device *device = gs_currentdevice(ctx->pgs);
4117
0
    gs_matrix devmatrix, ctm, matrix;
4118
0
    uint64_t arraysize, index;
4119
0
    double old_x, old_y;
4120
0
    gs_point point;
4121
4122
0
    code = pdfi_dict_get(ctx, annot, "QuadPoints", (pdf_obj **)&QP);
4123
0
    if (code < 0) goto exit;
4124
4125
0
    if (pdfi_type_of(QP) != PDF_ARRAY) {
4126
        /* Invalid QuadPoints, just delete it because I dunno what to do...
4127
         * TODO: Should flag a warning here
4128
         */
4129
0
        code = pdfi_dict_delete_pair(ctx, annot, QP_key);
4130
0
        goto exit;
4131
0
    }
4132
4133
0
    arraysize = pdfi_array_size(QP);
4134
0
    if (arraysize % 8 != 0) {
4135
        /* TODO: Flag a warning -- must be sets of 8 values (4 points) */
4136
0
        code = gs_note_error(gs_error_syntaxerror);
4137
0
        goto exit;
4138
0
    }
4139
4140
4141
    /* Get device matrix and adjust by default 72.0 (no idea, just following PS code...) */
4142
0
    devmatrix.xx = 72.0 / device->HWResolution[0];
4143
0
    devmatrix.xy = 0;
4144
0
    devmatrix.yx = 0;
4145
0
    devmatrix.yy = 72.0 / device->HWResolution[1];
4146
0
    devmatrix.tx = 0;
4147
0
    devmatrix.ty = 0;
4148
4149
    /* Get the CTM */
4150
0
    gs_currentmatrix(ctx->pgs, &ctm);
4151
4152
    /* Get matrix to adjust the QuadPoints */
4153
0
    code = gs_matrix_multiply(&ctm, &devmatrix, &matrix);
4154
0
    if (code < 0) goto exit;
4155
4156
    /* Transform all the points by the calculated matrix */
4157
0
    for (index = 0; index < arraysize; index += 2) {
4158
0
        code = pdfi_array_get_number(ctx, QP, index, &old_x);
4159
0
        if (code < 0) goto exit;
4160
0
        code = pdfi_array_get_number(ctx, QP, index+1, &old_y);
4161
0
        if (code < 0) goto exit;
4162
4163
0
        code = gs_point_transform(old_x, old_y, &matrix, &point);
4164
0
        if (code < 0) goto exit;
4165
4166
0
        code = pdfi_array_put_real(ctx, QP, index, point.x);
4167
0
        if (code < 0) goto exit;
4168
0
        code = pdfi_array_put_real(ctx, QP, index+1, point.y);
4169
0
        if (code < 0) goto exit;
4170
0
    }
4171
4172
4173
0
 exit:
4174
0
    pdfi_countdown(QP);
4175
0
    return code;
4176
0
}
4177
4178
/* Build a high level form for the AP and replace it with an indirect reference
4179
 *
4180
 * There can multiple AP in the dictionary, so do all of them.
4181
 */
4182
static int pdfi_annot_preserve_modAP(pdf_context *ctx, pdf_dict *annot, pdf_name *AP_key)
4183
0
{
4184
0
    int code = 0;
4185
0
    pdf_dict *AP = NULL;
4186
0
    uint64_t index;
4187
0
    pdf_name *Key = NULL;
4188
0
    pdf_indirect_ref *Value = NULL;
4189
0
    byte *labeldata = NULL;
4190
0
    int labellen;
4191
0
    int form_id;
4192
0
    gx_device *device = gs_currentdevice(ctx->pgs);
4193
0
    bool found_ap = false; /* found at least one AP stream? */
4194
0
    pdf_obj *object = NULL;
4195
4196
0
    code = pdfi_dict_get(ctx, annot, "AP", (pdf_obj **)&AP);
4197
0
    if (code < 0) goto exit;
4198
4199
0
    if (pdfi_type_of(AP) != PDF_DICT) {
4200
        /* This is an invalid AP, we will flag and delete it below */
4201
0
        found_ap = false;
4202
0
        goto exit;
4203
0
    }
4204
4205
0
    code = pdfi_dict_key_first(ctx, AP, (pdf_obj **)&Key, &index);
4206
0
    while (code >= 0) {
4207
0
        found_ap = true;
4208
0
        code = pdfi_dict_get_no_deref(ctx, AP, Key, (pdf_obj **)&Value);
4209
0
        if (code < 0) goto exit;
4210
4211
        /* Handle indirect object */
4212
0
        if (pdfi_type_of(Value) != PDF_INDIRECT)
4213
0
            goto loop_continue;
4214
4215
        /* Dereference it */
4216
0
        code = pdfi_dereference(ctx, Value->ref_object_num, Value->ref_generation_num, &object);
4217
0
        if (code < 0) goto exit;
4218
4219
0
        if (pdfi_type_of(object) == PDF_STREAM) {
4220
            /* Get a form label */
4221
0
            code = pdfi_annot_preserve_nextformlabel(ctx, &labeldata, &labellen);
4222
0
            if (code < 0) goto exit;
4223
4224
            /* Notify the driver of the label name */
4225
0
            code = (*dev_proc(device, dev_spec_op))
4226
0
                (device, gxdso_pdf_form_name, labeldata, labellen);
4227
4228
0
            code = pdfi_op_q(ctx);
4229
0
            if (code < 0)
4230
0
                goto exit;
4231
4232
0
            code = pdfi_annot_position_AP(ctx, annot, (pdf_stream *)object);
4233
            /* Draw the high-level form */
4234
0
            code = pdfi_do_highlevel_form(ctx, ctx->page.CurrentPageDict, (pdf_stream *)object);
4235
0
            (void)pdfi_op_Q(ctx);
4236
0
            if (code < 0) goto exit;
4237
4238
            /* Get the object number (form_id) of the high level form */
4239
0
            code = (*dev_proc(device, dev_spec_op))
4240
0
                (device, gxdso_get_form_ID, &form_id, sizeof(int));
4241
4242
            /* Save the highlevel form info for pdfi_obj_indirect_str() */
4243
0
            Value->highlevel_object_num = form_id;
4244
0
            Value->is_highlevelform = true;
4245
4246
0
        }
4247
4248
0
    loop_continue:
4249
0
        pdfi_countdown(Key);
4250
0
        Key = NULL;
4251
0
        pdfi_countdown(Value);
4252
0
        Value = NULL;
4253
0
        pdfi_countdown(object);
4254
0
        object = NULL;
4255
0
        gs_free_object(ctx->memory, labeldata, "pdfi_annot_preserve_modAP(labeldata)");
4256
0
        labeldata = NULL;
4257
4258
0
        code = pdfi_dict_key_next(ctx, AP, (pdf_obj **)&Key, &index);
4259
0
        if (code == gs_error_undefined) {
4260
0
            code = 0;
4261
0
            break;
4262
0
        }
4263
0
    }
4264
0
    if (code < 0) goto exit;
4265
4266
0
 exit:
4267
    /* If there was no AP found, then delete the key completely.
4268
     * (Bug697951.pdf)
4269
     */
4270
0
    if (!found_ap) {
4271
        /* TODO: Flag a warning for broken file? */
4272
0
        code = pdfi_dict_delete_pair(ctx, annot, AP_key);
4273
0
    }
4274
4275
0
    if (labeldata)
4276
0
        gs_free_object(ctx->memory, labeldata, "pdfi_annot_preserve_modAP(labeldata)");
4277
0
    pdfi_countdown(AP);
4278
0
    pdfi_countdown(Key);
4279
0
    pdfi_countdown(Value);
4280
0
    pdfi_countdown(object);
4281
0
    return code;
4282
0
}
4283
4284
/* Make a temporary copy of the annotation dict with some fields left out or
4285
 * modified, then do a pdfmark on it
4286
 */
4287
4288
const char *PermittedKeys[] = {
4289
    /* These keys are valid for all annotation types, we specifically do not allow /P or /Parent */
4290
    "Type",
4291
    "Subtype",
4292
    "Rect",
4293
    "Contents",
4294
    "NM",
4295
    "M",
4296
    "F",
4297
    "AP",
4298
    "AS",
4299
    "Border",
4300
    "C",
4301
    "StructParent",
4302
    "OC",
4303
    "AF",
4304
    "ca",
4305
    "CA",
4306
    "BM",
4307
    "Lang",
4308
    /* Keys by annotation type (some are common to more than one type, only one entry per key) */
4309
    /* Markup Annotations we specifically do not permit RT, IRT or Popup */
4310
    "T",
4311
    "RC",
4312
    "CreationDate",
4313
    "Subj",
4314
    "IT",
4315
    "ExData",
4316
    /* Text annotations */
4317
    "Open",
4318
    "Name",
4319
    "State",
4320
    "StateModel",
4321
    /* This isn't specified as being allowed, but Acrobat does something with it, so we need to preserve it */
4322
    "Rotate",
4323
    /* Link annotations */
4324
    "A",
4325
    "Dest",
4326
    "H",
4327
    "PA",
4328
    "QuadPoints",
4329
    /* FreeText annotations */
4330
    "DA",
4331
    "Q",
4332
    "DS",
4333
    "CL",
4334
    "IT",
4335
    "BE",
4336
    "RD",
4337
    "BS",
4338
    "LE",
4339
    /* Line Annotations */
4340
    "L",
4341
    "LE",
4342
    "IC",
4343
    "LL",
4344
    "LLE",
4345
    "Cap",
4346
    "LLO",
4347
    "CP",
4348
    "Measure",
4349
    "CO",
4350
    /* Square and Circle annotations */
4351
    "Path",
4352
    /* Polygon and PolyLine annotations */
4353
    "Vertices",
4354
    /* Text Markup annotations */
4355
    /* Caret annotations */
4356
    "Sy",
4357
    /* Rubber Stamp annotations */
4358
    /* Ink annotations */
4359
    "InkList",
4360
    /* Popup annotations */
4361
    "Open",
4362
    /* File attachment annotation */
4363
    "FS",
4364
    /* Sound annotations */
4365
    "Sound",
4366
    /* Movie annotations */
4367
    "Movie",
4368
    /* Screen annotations */
4369
    "MK",
4370
    "AA",
4371
    /* We don't handle Widget annotations as annotations, we draw them */
4372
    /* Printer's Mark annotations */
4373
    /* Trap Network annotations */
4374
    /* Watermark annotations */
4375
    "FixedPrint",
4376
    "Matrix",
4377
    "H",
4378
    "V",
4379
    /* Redaction annotations */
4380
    "RO",
4381
    "OverlayText",
4382
    "Repeat",
4383
    /* Projection annotations */
4384
    /* 3D and RichMedia annotations */
4385
};
4386
4387
static int isKnownKey(pdf_context *ctx, pdf_name *Key)
4388
0
{
4389
0
    int i = 0;
4390
4391
0
    for (i = 0; i < sizeof(PermittedKeys) / sizeof (const char *); i++) {
4392
0
        if (pdfi_name_is(Key, PermittedKeys[i]))
4393
0
            return 1;
4394
0
    }
4395
0
    return 0;
4396
0
}
4397
4398
static int pdfi_annot_preserve_mark(pdf_context *ctx, pdf_dict *annot, pdf_name *subtype)
4399
0
{
4400
0
    int code = 0;
4401
0
    gs_matrix ctm;
4402
0
    pdf_dict *tempdict = NULL;
4403
0
    uint64_t dictsize;
4404
0
    uint64_t index;
4405
0
    pdf_name *Key = NULL;
4406
0
    pdf_obj *Value = NULL;
4407
0
    bool resolve = false;
4408
4409
    /* Create a temporary copy of the annot dict */
4410
0
    dictsize = pdfi_dict_entries(annot);
4411
0
    code = pdfi_dict_alloc(ctx, dictsize, &tempdict);
4412
0
    if (code < 0) goto exit;
4413
0
    pdfi_countup(tempdict);
4414
0
    code = pdfi_dict_copy(ctx, tempdict, annot);
4415
0
    if (code < 0) goto exit;
4416
4417
4418
     /* see also: 'loadannot' in gs code */
4419
    /* Go through the dict, deleting and modifying some entries
4420
     * Note that I am iterating through the original dict because I will
4421
     * be deleting some keys from tempdict and the iterators wouldn't work right.
4422
     */
4423
0
    code = pdfi_dict_key_first(ctx, annot, (pdf_obj **)&Key, &index);
4424
0
    while (code >= 0) {
4425
0
        resolve = false;
4426
4427
0
        if (!isKnownKey(ctx, Key)) {
4428
0
            code = pdfi_dict_delete_pair(ctx, tempdict, Key);
4429
0
            if (code < 0) goto exit;
4430
0
        } else {
4431
0
            if (pdfi_name_is(Key, "AP")) {
4432
                /* Special handling for AP -- have fun! */
4433
0
                code = pdfi_annot_preserve_modAP(ctx, tempdict, Key);
4434
0
                if (code < 0) goto exit;
4435
0
            } else if (pdfi_name_is(Key, "QuadPoints")) {
4436
0
                code = pdfi_annot_preserve_modQP(ctx, tempdict, Key);
4437
0
                if (code < 0) goto exit;
4438
0
            } else if (pdfi_name_is(Key, "A")) {
4439
0
                code = pdfi_pdfmark_modA(ctx, tempdict);
4440
0
                if (code < 0) goto exit;
4441
0
            } else if (pdfi_name_is(Key, "Dest")) {
4442
0
                if (ctx->args.no_pdfmark_dests) {
4443
                    /* If omitting dests, such as for multi-page output, then omit this whole annotation */
4444
0
                    code = 0;
4445
0
                    goto exit;
4446
0
                }
4447
0
                code = pdfi_pdfmark_modDest(ctx, tempdict);
4448
0
                if (code < 0) goto exit;
4449
0
            } else if (pdfi_name_is(Key, "StructTreeRoot")) {
4450
                /* TODO: Bug691785 has Link annots with /StructTreeRoot
4451
                 * It is super-circular, and causes issues.
4452
                 * GS code only adds in certain values for Link so it doesn't
4453
                 * run into a problem.  I am just going to delete it.
4454
                 * There should be a better solution to handle circular stuff
4455
                 * generically.
4456
                 */
4457
0
                code = pdfi_dict_delete_pair(ctx, tempdict, Key);
4458
0
                if (code < 0) goto exit;
4459
0
            } else if (pdfi_name_is(Key, "Sound") || pdfi_name_is(Key, "Movie")) {
4460
0
                resolve = false;
4461
0
            } else {
4462
0
                resolve = true;
4463
0
            }
4464
0
        }
4465
0
        if (resolve) {
4466
0
            code = pdfi_dict_get_by_key(ctx, annot, (const pdf_name *)Key, &Value);
4467
0
            if (code < 0) goto exit;
4468
4469
            /* Pre-resolve indirect references of any arrays/dicts
4470
             * TODO: I am doing this because the gs code does it, but I
4471
             * noticed it only goes down one level, not recursively through
4472
             * everything.  I am not sure what the point is in that case.
4473
             * For now, also doing only one level.
4474
             */
4475
0
            code = pdfi_resolve_indirect_loop_detect(ctx, (pdf_obj *)annot, Value, false);
4476
0
            if (code < 0) goto exit;
4477
0
        }
4478
4479
0
        pdfi_countdown(Key);
4480
0
        Key = NULL;
4481
0
        pdfi_countdown(Value);
4482
0
        Value = NULL;
4483
4484
0
        code = pdfi_dict_key_next(ctx, annot, (pdf_obj **)&Key, &index);
4485
0
        if (code == gs_error_undefined) {
4486
0
            code = 0;
4487
0
            break;
4488
0
        }
4489
0
    }
4490
0
    if (code < 0) goto exit;
4491
4492
    /* Do pdfmark from the tempdict */
4493
0
    gs_currentmatrix(ctx->pgs, &ctm);
4494
4495
0
    if (pdfi_name_is(subtype, "Link"))
4496
0
        code = pdfi_pdfmark_from_dict(ctx, tempdict, &ctm, "LNK");
4497
0
    else
4498
0
        code = pdfi_pdfmark_from_dict(ctx, tempdict, &ctm, "ANN");
4499
0
    if (code < 0) goto exit;
4500
4501
0
 exit:
4502
0
    pdfi_countdown(tempdict);
4503
0
    pdfi_countdown(Key);
4504
0
    pdfi_countdown(Value);
4505
0
    return code;
4506
0
}
4507
4508
static int pdfi_annot_preserve_default(pdf_context *ctx, pdf_dict *annot, pdf_name *subtype)
4509
0
{
4510
0
    return pdfi_annot_preserve_mark(ctx, annot, subtype);
4511
0
}
4512
4513
static int pdfi_annot_preserve_Link(pdf_context *ctx, pdf_dict *annot, pdf_name *subtype)
4514
0
{
4515
0
    int code = 0;
4516
4517
    /* TODO: The gs code does a whole bunch of stuff I don't understand yet.
4518
     * I think doing the default behavior might work in most cases?
4519
     * See: pdf_draw.ps/preserveannottypes dict/Link()
4520
     */
4521
0
    code = pdfi_annot_preserve_mark(ctx, annot, subtype);
4522
0
    return code;
4523
0
}
4524
4525
static int pdfi_annot_preserve_Widget(pdf_context *ctx, pdf_dict *annot, pdf_name *subtype)
4526
0
{
4527
    /* According to the gs implementation:
4528
%% Widget annotations are only used with AcroForms, and since we don't preserve AcroForms
4529
%% we don't want to preserve widget annotations either, because the consumer of the new
4530
%% PDF won't know what values they should take. So we draw widget annotations instead. If we
4531
%% ever preserve AcroForms then we should alter this to preserve Widgets as well.
4532
     *
4533
     * TODO: See also, comment on pdfi_annot_render_field().
4534
     */
4535
4536
0
    return pdfi_annot_draw(ctx, annot, subtype);
4537
0
}
4538
4539
/* TODO: When I started writing this, I thought these handlers were necessary because
4540
 * the PS code has a bunch of special cases.  As it turns out, I think it could easily have
4541
 * been done without the handlers.   Maybe they should be taken out...
4542
 */
4543
annot_preserve_dispatch_t annot_preserve_dispatch[] = {
4544
    {"Link", pdfi_annot_preserve_Link},
4545
    {"Widget", pdfi_annot_preserve_Widget},
4546
    {"Circle", pdfi_annot_preserve_default},
4547
    {"FileAttachment", pdfi_annot_preserve_default},
4548
    {"FreeText", pdfi_annot_preserve_default},
4549
    {"Highlight", pdfi_annot_preserve_default},
4550
    {"Ink", pdfi_annot_preserve_default},
4551
    {"Line", pdfi_annot_preserve_default},
4552
    {"Movie", pdfi_annot_preserve_default},
4553
    {"PolyLine", pdfi_annot_preserve_default},
4554
    {"Popup", pdfi_annot_preserve_default},
4555
    /*    {"Screen", pdfi_annot_preserve_default},  /* TODO: fts_07_0709.pdf */
4556
    {"Sound", pdfi_annot_preserve_default},
4557
    {"Square", pdfi_annot_preserve_default},
4558
    {"Squiggly", pdfi_annot_preserve_default},
4559
    {"StrikeOut", pdfi_annot_preserve_default},
4560
    {"Underline", pdfi_annot_preserve_default},
4561
    {"Stamp", pdfi_annot_preserve_default},
4562
    {"Text", pdfi_annot_preserve_default},
4563
    {"TrapNet", pdfi_annot_preserve_default},
4564
    { NULL, NULL},
4565
};
4566
4567
static int pdfi_annot_preserve(pdf_context *ctx, pdf_dict *annot, pdf_name *subtype)
4568
0
{
4569
0
    int code = 0;
4570
0
    annot_preserve_dispatch_t *dispatch_ptr;
4571
4572
    /* If not preserving this subtype, draw it instead */
4573
0
    if (!pdfi_annot_preserve_type(ctx, subtype))
4574
0
        return pdfi_annot_draw(ctx, annot, subtype);
4575
4576
    /* Handle the annotation */
4577
0
    for (dispatch_ptr = annot_preserve_dispatch; dispatch_ptr->subtype; dispatch_ptr ++) {
4578
0
        if (pdfi_name_is(subtype, dispatch_ptr->subtype)) {
4579
0
            code = dispatch_ptr->func(ctx, annot, subtype);
4580
0
            break;
4581
0
        }
4582
0
    }
4583
4584
    /* If there is no handler, just draw it */
4585
    /* NOTE: gs does a drawwidget here instead (?) */
4586
0
    if (!dispatch_ptr->subtype)
4587
0
        code = pdfi_annot_draw(ctx, annot, subtype);
4588
4589
0
    return code;
4590
0
}
4591
4592
static int pdfi_annot_handle(pdf_context *ctx, pdf_dict *annot)
4593
11.1k
{
4594
11.1k
    int code = 0;
4595
11.1k
    pdf_name *Subtype = NULL;
4596
4597
11.1k
    code = pdfi_dict_get_type(ctx, annot, "Subtype", PDF_NAME, (pdf_obj **)&Subtype);
4598
11.1k
    if (code != 0) {
4599
        /* TODO: Set warning flag */
4600
25
        dbgmprintf(ctx->memory, "WARNING: Ignoring annotation with missing Subtype\n");
4601
25
        code = 0;
4602
25
        goto exit;
4603
25
    }
4604
4605
11.1k
    if (ctx->args.preserveannots && ctx->device_state.annotations_preserved)
4606
0
        code = pdfi_annot_preserve(ctx, annot, Subtype);
4607
11.1k
    else
4608
11.1k
        code = pdfi_annot_draw(ctx, annot, Subtype);
4609
4610
11.1k
 exit:
4611
11.1k
    pdfi_countdown(Subtype);
4612
11.1k
    if (code < 0) {
4613
974
        code = pdfi_set_error_stop(ctx, code, NULL, E_PDF_BAD_ANNOTATION, "pdfi_annot_handle", "Error handling annotation");
4614
974
    }
4615
11.1k
    return code;
4616
11.1k
}
4617
4618
int pdfi_do_annotations(pdf_context *ctx, pdf_dict *page_dict)
4619
6.07k
{
4620
6.07k
    int code = 0;
4621
6.07k
    pdf_array *Annots = NULL;
4622
6.07k
    pdf_dict *annot = NULL;
4623
6.07k
    int i;
4624
4625
6.07k
    if (!ctx->args.showannots)
4626
0
        return 0;
4627
4628
6.07k
    code = pdfi_dict_knownget_type(ctx, page_dict, "Annots", PDF_ARRAY, (pdf_obj **)&Annots);
4629
6.07k
    if (code <= 0)
4630
4.84k
        return code;
4631
4632
16.0k
    for (i = 0; i < pdfi_array_size(Annots); i++) {
4633
14.8k
        code = pdfi_array_get_type(ctx, Annots, i, PDF_DICT, (pdf_obj **)&annot);
4634
14.8k
        if (code < 0) {
4635
3.72k
            if ((code = pdfi_set_warning_stop(ctx, code, NULL, W_PDF_ANNOT_BAD_TYPE, "pdfi_do_annotations", "")) < 0)
4636
0
                goto exit;
4637
3.72k
            continue;
4638
3.72k
        }
4639
11.1k
        code = pdfi_annot_handle(ctx, annot);
4640
11.1k
        if (code < 0 &&
4641
11.1k
           ((code = pdfi_set_error_stop(ctx, code, NULL, E_PDF_BAD_ANNOTATION, "pdfi_annot_handle", "Error handling annotation")) < 0)) {
4642
0
            goto exit;
4643
0
        }
4644
11.1k
        pdfi_countdown(annot);
4645
11.1k
        annot = NULL;
4646
11.1k
    }
4647
4648
1.22k
 exit:
4649
1.22k
    pdfi_countdown(annot);
4650
1.22k
    pdfi_countdown(Annots);
4651
1.22k
    return code;
4652
1.22k
}
4653
4654
/* draw terminal field */
4655
static int pdfi_form_draw_terminal(pdf_context *ctx, pdf_dict *Page, pdf_dict *field)
4656
0
{
4657
0
    int code = 0;
4658
0
    pdf_indirect_ref *P = NULL;
4659
0
    pdf_name *FT = NULL;
4660
0
    pdf_obj *AP = NULL;
4661
4662
    /* See if the field goes on this page */
4663
    /* NOTE: We know the "P" is an indirect ref, so just fetch it that way.
4664
     * If we fetch the actual object, it will result in a cyclical reference in the cache
4665
     * that causes a memory leak, so don't do that.
4666
     * (The cyclical reference is because the object containing P is actually inside P)
4667
     */
4668
0
    code = pdfi_dict_get_ref(ctx, field, "P", &P);
4669
0
    if (code < 0) {
4670
0
        if (code == gs_error_undefined)
4671
0
            code = 0;
4672
0
        goto exit;
4673
0
    }
4674
4675
0
    if (P->ref_object_num != Page->object_num) {
4676
        /* Not this page */
4677
0
        code = 0;
4678
0
        goto exit;
4679
0
    }
4680
4681
    /* Render the field */
4682
4683
    /* NOTE: The spec says the FT is inheritable, implying it might be present in
4684
     * a Parent and not explicitly in a Child.  The gs implementation doesn't seem
4685
     * to handle this case, but I am going to go ahead and handle it.  We will figure
4686
     * out if this causes diffs later, if ever...
4687
     */
4688
0
    code = pdfi_form_get_inheritable(ctx, field, "FT", PDF_NAME, (pdf_obj **)&FT);
4689
0
    if (code <= 0) goto exit;
4690
4691
0
    code = pdfi_annot_get_NormAP(ctx, field, &AP);
4692
0
    if (code < 0) goto exit;
4693
4694
0
    code = pdfi_annot_render_field(ctx, field, FT, AP);
4695
0
    if (code < 0) goto exit;
4696
4697
0
 exit:
4698
0
    pdfi_countdown(FT);
4699
0
    pdfi_countdown(P);
4700
0
    pdfi_countdown(AP);
4701
0
    return code;
4702
0
}
4703
4704
/* From pdf_draw.ps/draw_form_field():
4705
% We distinguish 4 types of nodes on the form field tree:
4706
%  - non-terminal field - has a kid that refers to the parent (or anywhere else)
4707
%  - terminal field with separate widget annotations - has a kid that doesn't have a parent
4708
%  - terminal field with a merged widget annotation - has no kids
4709
%  - widget annotation - has /Subtype and /Rect
4710
%
4711
% The recursive enumeration of the form fields doesn't descend into widget annotations.
4712
*/
4713
static int pdfi_form_draw_field(pdf_context *ctx, pdf_dict *Page, pdf_dict *field)
4714
0
{
4715
0
    int code = 0;
4716
0
    pdf_array *Kids = NULL;
4717
0
    pdf_dict *child = NULL;
4718
0
    pdf_dict *Parent = NULL;
4719
0
    int i;
4720
4721
0
    code = pdfi_dict_knownget_type(ctx, field, "Kids", PDF_ARRAY, (pdf_obj **)&Kids);
4722
0
    if (code < 0) goto exit;
4723
0
    if (code == 0) {
4724
0
        code = pdfi_form_draw_terminal(ctx, Page, field);
4725
0
        goto exit;
4726
0
    }
4727
4728
    /* Handle Kids */
4729
0
    if (pdfi_array_size(Kids) <= 0) {
4730
0
        code = pdfi_set_error_stop(ctx, gs_note_error(gs_error_undefined), NULL, E_PDF_EMPTY_FORM_KIDS, "pdfi_form_draw_field", "");
4731
0
        goto exit;
4732
0
    }
4733
4734
    /* Check first child to see if it has a parent */
4735
0
    code = pdfi_array_get_type(ctx, Kids, 0, PDF_DICT, (pdf_obj **)&child);
4736
0
    if (code < 0) goto exit;
4737
0
    code = pdfi_dict_knownget_type(ctx, child, "Parent", PDF_DICT, (pdf_obj **)&Parent);
4738
0
    if (code < 0) goto exit;
4739
4740
    /* If kid has no parent, then treat this as terminal field */
4741
0
    if (code == 0) {
4742
        /* TODO: This case isn't tested because no examples available.
4743
         * I think it's only relevant for Btn, and not sure if it
4744
         * should be dealt with here or in Btn routine.
4745
         */
4746
0
        code = pdfi_form_draw_terminal(ctx, Page, field);
4747
0
        goto exit;
4748
0
    }
4749
4750
0
    pdfi_countdown(child);
4751
0
    child = NULL;
4752
    /* Render the Kids (recursive) */
4753
0
    for (i=0; i<pdfi_array_size(Kids); i++) {
4754
0
        code = pdfi_array_get_type(ctx, Kids, i, PDF_DICT, (pdf_obj **)&child);
4755
0
        if (code < 0) goto exit;
4756
4757
0
        code = pdfi_form_draw_field(ctx, Page, child);
4758
0
        if (code < 0) goto exit;
4759
4760
0
        pdfi_countdown(child);
4761
0
        child = NULL;
4762
0
    }
4763
4764
0
 exit:
4765
0
    pdfi_countdown(child);
4766
0
    pdfi_countdown(Kids);
4767
0
    pdfi_countdown(Parent);
4768
0
    return code;
4769
0
}
4770
4771
int pdfi_do_acroform(pdf_context *ctx, pdf_dict *page_dict)
4772
6.07k
{
4773
6.07k
    int code = 0;
4774
6.07k
    pdf_array *Fields = NULL;
4775
6.07k
    pdf_dict *field = NULL;
4776
6.07k
    int i;
4777
4778
6.07k
    if (!ctx->args.showacroform)
4779
6.07k
        return 0;
4780
4781
0
    if (!ctx->AcroForm)
4782
0
        return 0;
4783
4784
0
    code = pdfi_dict_knownget_type(ctx, ctx->AcroForm, "Fields", PDF_ARRAY, (pdf_obj **)&Fields);
4785
0
    if (code <= 0)
4786
0
        goto exit;
4787
4788
0
    for (i=0; i<pdfi_array_size(Fields); i++) {
4789
0
        code = pdfi_array_get_type(ctx, Fields, i, PDF_DICT, (pdf_obj **)&field);
4790
0
        if (code < 0)
4791
0
            continue;
4792
0
        code = pdfi_form_draw_field(ctx, page_dict, field);
4793
0
        if (code < 0)
4794
0
            goto exit;
4795
0
        pdfi_countdown(field);
4796
0
        field = NULL;
4797
0
    }
4798
4799
0
 exit:
4800
0
    pdfi_countdown(field);
4801
0
    pdfi_countdown(Fields);
4802
0
    return code;
4803
0
}