Coverage Report

Created: 2026-07-30 07:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/poppler/poppler/Annot.cc
Line
Count
Source
1
//========================================================================
2
//
3
// Annot.cc
4
//
5
// Copyright 2000-2003 Glyph & Cog, LLC
6
//
7
//========================================================================
8
9
//========================================================================
10
//
11
// Modified under the Poppler project - http://poppler.freedesktop.org
12
//
13
// All changes made under the Poppler project to this file are licensed
14
// under GPL version 2 or later
15
//
16
// Copyright (C) 2006 Scott Turner <scotty1024@mac.com>
17
// Copyright (C) 2007, 2008 Julien Rebetez <julienr@svn.gnome.org>
18
// Copyright (C) 2007-2013, 2015-2026 Albert Astals Cid <aacid@kde.org>
19
// Copyright (C) 2007-2013, 2018 Carlos Garcia Campos <carlosgc@gnome.org>
20
// Copyright (C) 2007, 2008 Iñigo Martínez <inigomartinez@gmail.com>
21
// Copyright (C) 2007 Jeff Muizelaar <jeff@infidigm.net>
22
// Copyright (C) 2008, 2011 Pino Toscano <pino@kde.org>
23
// Copyright (C) 2008 Michael Vrable <mvrable@cs.ucsd.edu>
24
// Copyright (C) 2008 Hugo Mercier <hmercier31@gmail.com>
25
// Copyright (C) 2009 Ilya Gorenbein <igorenbein@finjan.com>
26
// Copyright (C) 2011, 2013, 2019 José Aliste <jaliste@src.gnome.org>
27
// Copyright (C) 2012, 2013 Fabio D'Urso <fabiodurso@hotmail.it>
28
// Copyright (C) 2012, 2013 Thomas Freitag <Thomas.Freitag@alfa.de>
29
// Copyright (C) 2012, 2015 Tobias Koenig <tokoe@kdab.com>
30
// Copyright (C) 2013 Peter Breitenlohner <peb@mppmu.mpg.de>
31
// Copyright (C) 2013, 2017 Adrian Johnson <ajohnson@redneon.com>
32
// Copyright (C) 2014, 2015 Marek Kasik <mkasik@redhat.com>
33
// Copyright (C) 2014 Jiri Slaby <jirislaby@gmail.com>
34
// Copyright (C) 2014 Anuj Khare <khareanuj18@gmail.com>
35
// Copyright (C) 2015 Petr Gajdos <pgajdos@suse.cz>
36
// Copyright (C) 2015 Philipp Reinkemeier <philipp.reinkemeier@offis.de>
37
// Copyright (C) 2015 Tamas Szekeres <szekerest@gmail.com>
38
// Copyright (C) 2017 Hans-Ulrich Jüttner <huj@froreich-bioscientia.de>
39
// Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, <info@kdab.com>. Work sponsored by the LiMux project of the city of Munich
40
// Copyright 2018 Andre Heinecke <aheinecke@intevation.de>
41
// Copyright (C) 2018 Adam Reichold <adam.reichold@t-online.de>
42
// Copyright (C) 2018 Dileep Sankhla <sankhla.dileep96@gmail.com>
43
// Copyright (C) 2018-2020 Tobias Deiminger <haxtibal@posteo.de>
44
// Copyright (C) 2018-2020, 2022, 2024 Oliver Sander <oliver.sander@tu-dresden.de>
45
// Copyright (C) 2019 Umang Malik <umang99m@gmail.com>
46
// Copyright (C) 2019 João Netto <joaonetto901@gmail.com>
47
// Copyright (C) 2020, 2024 Klarälvdalens Datakonsult AB, a KDAB Group company, <info@kdab.com>. Work sponsored by Technische Universität Dresden
48
// Copyright (C) 2020 Katarina Behrens <Katarina.Behrens@cib.de>
49
// Copyright (C) 2020 Thorsten Behrens <Thorsten.Behrens@CIB.de>
50
// Copyright (C) 2020, 2025 Nelson Benítez León <nbenitezl@gmail.com>
51
// Copyright (C) 2021 Klarälvdalens Datakonsult AB, a KDAB Group company, <info@kdab.com>.
52
// Copyright (C) 2021 Zachary Travis <ztravis@everlaw.com>
53
// Copyright (C) 2021 Mahmoud Ahmed Khalil <mahmoudkhalil11@gmail.com>
54
// Copyright (C) 2021 Georgiy Sgibnev <georgiy@sgibnev.com>. Work sponsored by lab50.net.
55
// Copyright (C) 2022 Martin <martinbts@gmx.net>
56
// Copyright (C) 2022 Andreas Naumann <42870-ANaumann85@users.noreply.gitlab.freedesktop.org>
57
// Copyright (C) 2022, 2024 Erich E. Hoover <erich.e.hoover@gmail.com>
58
// Copyright (C) 2023-2026 g10 Code GmbH, Author: Sune Stolborg Vuorela <sune@vuorela.dk>
59
// Copyright (C) 2024 Pratham Gandhi <ppg.1382@gmail.com>
60
// Copyright (C) 2024 Carsten Emde <ce@ceek.de>
61
// Copyright (C) 2024-2026 Lucas Baudin <lucas.baudin@ensae.fr>
62
// Copyright (C) 2025 Juraj Šarinay <juraj@sarinay.com>
63
// Copyright (C) 2025 Aditya Tiwari <suntiwari3495@gmail.com>
64
// Copyright (C) 2025 Arnav V <arnav0872@gmail.com>
65
// Copyright (C) 2026 Adam Sampson <ats@offog.org>
66
// Copyright (C) 2026 Stefan Brüns <stefan.bruens@rwth-aachen.de>
67
//
68
// To see a description of the changes please see the Changelog file that
69
// came with your tarball or type make ChangeLog if you are building from git
70
//
71
//========================================================================
72
73
#include <config.h>
74
75
#include <cstdlib>
76
#include <cmath>
77
#include <cassert>
78
#include <numbers>
79
#include "goo/gstrtod.h"
80
#include "Error.h"
81
#include "Object.h"
82
#include "Catalog.h"
83
#include "Gfx.h"
84
#include "Lexer.h"
85
#include "PDFDoc.h"
86
#include "Page.h"
87
#include "Annot.h"
88
#include "GfxFont.h"
89
#include "CharCodeToUnicode.h"
90
#include "PDFDocEncoding.h"
91
#include "Form.h"
92
#include "XRef.h"
93
#include "Movie.h"
94
#include "OptionalContent.h"
95
#include "Sound.h"
96
#include "DateInfo.h"
97
#include "Link.h"
98
#include "UTF.h"
99
#include <cstring>
100
#include <algorithm>
101
#include <utility>
102
103
#include "annot_stamp_approved.h"
104
#include "annot_stamp_as_is.h"
105
#include "annot_stamp_confidential.h"
106
#include "annot_stamp_departmental.h"
107
#include "annot_stamp_final.h"
108
#include "annot_stamp_for_comment.h"
109
#include "annot_stamp_experimental.h"
110
#include "annot_stamp_expired.h"
111
#include "annot_stamp_not_approved.h"
112
#include "annot_stamp_not_for_public_release.h"
113
#include "annot_stamp_sold.h"
114
#include "annot_stamp_top_secret.h"
115
#include "annot_stamp_for_public_release.h"
116
#include "annot_stamp_draft.h"
117
118
// distance of Bezier control point from center for circle approximation
119
// TODO uncomment when we can depend on C++26
120
// constexpr double bezierCircle = (4 * (sqrt(2) - 1) / 3);
121
constexpr double bezierCircle = 0.55228475;
122
123
static AnnotLineEndingStyle parseAnnotLineEndingStyle(const Object &name)
124
0
{
125
0
    if (!name.isName()) {
126
0
        return annotLineEndingNone;
127
0
    }
128
129
0
    if (name.isName("Square")) {
130
0
        return annotLineEndingSquare;
131
0
    }
132
0
    if (name.isName("Circle")) {
133
0
        return annotLineEndingCircle;
134
0
    }
135
0
    if (name.isName("Diamond")) {
136
0
        return annotLineEndingDiamond;
137
0
    }
138
0
    if (name.isName("OpenArrow")) {
139
0
        return annotLineEndingOpenArrow;
140
0
    }
141
0
    if (name.isName("ClosedArrow")) {
142
0
        return annotLineEndingClosedArrow;
143
0
    }
144
0
    if (name.isName("Butt")) {
145
0
        return annotLineEndingButt;
146
0
    }
147
0
    if (name.isName("ROpenArrow")) {
148
0
        return annotLineEndingROpenArrow;
149
0
    }
150
0
    if (name.isName("RClosedArrow")) {
151
0
        return annotLineEndingRClosedArrow;
152
0
    }
153
0
    if (name.isName("Slash")) {
154
0
        return annotLineEndingSlash;
155
0
    }
156
0
    return annotLineEndingNone;
157
0
}
158
159
static const char *convertAnnotLineEndingStyle(AnnotLineEndingStyle style)
160
0
{
161
0
    switch (style) {
162
0
    case annotLineEndingSquare:
163
0
        return "Square";
164
0
    case annotLineEndingCircle:
165
0
        return "Circle";
166
0
    case annotLineEndingDiamond:
167
0
        return "Diamond";
168
0
    case annotLineEndingOpenArrow:
169
0
        return "OpenArrow";
170
0
    case annotLineEndingClosedArrow:
171
0
        return "ClosedArrow";
172
0
    case annotLineEndingButt:
173
0
        return "Butt";
174
0
    case annotLineEndingROpenArrow:
175
0
        return "ROpenArrow";
176
0
    case annotLineEndingRClosedArrow:
177
0
        return "RClosedArrow";
178
0
    case annotLineEndingSlash:
179
0
        return "Slash";
180
0
    default:
181
0
        return "None";
182
0
    }
183
0
}
184
185
static AnnotExternalDataType parseAnnotExternalData(Dict *dict)
186
0
{
187
0
    AnnotExternalDataType type;
188
189
0
    Object obj1 = dict->lookup("Subtype");
190
0
    if (obj1.isName()) {
191
0
        const std::string &typeName = obj1.getNameString();
192
193
0
        if (typeName == "Markup3D") {
194
0
            type = annotExternalDataMarkup3D;
195
0
        } else {
196
0
            type = annotExternalDataMarkupUnknown;
197
0
        }
198
0
    } else {
199
0
        type = annotExternalDataMarkupUnknown;
200
0
    }
201
202
0
    return type;
203
0
}
204
205
static std::unique_ptr<PDFRectangle> parseDiffRectangle(Array *array, PDFRectangle *rect)
206
0
{
207
0
    if (array->getLength() == 4) {
208
        // deltas
209
0
        const double dx1 = array->get(0).getNumWithDefaultValue(0);
210
0
        const double dy1 = array->get(1).getNumWithDefaultValue(0);
211
0
        const double dx2 = array->get(2).getNumWithDefaultValue(0);
212
0
        const double dy2 = array->get(3).getNumWithDefaultValue(0);
213
214
        // checking that the numbers are valid (i.e. >= 0),
215
        // and that applying the differences still give us a valid rect
216
0
        if (dx1 >= 0 && dy1 >= 0 && dx2 >= 0 && dy2 && (rect->x2 - rect->x1 - dx1 - dx2) >= 0 && (rect->y2 - rect->y1 - dy1 - dy2) >= 0) {
217
0
            auto newRect = std::make_unique<PDFRectangle>();
218
0
            newRect->x1 = rect->x1 + dx1;
219
0
            newRect->y1 = rect->y1 + dy1;
220
0
            newRect->x2 = rect->x2 - dx2;
221
0
            newRect->y2 = rect->y2 - dy2;
222
0
            return newRect;
223
0
        }
224
0
    }
225
0
    return nullptr;
226
0
}
227
228
static std::unique_ptr<LinkAction> getAdditionalAction(Annot::AdditionalActionsType type, Object *additionalActions, PDFDoc *doc)
229
0
{
230
0
    Object additionalActionsObject = additionalActions->fetch(doc->getXRef());
231
232
0
    if (additionalActionsObject.isDict()) {
233
0
        const char *key = (type == Annot::actionCursorEntering          ? "E"
234
0
                                   : type == Annot::actionCursorLeaving ? "X"
235
0
                                   : type == Annot::actionMousePressed  ? "D"
236
0
                                   : type == Annot::actionMouseReleased ? "U"
237
0
                                   : type == Annot::actionFocusIn       ? "Fo"
238
0
                                   : type == Annot::actionFocusOut      ? "Bl"
239
0
                                   : type == Annot::actionPageOpening   ? "PO"
240
0
                                   : type == Annot::actionPageClosing   ? "PC"
241
0
                                   : type == Annot::actionPageVisible   ? "PV"
242
0
                                   : type == Annot::actionPageInvisible ? "PI"
243
0
                                                                        : nullptr);
244
245
0
        Object actionObject = additionalActionsObject.dictLookup(key);
246
0
        if (actionObject.isDict()) {
247
0
            return LinkAction::parseAction(&actionObject, doc->getCatalog()->getBaseURI());
248
0
        }
249
0
    }
250
251
0
    return nullptr;
252
0
}
253
254
static const char *getFormAdditionalActionKey(Annot::FormAdditionalActionsType type)
255
0
{
256
0
    return (type == Annot::actionFieldModified ? "K" : type == Annot::actionFormatField ? "F" : type == Annot::actionValidateField ? "V" : type == Annot::actionCalculateField ? "C" : nullptr);
257
0
}
258
259
static const char *determineFallbackFont(const std::string &tok, const char *defaultFallback)
260
0
{
261
    // TODO: adjust these based on other example PDFs.
262
0
    if (tok == "/ZaDb") {
263
0
        return "ZapfDingbats";
264
0
    }
265
0
    if (tok == "/Cour") {
266
0
        return "Courier";
267
0
    }
268
0
    if (tok == "/TiRo") {
269
0
        return "TimesNewRoman";
270
0
    }
271
0
    if (tok == "/Helvetica-Bold") {
272
0
        return "Helvetica-Bold";
273
0
    }
274
0
    return defaultFallback;
275
0
}
276
277
//------------------------------------------------------------------------
278
// AnnotBorderEffect
279
//------------------------------------------------------------------------
280
281
AnnotBorderEffect::AnnotBorderEffect(Dict *dict)
282
0
{
283
0
    Object obj1;
284
285
0
    obj1 = dict->lookup("S");
286
0
    if (obj1.isName()) {
287
0
        const std::string &effectName = obj1.getNameString();
288
289
0
        if (effectName == "C") {
290
0
            effectType = borderEffectCloudy;
291
0
        } else {
292
0
            effectType = borderEffectNoEffect;
293
0
        }
294
0
    } else {
295
0
        effectType = borderEffectNoEffect;
296
0
    }
297
298
0
    if (effectType == borderEffectCloudy) {
299
0
        intensity = dict->lookup("I").getNumWithDefaultValue(0);
300
0
    } else {
301
0
        intensity = 0;
302
0
    }
303
0
}
304
305
//------------------------------------------------------------------------
306
// AnnotPath
307
//------------------------------------------------------------------------
308
309
0
AnnotPath::AnnotPath() = default;
310
311
AnnotPath::AnnotPath(const Array &array)
312
0
{
313
0
    parsePathArray(array);
314
0
}
315
316
AnnotPath::AnnotPath(std::vector<AnnotCoord> &&coordsA)
317
0
{
318
0
    coords = std::move(coordsA);
319
0
}
320
321
0
AnnotPath::~AnnotPath() = default;
322
323
double AnnotPath::getX(int coord) const
324
0
{
325
0
    if (coord >= 0 && coord < getCoordsLength()) {
326
0
        return coords[coord].getX();
327
0
    }
328
0
    return 0;
329
0
}
330
331
double AnnotPath::getY(int coord) const
332
0
{
333
0
    if (coord >= 0 && coord < getCoordsLength()) {
334
0
        return coords[coord].getY();
335
0
    }
336
0
    return 0;
337
0
}
338
339
void AnnotPath::parsePathArray(const Array &array)
340
0
{
341
0
    if (array.getLength() % 2) {
342
0
        error(errSyntaxError, -1, "Bad Annot Path");
343
0
        return;
344
0
    }
345
346
0
    const auto tempLength = array.getLength() / 2;
347
0
    std::vector<AnnotCoord> tempCoords;
348
0
    tempCoords.reserve(tempLength);
349
0
    for (int i = 0; i < tempLength; i++) {
350
0
        double x = 0, y = 0;
351
352
0
        Object obj1 = array.get(i * 2);
353
0
        if (obj1.isNum()) {
354
0
            x = obj1.getNum();
355
0
        } else {
356
0
            return;
357
0
        }
358
359
0
        obj1 = array.get((i * 2) + 1);
360
0
        if (obj1.isNum()) {
361
0
            y = obj1.getNum();
362
0
        } else {
363
0
            return;
364
0
        }
365
366
0
        tempCoords.emplace_back(x, y);
367
0
    }
368
369
0
    coords = std::move(tempCoords);
370
0
}
371
372
//------------------------------------------------------------------------
373
// AnnotCalloutLine
374
//------------------------------------------------------------------------
375
376
0
AnnotCalloutLine::AnnotCalloutLine(double x1, double y1, double x2, double y2) : coord1(x1, y1), coord2(x2, y2) { }
377
378
0
AnnotCalloutLine::~AnnotCalloutLine() = default;
379
380
//------------------------------------------------------------------------
381
// AnnotCalloutMultiLine
382
//------------------------------------------------------------------------
383
384
0
AnnotCalloutMultiLine::AnnotCalloutMultiLine(double x1, double y1, double x2, double y2, double x3, double y3) : AnnotCalloutLine(x1, y1, x2, y2), coord3(x3, y3) { }
385
386
AnnotCalloutMultiLine::~AnnotCalloutMultiLine() = default;
387
388
//------------------------------------------------------------------------
389
// AnnotQuadrilateral
390
//------------------------------------------------------------------------
391
392
AnnotQuadrilaterals::AnnotQuadrilaterals(const Array &array)
393
0
{
394
0
    int arrayLength = array.getLength();
395
0
    int quadsLength = 0;
396
0
    double quadArray[8];
397
398
    // default values
399
0
    quadrilateralsLength = 0;
400
401
0
    if ((arrayLength % 8) == 0) {
402
0
        int i;
403
404
0
        quadsLength = arrayLength / 8;
405
0
        auto quads = std::make_unique<AnnotQuadrilateral[]>(quadsLength);
406
0
        for (i = 0; i < quadsLength; i++) {
407
0
            for (int j = 0; j < 8; j++) {
408
0
                Object obj = array.get(i * 8 + j);
409
0
                if (obj.isNum()) {
410
0
                    quadArray[j] = obj.getNum();
411
0
                } else {
412
0
                    error(errSyntaxError, -1, "Invalid QuadPoint in annot");
413
0
                    return;
414
0
                }
415
0
            }
416
417
0
            quads[i] = AnnotQuadrilateral(quadArray[0], quadArray[1], quadArray[2], quadArray[3], quadArray[4], quadArray[5], quadArray[6], quadArray[7]);
418
0
        }
419
420
0
        quadrilateralsLength = quadsLength;
421
0
        quadrilaterals = std::move(quads);
422
0
    }
423
0
}
424
425
AnnotQuadrilaterals::AnnotQuadrilaterals(std::unique_ptr<AnnotQuadrilateral[]> &&quads, int quadsLength)
426
0
{
427
0
    quadrilaterals = std::move(quads);
428
0
    quadrilateralsLength = quadsLength;
429
0
}
430
431
0
AnnotQuadrilaterals::~AnnotQuadrilaterals() = default;
432
433
double AnnotQuadrilaterals::getX1(int quadrilateral) const
434
0
{
435
0
    if (quadrilateral >= 0 && quadrilateral < quadrilateralsLength) {
436
0
        return quadrilaterals[quadrilateral].coord1.getX();
437
0
    }
438
0
    return 0;
439
0
}
440
441
double AnnotQuadrilaterals::getY1(int quadrilateral) const
442
0
{
443
0
    if (quadrilateral >= 0 && quadrilateral < quadrilateralsLength) {
444
0
        return quadrilaterals[quadrilateral].coord1.getY();
445
0
    }
446
0
    return 0;
447
0
}
448
449
double AnnotQuadrilaterals::getX2(int quadrilateral) const
450
0
{
451
0
    if (quadrilateral >= 0 && quadrilateral < quadrilateralsLength) {
452
0
        return quadrilaterals[quadrilateral].coord2.getX();
453
0
    }
454
0
    return 0;
455
0
}
456
457
double AnnotQuadrilaterals::getY2(int quadrilateral) const
458
0
{
459
0
    if (quadrilateral >= 0 && quadrilateral < quadrilateralsLength) {
460
0
        return quadrilaterals[quadrilateral].coord2.getY();
461
0
    }
462
0
    return 0;
463
0
}
464
465
double AnnotQuadrilaterals::getX3(int quadrilateral) const
466
0
{
467
0
    if (quadrilateral >= 0 && quadrilateral < quadrilateralsLength) {
468
0
        return quadrilaterals[quadrilateral].coord3.getX();
469
0
    }
470
0
    return 0;
471
0
}
472
473
double AnnotQuadrilaterals::getY3(int quadrilateral) const
474
0
{
475
0
    if (quadrilateral >= 0 && quadrilateral < quadrilateralsLength) {
476
0
        return quadrilaterals[quadrilateral].coord3.getY();
477
0
    }
478
0
    return 0;
479
0
}
480
481
double AnnotQuadrilaterals::getX4(int quadrilateral) const
482
0
{
483
0
    if (quadrilateral >= 0 && quadrilateral < quadrilateralsLength) {
484
0
        return quadrilaterals[quadrilateral].coord4.getX();
485
0
    }
486
0
    return 0;
487
0
}
488
489
double AnnotQuadrilaterals::getY4(int quadrilateral) const
490
0
{
491
0
    if (quadrilateral >= 0 && quadrilateral < quadrilateralsLength) {
492
0
        return quadrilaterals[quadrilateral].coord4.getY();
493
0
    }
494
0
    return 0;
495
0
}
496
497
0
AnnotQuadrilaterals::AnnotQuadrilateral::AnnotQuadrilateral() = default;
498
499
0
AnnotQuadrilaterals::AnnotQuadrilateral::AnnotQuadrilateral(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4) : coord1(x1, y1), coord2(x2, y2), coord3(x3, y3), coord4(x4, y4) { }
500
501
//------------------------------------------------------------------------
502
// AnnotBorder
503
//------------------------------------------------------------------------
504
AnnotBorder::AnnotBorder()
505
0
{
506
0
    width = 1;
507
0
    style = borderSolid;
508
0
}
509
510
bool AnnotBorder::parseDashArray(Object *dashObj)
511
0
{
512
0
    bool correct = true;
513
0
    const int tempLength = dashObj->arrayGetLength();
514
0
    std::vector<double> tempDash(tempLength);
515
516
    // TODO: check not all zero (Line Dash Pattern Page 217 PDF 8.1)
517
0
    for (int i = 0; i < tempLength && i < DASH_LIMIT && correct; i++) {
518
0
        const Object obj1 = dashObj->arrayGet(i);
519
0
        if (obj1.isNum()) {
520
0
            tempDash[i] = obj1.getNum();
521
522
0
            correct = tempDash[i] >= 0;
523
0
        } else {
524
0
            correct = false;
525
0
        }
526
0
    }
527
528
0
    if (correct) {
529
0
        dash = std::move(tempDash);
530
0
        style = borderDashed;
531
0
    }
532
533
0
    return correct;
534
0
}
535
536
0
AnnotBorder::~AnnotBorder() = default;
537
538
//------------------------------------------------------------------------
539
// AnnotBorderArray
540
//------------------------------------------------------------------------
541
542
AnnotBorderArray::AnnotBorderArray()
543
0
{
544
0
    horizontalCorner = 0;
545
0
    verticalCorner = 0;
546
0
}
547
548
AnnotBorderArray::AnnotBorderArray(const Array &array)
549
0
{
550
0
    Object obj1;
551
0
    int arrayLength = array.getLength();
552
553
0
    bool correct = true;
554
0
    if (arrayLength == 3 || arrayLength == 4) {
555
        // implementation note 81 in Appendix H.
556
557
0
        obj1 = array.get(0);
558
0
        if (obj1.isNum()) {
559
0
            horizontalCorner = obj1.getNum();
560
0
        } else {
561
0
            correct = false;
562
0
        }
563
564
0
        obj1 = array.get(1);
565
0
        if (obj1.isNum()) {
566
0
            verticalCorner = obj1.getNum();
567
0
        } else {
568
0
            correct = false;
569
0
        }
570
571
0
        obj1 = array.get(2);
572
0
        if (obj1.isNum()) {
573
0
            width = obj1.getNum();
574
0
        } else {
575
0
            correct = false;
576
0
        }
577
578
0
        if (arrayLength == 4) {
579
0
            obj1 = array.get(3);
580
0
            if (obj1.isArray()) {
581
0
                correct = parseDashArray(&obj1);
582
0
            } else {
583
0
                correct = false;
584
0
            }
585
0
        }
586
0
    } else {
587
0
        correct = false;
588
0
    }
589
590
0
    if (!correct) {
591
0
        width = 0;
592
0
    }
593
0
}
594
595
std::unique_ptr<AnnotBorder> AnnotBorderArray::copy() const
596
0
{
597
0
    auto *res = new AnnotBorderArray();
598
0
    res->type = type;
599
0
    res->width = width;
600
0
    res->dash = dash;
601
0
    res->style = style;
602
0
    res->horizontalCorner = horizontalCorner;
603
0
    res->verticalCorner = verticalCorner;
604
0
    return std::unique_ptr<AnnotBorder>(res);
605
0
}
606
607
Object AnnotBorderArray::writeToObject(XRef *xref) const
608
0
{
609
0
    auto borderArray = std::make_unique<Array>(xref);
610
0
    borderArray->add(Object(horizontalCorner));
611
0
    borderArray->add(Object(verticalCorner));
612
0
    borderArray->add(Object(width));
613
614
0
    if (!dash.empty()) {
615
0
        auto a = std::make_unique<Array>(xref);
616
617
0
        for (double d : dash) {
618
0
            a->add(Object(d));
619
0
        }
620
621
0
        borderArray->add(Object(std::move(a)));
622
0
    }
623
624
0
    return Object(std::move(borderArray));
625
0
}
626
627
//------------------------------------------------------------------------
628
// AnnotBorderBS
629
//------------------------------------------------------------------------
630
631
0
AnnotBorderBS::AnnotBorderBS() = default;
632
633
AnnotBorderBS::AnnotBorderBS(Dict *dict)
634
0
{
635
    // Border width (in points)
636
0
    Object obj1 = dict->lookup("W");
637
0
    width = obj1.getNumWithDefaultValue(1.0);
638
639
    // Border style
640
0
    obj1 = dict->lookup("S");
641
0
    if (obj1.isName()) {
642
0
        const std::string &styleName = obj1.getNameString();
643
644
0
        if (styleName == "S") {
645
0
            style = borderSolid;
646
0
        } else if (styleName == "D") {
647
0
            style = borderDashed;
648
0
        } else if (styleName == "B") {
649
0
            style = borderBeveled;
650
0
        } else if (styleName == "I") {
651
0
            style = borderInset;
652
0
        } else if (styleName == "U") {
653
0
            style = borderUnderlined;
654
0
        } else {
655
0
            style = borderSolid;
656
0
        }
657
0
    } else {
658
0
        style = borderSolid;
659
0
    }
660
661
    // Border dash style
662
0
    if (style == borderDashed) {
663
0
        obj1 = dict->lookup("D");
664
0
        if (!obj1.isArray() || !parseDashArray(&obj1)) {
665
0
            dash = { 3 };
666
0
        }
667
0
    }
668
0
}
669
670
const char *AnnotBorderBS::getStyleName() const
671
0
{
672
0
    switch (style) {
673
0
    case borderSolid:
674
0
        return "S";
675
0
    case borderDashed:
676
0
        return "D";
677
0
    case borderBeveled:
678
0
        return "B";
679
0
    case borderInset:
680
0
        return "I";
681
0
    case borderUnderlined:
682
0
        return "U";
683
0
    }
684
685
0
    return "S";
686
0
}
687
688
std::unique_ptr<AnnotBorder> AnnotBorderBS::copy() const
689
0
{
690
0
    auto *res = new AnnotBorderBS();
691
0
    res->type = type;
692
0
    res->width = width;
693
0
    res->dash = dash;
694
0
    res->style = style;
695
0
    return std::unique_ptr<AnnotBorder>(res);
696
0
}
697
698
Object AnnotBorderBS::writeToObject(XRef *xref) const
699
0
{
700
0
    auto dict = std::make_unique<Dict>(xref);
701
0
    dict->set("W", Object(width));
702
0
    dict->set("S", Object::name(getStyleName()));
703
0
    if (style == borderDashed && !dash.empty()) {
704
0
        auto a = std::make_unique<Array>(xref);
705
706
0
        for (double d : dash) {
707
0
            a->add(Object(d));
708
0
        }
709
0
        dict->set("D", Object(std::move(a)));
710
0
    }
711
0
    return Object(std::move(dict));
712
0
}
713
714
//------------------------------------------------------------------------
715
// AnnotColor
716
//------------------------------------------------------------------------
717
718
AnnotColor::AnnotColor()
719
0
{
720
0
    length = 0;
721
0
}
722
723
AnnotColor::AnnotColor(double gray)
724
0
{
725
0
    length = 1;
726
727
0
    values[0] = gray;
728
0
}
729
730
AnnotColor::AnnotColor(double r, double g, double b)
731
0
{
732
0
    length = 3;
733
734
0
    values[0] = r;
735
0
    values[1] = g;
736
0
    values[2] = b;
737
0
}
738
739
AnnotColor::AnnotColor(double c, double m, double y, double k)
740
0
{
741
0
    length = 4;
742
743
0
    values[0] = c;
744
0
    values[1] = m;
745
0
    values[2] = y;
746
0
    values[3] = k;
747
0
}
748
749
// If <adjust> is +1, color is brightened;
750
// if <adjust> is -1, color is darkened;
751
// otherwise color is not modified.
752
AnnotColor::AnnotColor(const Array &array, int adjust)
753
0
{
754
0
    int i;
755
756
0
    length = array.getLength();
757
0
    if (length > 4) {
758
0
        length = 4;
759
0
    }
760
761
0
    for (i = 0; i < length; i++) {
762
0
        Object obj1 = array.get(i);
763
0
        if (obj1.isNum()) {
764
0
            values[i] = obj1.getNum();
765
766
0
            if (values[i] < 0 || values[i] > 1) {
767
0
                values[i] = 0;
768
0
            }
769
0
        } else {
770
0
            values[i] = 0;
771
0
        }
772
0
    }
773
774
0
    if (adjust != 0) {
775
0
        adjustColor(adjust);
776
0
    }
777
0
}
778
779
void AnnotColor::adjustColor(int adjust)
780
0
{
781
0
    int i;
782
783
0
    if (length == 4) {
784
0
        adjust = -adjust;
785
0
    }
786
0
    if (adjust > 0) {
787
0
        for (i = 0; i < length; ++i) {
788
0
            values[i] = 0.5 * values[i] + 0.5;
789
0
        }
790
0
    } else if (adjust < 0) {
791
0
        for (i = 0; i < length; ++i) {
792
0
            values[i] = 0.5 * values[i];
793
0
        }
794
0
    }
795
0
}
796
797
Object AnnotColor::writeToObject(XRef *xref) const
798
0
{
799
0
    if (length == 0) {
800
0
        return Object::null(); // Transparent (no color)
801
0
    }
802
0
    auto a = std::make_unique<Array>(xref);
803
0
    for (int i = 0; i < length; ++i) {
804
0
        a->add(Object(values[i]));
805
0
    }
806
0
    return Object(std::move(a));
807
0
}
808
809
//------------------------------------------------------------------------
810
// DefaultAppearance
811
//------------------------------------------------------------------------
812
813
0
DefaultAppearance::DefaultAppearance(std::string fontNameA, double fontPtSizeA, std::unique_ptr<AnnotColor> &&fontColorA) : fontName(std::move(fontNameA)), fontPtSize(fontPtSizeA), fontColor(std::move(fontColorA)) { }
814
815
DefaultAppearance::DefaultAppearance(const std::string &da)
816
0
{
817
0
    fontPtSize = -1;
818
819
0
    std::vector<std::string> daToks;
820
0
    const std::optional<size_t> tfIndex = FormFieldText::tokenizeDA(da, &daToks, "Tf");
821
822
0
    if (tfIndex && tfIndex >= 1) {
823
0
        fontPtSize = gatof(daToks[*tfIndex - 1].c_str());
824
0
    }
825
0
    if (tfIndex && tfIndex >= 2) {
826
        // We are expecting a name, therefore the first letter should be '/'.
827
0
        const std::string &fontToken = daToks[*tfIndex - 2];
828
0
        if (fontToken.size() > 1 && fontToken[0] == '/') {
829
            // The +1 is here to skip the leading '/'.
830
0
            fontName = fontToken.substr(1);
831
0
        }
832
0
    }
833
    // Scan backwards: we are looking for the last set value
834
0
    size_t i = daToks.size();
835
0
    while (true) {
836
0
        if (i == 0) {
837
0
            break;
838
0
        }
839
0
        --i;
840
841
0
        if (!fontColor) {
842
0
            if (daToks[i] == "g" && i >= 1) {
843
0
                fontColor = std::make_unique<AnnotColor>(gatof(daToks[i - 1].c_str()));
844
0
            } else if (daToks[i] == "rg" && i >= 3) {
845
0
                fontColor = std::make_unique<AnnotColor>(gatof(daToks[i - 3].c_str()), gatof(daToks[i - 2].c_str()), gatof(daToks[i - 1].c_str()));
846
0
            } else if (daToks[i] == "k" && i >= 4) {
847
0
                fontColor = std::make_unique<AnnotColor>(gatof(daToks[i - 4].c_str()), gatof(daToks[i - 3].c_str()), gatof(daToks[i - 2].c_str()), gatof(daToks[i - 1].c_str()));
848
0
            }
849
0
        }
850
0
    }
851
0
}
852
853
void DefaultAppearance::setFontName(const std::string &fontNameA)
854
0
{
855
0
    fontName = fontNameA;
856
0
}
857
858
void DefaultAppearance::setFontPtSize(double fontPtSizeA)
859
0
{
860
0
    fontPtSize = fontPtSizeA;
861
0
}
862
863
void DefaultAppearance::setFontColor(std::unique_ptr<AnnotColor> fontColorA)
864
0
{
865
0
    fontColor = std::move(fontColorA);
866
0
}
867
868
std::string DefaultAppearance::toAppearanceString() const
869
0
{
870
0
    AnnotAppearanceBuilder appearBuilder;
871
0
    if (fontColor) {
872
0
        appearBuilder.setDrawColor(*fontColor, true);
873
0
    }
874
0
    appearBuilder.setTextFont(fontName, fontPtSize);
875
0
    return appearBuilder.buffer();
876
0
}
877
878
//------------------------------------------------------------------------
879
// AnnotIconFit
880
//------------------------------------------------------------------------
881
882
AnnotIconFit::AnnotIconFit(Dict *dict)
883
0
{
884
0
    Object obj1;
885
886
0
    obj1 = dict->lookup("SW");
887
0
    if (obj1.isName()) {
888
0
        const std::string &scaleName = obj1.getNameString();
889
890
0
        if (scaleName == "B") {
891
0
            scaleWhen = scaleBigger;
892
0
        } else if (scaleName == "S") {
893
0
            scaleWhen = scaleSmaller;
894
0
        } else if (scaleName == "N") {
895
0
            scaleWhen = scaleNever;
896
0
        } else {
897
0
            scaleWhen = scaleAlways;
898
0
        }
899
0
    } else {
900
0
        scaleWhen = scaleAlways;
901
0
    }
902
903
0
    obj1 = dict->lookup("S");
904
0
    if (obj1.isName()) {
905
0
        const std::string &scaleName = obj1.getNameString();
906
907
0
        if (scaleName == "A") {
908
0
            scale = scaleAnamorphic;
909
0
        } else {
910
0
            scale = scaleProportional;
911
0
        }
912
0
    } else {
913
0
        scale = scaleProportional;
914
0
    }
915
916
0
    obj1 = dict->lookup("A");
917
0
    if (obj1.isArrayOfLength(2)) {
918
0
        left = obj1.arrayGet(0).getNumWithDefaultValue(0);
919
0
        bottom = obj1.arrayGet(1).getNumWithDefaultValue(0);
920
921
0
        if (left < 0 || left > 1) {
922
0
            left = 0.5;
923
0
        }
924
925
0
        if (bottom < 0 || bottom > 1) {
926
0
            bottom = 0.5;
927
0
        }
928
929
0
    } else {
930
0
        left = bottom = 0.5;
931
0
    }
932
933
0
    fullyBounds = dict->lookup("FB").getBoolWithDefaultValue(false);
934
0
}
935
936
//------------------------------------------------------------------------
937
// AnnotAppearance
938
//------------------------------------------------------------------------
939
940
AnnotAppearance::AnnotAppearance(PDFDoc *docA, Object *dict)
941
0
{
942
0
    assert(dict->isDict());
943
0
    doc = docA;
944
0
    appearDict = dict->copy();
945
0
}
946
947
0
AnnotAppearance::~AnnotAppearance() = default;
948
949
Object AnnotAppearance::getAppearanceStream(AnnotAppearanceType type, std::string_view state) const
950
0
{
951
0
    Object apData;
952
953
    // Obtain dictionary or stream associated to appearance type
954
0
    switch (type) {
955
0
    case appearRollover:
956
0
        apData = appearDict.dictLookupNF("R").copy();
957
0
        if (apData.isNull()) {
958
0
            apData = appearDict.dictLookupNF("N").copy();
959
0
        }
960
0
        break;
961
0
    case appearDown:
962
0
        apData = appearDict.dictLookupNF("D").copy();
963
0
        if (apData.isNull()) {
964
0
            apData = appearDict.dictLookupNF("N").copy();
965
0
        }
966
0
        break;
967
0
    case appearNormal:
968
0
        apData = appearDict.dictLookupNF("N").copy();
969
0
        break;
970
0
    }
971
972
0
    if (apData.isDict()) {
973
0
        return apData.dictLookupNF(state).copy();
974
0
    }
975
0
    if (apData.isRef()) {
976
        // Ref can point to 1)Stream or 2)dictionary with named streams - Issue #1558
977
0
        Object obj = apData.fetch(doc->getXRef());
978
0
        if (obj.isDict()) {
979
0
            return obj.dictLookupNF(state).copy();
980
0
        }
981
0
        return apData;
982
0
    }
983
984
0
    return Object();
985
0
}
986
987
std::optional<std::string> AnnotAppearance::getStateKey(int i) const
988
0
{
989
0
    const Object &obj1 = appearDict.dictLookupNF("N");
990
0
    if (obj1.isDict()) {
991
0
        return obj1.getDict()->getKey(i);
992
0
    }
993
0
    return std::nullopt;
994
0
}
995
996
int AnnotAppearance::getNumStates() const
997
0
{
998
0
    int res = 0;
999
0
    const Object &obj1 = appearDict.dictLookupNF("N");
1000
0
    if (obj1.isDict()) {
1001
0
        res = obj1.dictGetLength();
1002
0
    }
1003
0
    return res;
1004
0
}
1005
1006
// Test if stateObj (a Ref or a Dict) points to the specified stream
1007
bool AnnotAppearance::referencesStream(const Object &stateObj, Ref refToStream)
1008
0
{
1009
0
    if (stateObj.isRef()) {
1010
0
        const Ref r = stateObj.getRef();
1011
0
        if (r == refToStream) {
1012
0
            return true;
1013
0
        }
1014
0
    } else if (stateObj.isDict()) { // Test each value
1015
0
        const Dict *stateDict = stateObj.getDict();
1016
0
        const int size = stateDict->getLength();
1017
0
        for (int i = 0; i < size; ++i) {
1018
0
            const Object &obj1 = stateDict->getValNF(i);
1019
0
            if (obj1.isRef()) {
1020
0
                const Ref r = obj1.getRef();
1021
0
                if (r == refToStream) {
1022
0
                    return true;
1023
0
                }
1024
0
            }
1025
0
        }
1026
0
    }
1027
0
    return false; // Not found
1028
0
}
1029
1030
// Test if this AnnotAppearance references the specified stream
1031
bool AnnotAppearance::referencesStream(Ref refToStream)
1032
0
{
1033
0
    bool found;
1034
1035
    // Scan each state's ref/subdictionary
1036
0
    const Object &objN = appearDict.dictLookupNF("N");
1037
0
    found = referencesStream(objN, refToStream);
1038
0
    if (found) {
1039
0
        return true;
1040
0
    }
1041
1042
0
    const Object &objR = appearDict.dictLookupNF("R");
1043
0
    found = referencesStream(objR, refToStream);
1044
0
    if (found) {
1045
0
        return true;
1046
0
    }
1047
1048
0
    const Object &objD = appearDict.dictLookupNF("D");
1049
0
    found = referencesStream(objD, refToStream);
1050
0
    return found;
1051
0
}
1052
1053
// If this is the only annotation in the document that references the
1054
// specified appearance stream, remove the appearance stream
1055
void AnnotAppearance::removeStream(Ref refToStream)
1056
0
{
1057
0
    const int lastpage = doc->getNumPages();
1058
0
    for (int pg = 1; pg <= lastpage; ++pg) { // Scan all annotations in the document
1059
0
        Page *page = doc->getPage(pg);
1060
0
        if (!page) {
1061
0
            error(errSyntaxError, -1, "Failed check for shared annotation stream at page {0:d}", pg);
1062
0
            continue;
1063
0
        }
1064
0
        Annots *annots = page->getAnnots();
1065
0
        for (const std::shared_ptr<Annot> &annot : annots->getAnnots()) {
1066
0
            AnnotAppearance *annotAp = annot->getAppearStreams();
1067
0
            if (annotAp && annotAp != this && annotAp->referencesStream(refToStream)) {
1068
0
                return; // Another annotation points to the stream -> Don't delete it
1069
0
            }
1070
0
        }
1071
0
    }
1072
1073
    // TODO: stream resources (e.g. font), AP name tree
1074
0
    doc->getXRef()->removeIndirectObject(refToStream);
1075
0
}
1076
1077
// Removes stream if obj is a Ref, or removes pointed streams if obj is a Dict
1078
void AnnotAppearance::removeStateStreams(const Object &state)
1079
0
{
1080
0
    if (state.isRef()) {
1081
0
        removeStream(state.getRef());
1082
0
    } else if (state.isDict()) {
1083
0
        const Dict *stateDict = state.getDict();
1084
0
        const int size = stateDict->getLength();
1085
0
        for (int i = 0; i < size; ++i) {
1086
0
            const Object &obj2 = stateDict->getValNF(i);
1087
0
            if (obj2.isRef()) {
1088
0
                removeStream(obj2.getRef());
1089
0
            }
1090
0
        }
1091
0
    }
1092
0
}
1093
1094
void AnnotAppearance::removeAllStreams()
1095
0
{
1096
0
    const Object &objN = appearDict.dictLookupNF("N");
1097
0
    removeStateStreams(objN);
1098
0
    const Object &objR = appearDict.dictLookupNF("R");
1099
0
    removeStateStreams(objR);
1100
0
    const Object &objD = appearDict.dictLookupNF("D");
1101
0
    removeStateStreams(objD);
1102
0
}
1103
1104
//------------------------------------------------------------------------
1105
// AnnotAppearanceCharacs
1106
//------------------------------------------------------------------------
1107
1108
AnnotAppearanceCharacs::AnnotAppearanceCharacs(Dict *dict)
1109
0
{
1110
0
    Object obj1;
1111
1112
0
    if (!dict) {
1113
0
        rotation = 0;
1114
0
        position = captionNoIcon;
1115
0
        return;
1116
0
    }
1117
1118
0
    obj1 = dict->lookup("R");
1119
0
    if (obj1.isInt()) {
1120
0
        rotation = obj1.getInt();
1121
0
    } else {
1122
0
        rotation = 0;
1123
0
    }
1124
1125
0
    obj1 = dict->lookup("BC");
1126
0
    if (obj1.isArray()) {
1127
0
        Array *colorComponents = obj1.getArray();
1128
0
        if (colorComponents->getLength() > 0) {
1129
0
            borderColor = std::make_unique<AnnotColor>(*colorComponents);
1130
0
        }
1131
0
    }
1132
1133
0
    obj1 = dict->lookup("BG");
1134
0
    if (obj1.isArray()) {
1135
0
        Array *colorComponents = obj1.getArray();
1136
0
        if (colorComponents->getLength() > 0) {
1137
0
            backColor = std::make_unique<AnnotColor>(*colorComponents);
1138
0
        }
1139
0
    }
1140
1141
0
    obj1 = dict->lookup("CA");
1142
0
    if (obj1.isString()) {
1143
0
        normalCaption = obj1.takeString();
1144
0
    }
1145
1146
0
    obj1 = dict->lookup("RC");
1147
0
    if (obj1.isString()) {
1148
0
        rolloverCaption = obj1.takeString();
1149
0
    }
1150
1151
0
    obj1 = dict->lookup("AC");
1152
0
    if (obj1.isString()) {
1153
0
        alternateCaption = obj1.takeString();
1154
0
    }
1155
1156
0
    obj1 = dict->lookup("IF");
1157
0
    if (obj1.isDict()) {
1158
0
        iconFit = std::make_unique<AnnotIconFit>(obj1.getDict());
1159
0
    }
1160
1161
0
    obj1 = dict->lookup("TP");
1162
0
    if (obj1.isInt()) {
1163
0
        position = static_cast<AnnotAppearanceCharacsTextPos>(obj1.getInt());
1164
0
    } else {
1165
0
        position = captionNoIcon;
1166
0
    }
1167
0
}
1168
1169
0
AnnotAppearanceCharacs::~AnnotAppearanceCharacs() = default;
1170
1171
std::unique_ptr<AnnotAppearanceCharacs> AnnotAppearanceCharacs::copy() const
1172
0
{
1173
0
    auto *res = new AnnotAppearanceCharacs(nullptr);
1174
0
    res->rotation = rotation;
1175
0
    if (borderColor) {
1176
0
        res->borderColor = std::make_unique<AnnotColor>(*borderColor);
1177
0
    }
1178
0
    if (backColor) {
1179
0
        res->backColor = std::make_unique<AnnotColor>(*backColor);
1180
0
    }
1181
0
    if (normalCaption) {
1182
0
        res->normalCaption = normalCaption->copy();
1183
0
    }
1184
0
    if (rolloverCaption) {
1185
0
        res->rolloverCaption = rolloverCaption->copy();
1186
0
    }
1187
0
    if (alternateCaption) {
1188
0
        res->alternateCaption = alternateCaption->copy();
1189
0
    }
1190
0
    if (iconFit) {
1191
0
        res->iconFit = std::make_unique<AnnotIconFit>(*iconFit);
1192
0
    }
1193
0
    res->position = position;
1194
0
    return std::unique_ptr<AnnotAppearanceCharacs>(res);
1195
0
}
1196
1197
//------------------------------------------------------------------------
1198
// AnnotAppearanceBBox
1199
//------------------------------------------------------------------------
1200
1201
AnnotAppearanceBBox::AnnotAppearanceBBox(const PDFRectangle &rect)
1202
0
{
1203
0
    origX = rect.x1;
1204
0
    origY = rect.y1;
1205
0
    borderWidth = 0;
1206
1207
    // Initially set the same size as rect
1208
0
    minX = 0;
1209
0
    minY = 0;
1210
0
    maxX = rect.x2 - rect.x1;
1211
0
    maxY = rect.y2 - rect.y1;
1212
0
}
1213
1214
void AnnotAppearanceBBox::extendTo(double x, double y)
1215
0
{
1216
0
    if (x < minX) {
1217
0
        minX = x;
1218
0
    } else if (x > maxX) {
1219
0
        maxX = x;
1220
0
    }
1221
0
    if (y < minY) {
1222
0
        minY = y;
1223
0
    } else if (y > maxY) {
1224
0
        maxY = y;
1225
0
    }
1226
0
}
1227
1228
std::array<double, 4> AnnotAppearanceBBox::getBBoxRect() const
1229
0
{
1230
0
    return { minX - borderWidth, minY - borderWidth, maxX + borderWidth, maxY + borderWidth };
1231
0
}
1232
1233
double AnnotAppearanceBBox::getPageXMin() const
1234
0
{
1235
0
    return origX + minX - borderWidth;
1236
0
}
1237
1238
double AnnotAppearanceBBox::getPageYMin() const
1239
0
{
1240
0
    return origY + minY - borderWidth;
1241
0
}
1242
1243
double AnnotAppearanceBBox::getPageXMax() const
1244
0
{
1245
0
    return origX + maxX + borderWidth;
1246
0
}
1247
1248
double AnnotAppearanceBBox::getPageYMax() const
1249
0
{
1250
0
    return origY + maxY + borderWidth;
1251
0
}
1252
1253
//------------------------------------------------------------------------
1254
// Annot
1255
//------------------------------------------------------------------------
1256
1257
0
#define annotLocker() const std::scoped_lock locker(mutex)
1258
1259
Annot::Annot(PDFDoc *docA, const PDFRectangle &rectA)
1260
0
{
1261
1262
0
    flags = flagUnknown;
1263
0
    type = typeUnknown;
1264
1265
0
    auto a = std::make_unique<Array>(docA->getXRef());
1266
0
    a->add(Object(rectA.x1));
1267
0
    a->add(Object(rectA.y1));
1268
0
    a->add(Object(rectA.x2));
1269
0
    a->add(Object(rectA.y2));
1270
1271
0
    annotObj = Object(std::make_unique<Dict>(docA->getXRef()));
1272
0
    annotObj.dictSet("Type", Object::name("Annot"));
1273
0
    annotObj.dictSet("Rect", Object(std::move(a)));
1274
1275
0
    ref = docA->getXRef()->addIndirectObject(annotObj);
1276
1277
0
    initialize(docA, annotObj.getDict());
1278
0
}
1279
1280
Annot::Annot(PDFDoc *docA, Object &&dictObject)
1281
0
{
1282
0
    hasRef = false;
1283
0
    flags = flagUnknown;
1284
0
    type = typeUnknown;
1285
0
    annotObj = std::move(dictObject);
1286
0
    initialize(docA, annotObj.getDict());
1287
0
}
1288
1289
Annot::Annot(PDFDoc *docA, Object &&dictObject, const Object &obj)
1290
0
{
1291
0
    if (obj.isRef()) {
1292
0
        hasRef = true;
1293
0
        ref = obj.getRef();
1294
0
    } else {
1295
0
        hasRef = false;
1296
0
    }
1297
0
    flags = flagUnknown;
1298
0
    type = typeUnknown;
1299
0
    annotObj = std::move(dictObject);
1300
0
    initialize(docA, annotObj.getDict());
1301
0
}
1302
1303
void Annot::initialize(PDFDoc *docA, Dict *dict)
1304
0
{
1305
0
    Object apObj, asObj, obj1;
1306
1307
0
    ok = true;
1308
0
    doc = docA;
1309
1310
0
    appearance.setToNull();
1311
1312
    //----- parse the rectangle
1313
0
    rect = std::make_unique<PDFRectangle>();
1314
0
    obj1 = dict->lookup("Rect");
1315
0
    if (obj1.isArrayOfLength(4)) {
1316
0
        rect->x1 = obj1.arrayGet(0).getNumWithDefaultValue(0);
1317
0
        rect->y1 = obj1.arrayGet(1).getNumWithDefaultValue(0);
1318
0
        rect->x2 = obj1.arrayGet(2).getNumWithDefaultValue(1);
1319
0
        rect->y2 = obj1.arrayGet(3).getNumWithDefaultValue(1);
1320
1321
0
        if (rect->x1 > rect->x2) {
1322
0
            double t = rect->x1;
1323
0
            rect->x1 = rect->x2;
1324
0
            rect->x2 = t;
1325
0
        }
1326
1327
0
        if (rect->y1 > rect->y2) {
1328
0
            double t = rect->y1;
1329
0
            rect->y1 = rect->y2;
1330
0
            rect->y2 = t;
1331
0
        }
1332
0
    } else {
1333
0
        rect->x1 = rect->y1 = 0;
1334
0
        rect->x2 = rect->y2 = 1;
1335
0
        error(errSyntaxError, -1, "Bad bounding box for annotation");
1336
0
        ok = false;
1337
0
    }
1338
1339
0
    obj1 = dict->lookup("Contents");
1340
0
    if (obj1.isString()) {
1341
0
        contents = GooString(obj1.getString());
1342
0
    } else {
1343
0
        contents.clear();
1344
0
    }
1345
1346
    // Note: This value is overwritten by Annots ctor
1347
0
    const Object &pObj = dict->lookupNF("P");
1348
0
    if (pObj.isRef()) {
1349
0
        const Ref pRef = pObj.getRef();
1350
1351
0
        page = doc->getCatalog()->findPage(pRef);
1352
0
    } else {
1353
0
        page = 0;
1354
0
    }
1355
1356
0
    obj1 = dict->lookup("NM");
1357
0
    if (obj1.isString()) {
1358
0
        name = obj1.takeString();
1359
0
    }
1360
1361
0
    obj1 = dict->lookup("M");
1362
0
    if (obj1.isString()) {
1363
0
        modified = obj1.takeString();
1364
0
    }
1365
1366
    //----- get the flags
1367
0
    obj1 = dict->lookup("F");
1368
0
    if (obj1.isInt()) {
1369
0
        flags |= obj1.getInt();
1370
0
    } else {
1371
0
        flags = flagUnknown;
1372
0
    }
1373
1374
    //----- get the annotation appearance dictionary
1375
0
    apObj = dict->lookup("AP");
1376
0
    if (apObj.isDict()) {
1377
0
        appearStreams = std::make_unique<AnnotAppearance>(doc, &apObj);
1378
0
    }
1379
1380
    //----- get the appearance state
1381
0
    asObj = dict->lookup("AS");
1382
0
    if (asObj.isName()) {
1383
0
        appearState = asObj.getNameString();
1384
0
    } else if (appearStreams && appearStreams->getNumStates() != 0) {
1385
0
        error(errSyntaxError, -1, "Invalid or missing AS value in annotation containing one or more appearance subdictionaries");
1386
        // AS value is required in this case, but if the
1387
        // N dictionary contains only one entry
1388
        // take it as default appearance.
1389
0
        if (appearStreams->getNumStates() == 1) {
1390
0
            appearState = appearStreams->getStateKey(0);
1391
0
        }
1392
0
    }
1393
0
    if (!appearState) {
1394
0
        appearState = "Off";
1395
0
    }
1396
1397
    //----- get the annotation appearance
1398
0
    if (appearStreams) {
1399
0
        appearance = appearStreams->getAppearanceStream(AnnotAppearance::appearNormal, appearState.value());
1400
0
        Object obj = appearance.fetch(doc->getXRef());
1401
0
        if (obj.isStream() && !obj.getStream()->rewind()) {
1402
            // appearance stream is invalid as reset() returned false - Issue #1557
1403
0
            appearance.setToNull();
1404
0
        }
1405
0
    }
1406
1407
    //----- parse the border style
1408
    // According to the spec if neither the Border nor the BS entry is present,
1409
    // the border shall be drawn as a solid line with a width of 1 point. But acroread
1410
    // seems to ignore the Border entry for annots that can't have a BS entry. So, we only
1411
    // follow this rule for annots tha can have a BS entry.
1412
0
    obj1 = dict->lookup("Border");
1413
0
    if (obj1.isArray()) {
1414
0
        border = std::make_unique<AnnotBorderArray>(*obj1.getArray());
1415
0
    }
1416
1417
0
    obj1 = dict->lookup("C");
1418
0
    if (obj1.isArray()) {
1419
0
        color = std::make_unique<AnnotColor>(*obj1.getArray());
1420
0
    }
1421
1422
0
    obj1 = dict->lookup("StructParent");
1423
0
    if (obj1.isInt()) {
1424
0
        treeKey = obj1.getInt();
1425
0
    } else {
1426
0
        treeKey = 0;
1427
0
    }
1428
1429
0
    oc = dict->lookupNF("OC").copy();
1430
0
}
1431
1432
void Annot::getRect(double *x1, double *y1, double *x2, double *y2) const
1433
0
{
1434
0
    *x1 = rect->x1;
1435
0
    *y1 = rect->y1;
1436
0
    *x2 = rect->x2;
1437
0
    *y2 = rect->y2;
1438
0
}
1439
1440
void Annot::setRect(const PDFRectangle &rectA)
1441
0
{
1442
0
    setRect(rectA.x1, rectA.y1, rectA.x2, rectA.y2);
1443
0
}
1444
1445
void Annot::setRect(double x1, double y1, double x2, double y2)
1446
0
{
1447
0
    if (x1 < x2) {
1448
0
        rect->x1 = x1;
1449
0
        rect->x2 = x2;
1450
0
    } else {
1451
0
        rect->x1 = x2;
1452
0
        rect->x2 = x1;
1453
0
    }
1454
1455
0
    if (y1 < y2) {
1456
0
        rect->y1 = y1;
1457
0
        rect->y2 = y2;
1458
0
    } else {
1459
0
        rect->y1 = y2;
1460
0
        rect->y2 = y1;
1461
0
    }
1462
1463
0
    auto a = std::make_unique<Array>(doc->getXRef());
1464
0
    a->add(Object(rect->x1));
1465
0
    a->add(Object(rect->y1));
1466
0
    a->add(Object(rect->x2));
1467
0
    a->add(Object(rect->y2));
1468
1469
0
    update("Rect", Object(std::move(a)));
1470
0
    invalidateAppearance();
1471
0
}
1472
1473
bool Annot::inRect(double x, double y) const
1474
0
{
1475
0
    return rect->contains(x, y);
1476
0
}
1477
1478
void Annot::update(const char *key, Object &&value)
1479
0
{
1480
0
    annotLocker();
1481
    /* Set M to current time, unless we are updating M itself */
1482
0
    if (strcmp(key, "M") != 0) {
1483
0
        modified = timeToDateString(nullptr);
1484
1485
0
        annotObj.dictSet("M", Object(std::string { modified->toStr() }));
1486
0
    }
1487
1488
0
    annotObj.dictSet(key, std::move(value));
1489
1490
0
    doc->getXRef()->setModifiedObject(&annotObj, ref);
1491
1492
0
    hasBeenUpdated = true;
1493
0
}
1494
1495
void Annot::setContents(std::unique_ptr<GooString> &&new_content)
1496
0
{
1497
0
    annotLocker();
1498
1499
0
    if (new_content) {
1500
0
        contents = GooString(new_content->toStr());
1501
        // append the unicode marker <FE FF> if needed
1502
0
        if (!hasUnicodeByteOrderMark(contents.toStr())) {
1503
0
            prependUnicodeByteOrderMark(contents.toNonConstStr());
1504
0
        }
1505
0
    } else {
1506
0
        contents.clear();
1507
0
    }
1508
1509
0
    update("Contents", Object(std::string { contents.toStr() }));
1510
0
}
1511
1512
void Annot::setName(GooString *new_name)
1513
0
{
1514
0
    annotLocker();
1515
1516
0
    if (new_name) {
1517
0
        name = new_name->copy();
1518
0
    } else {
1519
0
        name = std::make_unique<GooString>();
1520
0
    }
1521
1522
0
    update("NM", Object(std::string { name->toStr() }));
1523
0
}
1524
1525
void Annot::setModified(std::unique_ptr<GooString> new_modified)
1526
0
{
1527
0
    annotLocker();
1528
1529
0
    if (new_modified) {
1530
0
        modified = std::move(new_modified);
1531
0
        update("M", Object(std::string { modified->toStr() }));
1532
0
    } else {
1533
0
        modified.reset(nullptr);
1534
0
        update("M", Object::null());
1535
0
    }
1536
0
}
1537
1538
void Annot::setFlags(unsigned int new_flags)
1539
0
{
1540
0
    annotLocker();
1541
0
    flags = new_flags;
1542
0
    update("F", Object(static_cast<int>(flags)));
1543
0
}
1544
1545
void Annot::setBorder(std::unique_ptr<AnnotBorder> &&new_border)
1546
0
{
1547
0
    annotLocker();
1548
1549
0
    if (new_border) {
1550
0
        Object obj1 = new_border->writeToObject(doc->getXRef());
1551
0
        update(new_border->getType() == AnnotBorder::typeArray ? "Border" : "BS", std::move(obj1));
1552
0
        border = std::move(new_border);
1553
0
    } else {
1554
0
        border = nullptr;
1555
0
    }
1556
0
    invalidateAppearance();
1557
0
}
1558
1559
void Annot::setColor(std::unique_ptr<AnnotColor> &&new_color)
1560
0
{
1561
0
    annotLocker();
1562
1563
0
    if (new_color) {
1564
0
        Object obj1 = new_color->writeToObject(doc->getXRef());
1565
0
        update("C", std::move(obj1));
1566
0
        color = std::move(new_color);
1567
0
    } else {
1568
0
        color = nullptr;
1569
0
    }
1570
0
    invalidateAppearance();
1571
0
}
1572
1573
void Annot::setPage(int pageIndex, bool updateP)
1574
0
{
1575
0
    annotLocker();
1576
0
    Page *pageobj = doc->getPage(pageIndex);
1577
0
    Object obj1 = Object::null();
1578
1579
0
    if (pageobj) {
1580
0
        const Ref pageRef = pageobj->getRef();
1581
0
        obj1 = Object(pageRef);
1582
0
        page = pageIndex;
1583
0
    } else {
1584
0
        page = 0;
1585
0
    }
1586
1587
0
    if (updateP) {
1588
0
        update("P", std::move(obj1));
1589
0
    }
1590
0
}
1591
1592
void Annot::setAppearanceState(const char *state)
1593
0
{
1594
0
    annotLocker();
1595
0
    if (!state) {
1596
0
        return;
1597
0
    }
1598
1599
0
    appearState = state;
1600
0
    appearBBox = nullptr;
1601
1602
0
    update("AS", Object::name(state));
1603
1604
    // The appearance state determines the current appearance stream
1605
0
    if (appearStreams) {
1606
0
        assert(appearState.has_value());
1607
0
        appearance = appearStreams->getAppearanceStream(AnnotAppearance::appearNormal, appearState.value());
1608
0
    } else {
1609
0
        appearance.setToNull();
1610
0
    }
1611
0
}
1612
1613
void Annot::invalidateAppearance()
1614
0
{
1615
0
    annotLocker();
1616
0
    updatedAppearanceStream = Ref::INVALID();
1617
0
    if (appearStreams) { // Remove existing appearance streams
1618
0
        appearStreams->removeAllStreams();
1619
0
    }
1620
0
    appearStreams = nullptr;
1621
0
    appearState = std::nullopt;
1622
0
    appearBBox = nullptr;
1623
0
    appearance.setToNull();
1624
1625
0
    Object obj2 = annotObj.dictLookup("AP");
1626
0
    if (!obj2.isNull()) {
1627
0
        update("AP", Object::null()); // Remove AP
1628
0
    }
1629
1630
0
    obj2 = annotObj.dictLookup("AS");
1631
0
    if (!obj2.isNull()) {
1632
0
        update("AS", Object::null()); // Remove AS
1633
0
    }
1634
0
}
1635
1636
double Annot::getXMin() const
1637
0
{
1638
0
    return rect->x1;
1639
0
}
1640
1641
double Annot::getYMin() const
1642
0
{
1643
0
    return rect->y1;
1644
0
}
1645
1646
double Annot::getXMax() const
1647
0
{
1648
0
    return rect->x2;
1649
0
}
1650
1651
double Annot::getYMax() const
1652
0
{
1653
0
    return rect->y2;
1654
0
}
1655
1656
void Annot::readArrayNum(const Object &pdfArray, int key, double *value)
1657
0
{
1658
0
    Object valueObject = pdfArray.arrayGet(key);
1659
0
    if (valueObject.isNum()) {
1660
0
        *value = valueObject.getNum();
1661
0
    } else {
1662
0
        *value = 0;
1663
0
        ok = false;
1664
0
    }
1665
0
}
1666
1667
void Annot::removeReferencedObjects()
1668
0
{
1669
    // Remove appearance streams (if any)
1670
0
    invalidateAppearance();
1671
0
}
1672
1673
0
Annot::~Annot() = default;
1674
1675
void AnnotAppearanceBuilder::setDrawColor(const AnnotColor &drawColor, bool fill)
1676
0
{
1677
0
    const std::array<double, 4> &values = drawColor.getValues();
1678
1679
0
    switch (drawColor.getSpace()) {
1680
0
    case AnnotColor::colorCMYK:
1681
0
        GooString::appendf(appearBuf, "{0:.5f} {1:.5f} {2:.5f} {3:.5f} {4:c}\n", values[0], values[1], values[2], values[3], fill ? 'k' : 'K');
1682
0
        break;
1683
0
    case AnnotColor::colorRGB:
1684
0
        GooString::appendf(appearBuf, "{0:.5f} {1:.5f} {2:.5f} {3:s}\n", values[0], values[1], values[2], fill ? "rg" : "RG");
1685
0
        break;
1686
0
    case AnnotColor::colorGray:
1687
0
        GooString::appendf(appearBuf, "{0:.5f} {1:c}\n", values[0], fill ? 'g' : 'G');
1688
0
        break;
1689
0
    case AnnotColor::colorTransparent:
1690
0
    default:
1691
0
        break;
1692
0
    }
1693
0
}
1694
1695
void AnnotAppearanceBuilder::setTextFont(const std::string &fontName, double fontSize)
1696
0
{
1697
0
    if (!fontName.empty()) {
1698
0
        GooString::appendf(appearBuf, "/{0:s} {1:.2f} Tf\n", fontName.c_str(), fontSize);
1699
0
    }
1700
0
}
1701
1702
void AnnotAppearanceBuilder::setLineStyleForBorder(const AnnotBorder &border)
1703
0
{
1704
0
    switch (border.getStyle()) {
1705
0
    case AnnotBorder::borderDashed:
1706
0
        appearBuf.append("[");
1707
0
        for (double dash : border.getDash()) {
1708
0
            GooString::appendf(appearBuf, " {0:.2f}", dash);
1709
0
        }
1710
0
        appearBuf.append(" ] 0 d\n");
1711
0
        break;
1712
0
    default:
1713
0
        appearBuf.append("[] 0 d\n");
1714
0
        break;
1715
0
    }
1716
0
    GooString::appendf(appearBuf, "{0:.2f} w\n", border.getWidth());
1717
0
}
1718
1719
// Draw an (approximate) circle of radius <r> centered at (<cx>, <cy>).
1720
// If <fill> is true, the circle is filled; otherwise it is stroked.
1721
void AnnotAppearanceBuilder::drawCircle(double cx, double cy, double r, bool fill)
1722
0
{
1723
0
    if (fill) {
1724
0
        drawEllipse(cx, cy, r, r, true, false);
1725
0
    } else {
1726
0
        drawEllipse(cx, cy, r, r, false, true);
1727
0
    }
1728
0
}
1729
1730
// Draw an (approximate) ellipse of radius <rx> on x-axis and <ry> on y-axis, centered at (<cx>, <cy>).
1731
// If <fill> is true, the ellipse is filled with current color for non-stroking operations.
1732
// If <stroke> is true, the ellipse path ist stroked with current color and color space for stroking operations.
1733
// Path will be closed if either fill or stroke is true; otherwise it's left open.
1734
void AnnotAppearanceBuilder::drawEllipse(double cx, double cy, double rx, double ry, bool fill, bool stroke)
1735
0
{
1736
0
    GooString::appendf(appearBuf, "{0:.2f} {1:.2f} m\n", cx + rx, cy);
1737
0
    GooString::appendf(appearBuf, "{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n", cx + rx, cy + bezierCircle * ry, cx + bezierCircle * rx, cy + ry, cx, cy + ry);
1738
0
    GooString::appendf(appearBuf, "{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n", cx - bezierCircle * rx, cy + ry, cx - rx, cy + bezierCircle * ry, cx - rx, cy);
1739
0
    GooString::appendf(appearBuf, "{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n", cx - rx, cy - bezierCircle * ry, cx - bezierCircle * rx, cy - ry, cx, cy - ry);
1740
0
    GooString::appendf(appearBuf, "{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n", cx + bezierCircle * rx, cy - ry, cx + rx, cy - bezierCircle * ry, cx + rx, cy);
1741
0
    if (!fill && stroke) {
1742
0
        appearBuf.append("s\n");
1743
0
    } else if (fill && !stroke) {
1744
0
        appearBuf.append("f\n");
1745
0
    } else if (fill && stroke) {
1746
0
        appearBuf.append("b\n");
1747
0
    }
1748
0
}
1749
1750
// Draw the top-left half of an (approximate) circle of radius <r>
1751
// centered at (<cx>, <cy>).
1752
void AnnotAppearanceBuilder::drawCircleTopLeft(double cx, double cy, double r)
1753
0
{
1754
0
    double r2;
1755
1756
0
    r2 = r / std::numbers::sqrt2;
1757
0
    GooString::appendf(appearBuf, "{0:.2f} {1:.2f} m\n", cx + r2, cy + r2);
1758
0
    GooString::appendf(appearBuf, "{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n", cx + (1 - bezierCircle) * r2, cy + (1 + bezierCircle) * r2, cx - (1 - bezierCircle) * r2, cy + (1 + bezierCircle) * r2, cx - r2, cy + r2);
1759
0
    GooString::appendf(appearBuf, "{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n", cx - (1 + bezierCircle) * r2, cy + (1 - bezierCircle) * r2, cx - (1 + bezierCircle) * r2, cy - (1 - bezierCircle) * r2, cx - r2, cy - r2);
1760
0
    appearBuf.append("S\n");
1761
0
}
1762
1763
// Draw the bottom-right half of an (approximate) circle of radius <r>
1764
// centered at (<cx>, <cy>).
1765
void AnnotAppearanceBuilder::drawCircleBottomRight(double cx, double cy, double r)
1766
0
{
1767
0
    double r2;
1768
1769
0
    r2 = r / std::numbers::sqrt2;
1770
0
    GooString::appendf(appearBuf, "{0:.2f} {1:.2f} m\n", cx - r2, cy - r2);
1771
0
    GooString::appendf(appearBuf, "{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n", cx - (1 - bezierCircle) * r2, cy - (1 + bezierCircle) * r2, cx + (1 - bezierCircle) * r2, cy - (1 + bezierCircle) * r2, cx + r2, cy - r2);
1772
0
    GooString::appendf(appearBuf, "{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n", cx + (1 + bezierCircle) * r2, cy - (1 - bezierCircle) * r2, cx + (1 + bezierCircle) * r2, cy + (1 - bezierCircle) * r2, cx + r2, cy + r2);
1773
0
    appearBuf.append("S\n");
1774
0
}
1775
1776
void AnnotAppearanceBuilder::drawLineEndSquare(double x, double y, double size, bool fill, const Matrix &m)
1777
0
{
1778
0
    const double halfSize { size / 2. };
1779
0
    const double x1[3] { x - size, x - size, x };
1780
0
    const double y1[3] { y + halfSize, y - halfSize, y - halfSize };
1781
0
    double tx, ty;
1782
1783
0
    m.transform(x, y + halfSize, &tx, &ty);
1784
0
    appendf("{0:.2f} {1:.2f} m\n", tx, ty);
1785
0
    for (int i = 0; i < 3; i++) {
1786
0
        m.transform(x1[i], y1[i], &tx, &ty);
1787
0
        appendf("{0:.2f} {1:.2f} l\n", tx, ty);
1788
0
    }
1789
0
    appearBuf.append(fill ? "b\n" : "s\n");
1790
0
}
1791
1792
void AnnotAppearanceBuilder::drawLineEndCircle(double x, double y, double size, bool fill, const Matrix &m)
1793
0
{
1794
0
    const double halfSize { size / 2. };
1795
0
    const double x1[4] { x, x - halfSize - bezierCircle * halfSize, x - size, x - halfSize + bezierCircle * halfSize };
1796
0
    const double x2[4] { x - halfSize + bezierCircle * halfSize, x - size, x - halfSize - bezierCircle * halfSize, x };
1797
0
    const double x3[4] { x - halfSize, x - size, x - halfSize, x };
1798
0
    const double y1[4] { y + bezierCircle * halfSize, y + halfSize, y - bezierCircle * halfSize, y - halfSize };
1799
0
    const double y2[4] { y + halfSize, y + bezierCircle * halfSize, y - halfSize, y - bezierCircle * halfSize };
1800
0
    const double y3[4] { y + halfSize, y, y - halfSize, y };
1801
0
    double tx[3];
1802
0
    double ty[3];
1803
1804
0
    m.transform(x, y, &tx[0], &ty[0]);
1805
0
    GooString::appendf(appearBuf, "{0:.2f} {1:.2f} m\n", tx[0], ty[0]);
1806
0
    for (int i = 0; i < 4; i++) {
1807
0
        m.transform(x1[i], y1[i], &tx[0], &ty[0]);
1808
0
        m.transform(x2[i], y2[i], &tx[1], &ty[1]);
1809
0
        m.transform(x3[i], y3[i], &tx[2], &ty[2]);
1810
0
        GooString::appendf(appearBuf, "{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n", tx[0], ty[0], tx[1], ty[1], tx[2], ty[2]);
1811
0
    }
1812
0
    appearBuf.append(fill ? "b\n" : "s\n");
1813
0
}
1814
1815
void AnnotAppearanceBuilder::drawLineEndDiamond(double x, double y, double size, bool fill, const Matrix &m)
1816
0
{
1817
0
    const double halfSize { size / 2. };
1818
0
    const double x1[3] { x - halfSize, x - size, x - halfSize };
1819
0
    const double y1[3] { y + halfSize, y, y - halfSize };
1820
0
    double tx, ty;
1821
1822
0
    m.transform(x, y, &tx, &ty);
1823
0
    appendf("{0:.2f} {1:.2f} m\n", tx, ty);
1824
0
    for (int i = 0; i < 3; i++) {
1825
0
        m.transform(x1[i], y1[i], &tx, &ty);
1826
0
        appendf("{0:.2f} {1:.2f} l\n", tx, ty);
1827
0
    }
1828
0
    appearBuf.append(fill ? "b\n" : "s\n");
1829
0
}
1830
1831
void AnnotAppearanceBuilder::drawLineEndArrow(double x, double y, double size, int orientation, bool isOpen, bool fill, const Matrix &m)
1832
0
{
1833
0
    const double alpha { std::numbers::pi / 6. };
1834
0
    const double xOffs { orientation * size };
1835
0
    const double yOffs { tan(alpha) * size };
1836
0
    double tx, ty;
1837
1838
0
    m.transform(x - xOffs, y + yOffs, &tx, &ty);
1839
0
    appendf("{0:.2f} {1:.2f} m\n", tx, ty);
1840
0
    m.transform(x, y, &tx, &ty);
1841
0
    appendf("{0:.2f} {1:.2f} l\n", tx, ty);
1842
0
    m.transform(x - xOffs, y - yOffs, &tx, &ty);
1843
0
    appendf("{0:.2f} {1:.2f} l\n", tx, ty);
1844
1845
0
    if (isOpen) {
1846
0
        appearBuf.append("S\n");
1847
0
    } else {
1848
0
        appearBuf.append(fill ? "b\n" : "s\n");
1849
0
    }
1850
0
}
1851
1852
void AnnotAppearanceBuilder::drawLineEndSlash(double x, double y, double size, const Matrix &m)
1853
0
{
1854
0
    const double halfSize { size / 2. };
1855
0
    const double xOffs { cos(std::numbers::pi / 3.) * halfSize };
1856
0
    double tx, ty;
1857
1858
0
    m.transform(x - xOffs, y - halfSize, &tx, &ty);
1859
0
    appendf("{0:.2f} {1:.2f} m\n", tx, ty);
1860
0
    m.transform(x + xOffs, y + halfSize, &tx, &ty);
1861
0
    appendf("{0:.2f} {1:.2f} l\n", tx, ty);
1862
0
    appearBuf.append("S\n");
1863
0
}
1864
1865
void AnnotAppearanceBuilder::drawLineEnding(AnnotLineEndingStyle endingStyle, double x, double y, double size, bool fill, const Matrix &m)
1866
0
{
1867
0
    switch (endingStyle) {
1868
0
    case annotLineEndingSquare:
1869
0
        drawLineEndSquare(x, y, size, fill, m);
1870
0
        break;
1871
0
    case annotLineEndingCircle:
1872
0
        drawLineEndCircle(x, y, size, fill, m);
1873
0
        break;
1874
0
    case annotLineEndingDiamond:
1875
0
        drawLineEndDiamond(x, y, size, fill, m);
1876
0
        break;
1877
0
    case annotLineEndingOpenArrow:
1878
0
        drawLineEndArrow(x, y, size, 1, true, fill, m);
1879
0
        break;
1880
0
    case annotLineEndingClosedArrow:
1881
0
        drawLineEndArrow(x, y, size, 1, false, fill, m);
1882
0
        break;
1883
0
    case annotLineEndingButt: {
1884
0
        const double halfSize { size / 2. };
1885
0
        double tx, ty;
1886
0
        m.transform(x, y + halfSize, &tx, &ty);
1887
0
        appendf("{0:.2f} {1:.2f} m\n", tx, ty);
1888
0
        m.transform(x, y - halfSize, &tx, &ty);
1889
0
        appendf("{0:.2f} {1:.2f} l S\n", tx, ty);
1890
0
    } break;
1891
0
    case annotLineEndingROpenArrow:
1892
0
        drawLineEndArrow(x, y, size, -1, true, fill, m);
1893
0
        break;
1894
0
    case annotLineEndingRClosedArrow:
1895
0
        drawLineEndArrow(x, y, size, -1, false, fill, m);
1896
0
        break;
1897
0
    case annotLineEndingSlash:
1898
0
        drawLineEndSlash(x, y, size, m);
1899
0
        break;
1900
0
    default:
1901
0
        break;
1902
0
    }
1903
0
}
1904
1905
double AnnotAppearanceBuilder::lineEndingXShorten(AnnotLineEndingStyle endingStyle, double size)
1906
0
{
1907
0
    switch (endingStyle) {
1908
0
    case annotLineEndingCircle:
1909
0
    case annotLineEndingClosedArrow:
1910
0
    case annotLineEndingDiamond:
1911
0
    case annotLineEndingSquare:
1912
0
        return size;
1913
0
    default:
1914
0
        break;
1915
0
    }
1916
0
    return 0;
1917
0
}
1918
1919
double AnnotAppearanceBuilder::lineEndingXExtendBBox(AnnotLineEndingStyle endingStyle, double size)
1920
0
{
1921
0
    switch (endingStyle) {
1922
0
    case annotLineEndingRClosedArrow:
1923
0
    case annotLineEndingROpenArrow:
1924
0
        return size;
1925
0
    case annotLineEndingSlash:
1926
0
        return cos(std::numbers::pi / 3.) * size / 2.;
1927
0
    default:
1928
0
        break;
1929
0
    }
1930
0
    return 0;
1931
0
}
1932
1933
Object Annot::createForm(const std::string &appearBuf, const std::array<double, 4> &bbox, bool transparencyGroup, std::unique_ptr<Dict> resDict)
1934
0
{
1935
0
    return createForm(appearBuf, bbox, transparencyGroup, resDict ? Object(std::move(resDict)) : Object());
1936
0
}
1937
1938
Object Annot::createForm(const std::string &appearBuf, const std::array<double, 4> &bbox, bool transparencyGroup, Object &&resDictObject)
1939
0
{
1940
0
    auto appearDict = std::make_unique<Dict>(doc->getXRef());
1941
0
    appearDict->set("Length", Object(static_cast<int>(appearBuf.size())));
1942
0
    appearDict->set("Subtype", Object::name("Form"));
1943
1944
0
    auto a = std::make_unique<Array>(doc->getXRef());
1945
0
    a->add(Object(bbox[0]));
1946
0
    a->add(Object(bbox[1]));
1947
0
    a->add(Object(bbox[2]));
1948
0
    a->add(Object(bbox[3]));
1949
0
    appearDict->set("BBox", Object(std::move(a)));
1950
0
    if (transparencyGroup) {
1951
0
        auto d = std::make_unique<Dict>(doc->getXRef());
1952
0
        d->set("S", Object::name("Transparency"));
1953
0
        appearDict->set("Group", Object(std::move(d)));
1954
0
    }
1955
0
    if (resDictObject.isDict()) {
1956
0
        appearDict->set("Resources", std::move(resDictObject));
1957
0
    }
1958
1959
0
    std::vector<char> data { appearBuf.c_str(), appearBuf.c_str() + appearBuf.size() };
1960
0
    auto mStream = std::make_unique<AutoFreeMemStream>(std::move(data), Object(std::move(appearDict)));
1961
0
    return Object(std::move(mStream));
1962
0
}
1963
1964
std::unique_ptr<Dict> Annot::createResourcesDict(const char *formName, Object &&formStream, const char *stateName, double opacity, const char *blendMode)
1965
0
{
1966
0
    auto gsDict = std::make_unique<Dict>(doc->getXRef());
1967
0
    if (opacity != 1) {
1968
0
        gsDict->set("CA", Object(opacity));
1969
0
        gsDict->set("ca", Object(opacity));
1970
0
    }
1971
0
    if (blendMode) {
1972
0
        gsDict->set("BM", Object::name(blendMode));
1973
0
    }
1974
0
    auto stateDict = std::make_unique<Dict>(doc->getXRef());
1975
0
    stateDict->set(stateName, Object(std::move(gsDict)));
1976
1977
0
    auto resDict = std::make_unique<Dict>(doc->getXRef());
1978
0
    resDict->set("ExtGState", Object(std::move(stateDict)));
1979
0
    if (formName) {
1980
0
        auto formDict = std::make_unique<Dict>(doc->getXRef());
1981
0
        formDict->set(formName, std::move(formStream));
1982
0
        resDict->set("XObject", Object(std::move(formDict)));
1983
0
    }
1984
1985
0
    return resDict;
1986
0
}
1987
1988
Object Annot::getAppearanceResDict()
1989
0
{
1990
0
    Object obj1, obj2;
1991
1992
    // Fetch appearance's resource dict (if any)
1993
0
    obj1 = appearance.fetch(doc->getXRef());
1994
0
    if (obj1.isStream()) {
1995
0
        obj2 = obj1.getStream()->getDict()->lookup("Resources");
1996
0
        if (obj2.isDict()) {
1997
0
            return obj2;
1998
0
        }
1999
0
    }
2000
2001
0
    return Object::null();
2002
0
}
2003
2004
bool Annot::isVisible(bool printing) const
2005
0
{
2006
    // check the flags
2007
0
    if ((flags & flagHidden) || (printing && !(flags & flagPrint)) || (!printing && (flags & flagNoView))) {
2008
0
        return false;
2009
0
    }
2010
2011
    // check the OC
2012
0
    const OCGs *optContentConfig = doc->getCatalog()->getOptContentConfig();
2013
0
    if (optContentConfig) {
2014
0
        if (!optContentConfig->optContentIsVisible(&oc)) {
2015
0
            return false;
2016
0
        }
2017
0
    }
2018
2019
0
    return true;
2020
0
}
2021
2022
int Annot::getRotation() const
2023
0
{
2024
0
    Page *pageobj = doc->getPage(page);
2025
0
    assert(pageobj != nullptr);
2026
2027
0
    if (flags & flagNoRotate) {
2028
0
        return (360 - pageobj->getRotate()) % 360;
2029
0
    }
2030
0
    return 0;
2031
0
}
2032
2033
void Annot::draw(Gfx *gfx, bool printing)
2034
0
{
2035
0
    annotLocker();
2036
0
    if (!isVisible(printing)) {
2037
0
        return;
2038
0
    }
2039
2040
    // draw the appearance stream
2041
0
    Object obj = appearance.fetch(gfx->getXRef());
2042
0
    gfx->drawAnnot(&obj, nullptr, color.get(), rect->x1, rect->y1, rect->x2, rect->y2, getRotation());
2043
0
}
2044
2045
void Annot::setNewAppearance(Object &&newAppearance)
2046
0
{
2047
0
    setNewAppearance(std::move(newAppearance), false);
2048
0
}
2049
2050
void Annot::setNewAppearance(Object &&newAppearance, bool keepAppearState)
2051
0
{
2052
0
    if (newAppearance.isNull()) {
2053
0
        return;
2054
0
    }
2055
2056
0
    annotLocker();
2057
0
    if (newAppearance.getType() == ObjType::objStream) {
2058
0
        Object oldAS = annotObj.dictLookup("AS");
2059
0
        invalidateAppearance();
2060
0
        appearance = std::move(newAppearance);
2061
2062
0
        updatedAppearanceStream = doc->getXRef()->addIndirectObject(appearance);
2063
2064
0
        Object obj1 = Object(std::make_unique<Dict>(doc->getXRef()));
2065
0
        obj1.dictAdd("N", Object(updatedAppearanceStream));
2066
0
        update("AP", std::move(obj1));
2067
0
        Object updatedAP = annotObj.dictLookup("AP");
2068
0
        appearStreams = std::make_unique<AnnotAppearance>(doc, &updatedAP);
2069
2070
0
        if (keepAppearState && !oldAS.isNull()) {
2071
0
            appearState = oldAS.getNameString();
2072
0
            update("AS", std::move(oldAS));
2073
0
        } else {
2074
0
            update("AS", Object::name("N"));
2075
0
        }
2076
0
    } else {
2077
0
        appearStreams = std::make_unique<AnnotAppearance>(doc, &newAppearance);
2078
0
        update("AP", std::move(newAppearance));
2079
2080
0
        if (appearStreams && appearState) {
2081
0
            appearance = appearStreams->getAppearanceStream(AnnotAppearance::appearNormal, appearState.value());
2082
0
        }
2083
0
    }
2084
0
}
2085
2086
Object Annot::getAppearance() const
2087
0
{
2088
0
    return appearance.fetch(doc->getXRef());
2089
0
}
2090
2091
//------------------------------------------------------------------------
2092
// AnnotPopup
2093
//------------------------------------------------------------------------
2094
2095
0
AnnotPopup::AnnotPopup(PDFDoc *docA, const PDFRectangle &rectA) : Annot(docA, rectA)
2096
0
{
2097
0
    type = typePopup;
2098
2099
0
    annotObj.dictSet("Subtype", Object::name("Popup"));
2100
0
    initialize(annotObj.getDict());
2101
0
}
2102
2103
0
AnnotPopup::AnnotPopup(PDFDoc *docA, Object &&dictObject, const Object &obj) : Annot(docA, std::move(dictObject), obj)
2104
0
{
2105
0
    type = typePopup;
2106
0
    initialize(annotObj.getDict());
2107
0
}
2108
2109
AnnotPopup::~AnnotPopup() = default;
2110
2111
void AnnotPopup::initialize(Dict *dict)
2112
0
{
2113
0
    const Object &parentObj = dict->lookupNF("Parent");
2114
0
    if (parentObj.isRef()) {
2115
0
        parentRef = parentObj.getRef();
2116
0
    } else {
2117
0
        parentRef = Ref::INVALID();
2118
0
    }
2119
2120
0
    open = dict->lookup("Open").getBoolWithDefaultValue(false);
2121
0
}
2122
2123
void AnnotPopup::setParent(Annot *parentA)
2124
0
{
2125
0
    parentRef = parentA->getRef();
2126
0
    update("Parent", Object(parentRef));
2127
0
}
2128
2129
void AnnotPopup::setOpen(bool openA)
2130
0
{
2131
0
    open = openA;
2132
0
    update("Open", Object(open));
2133
0
}
2134
2135
//------------------------------------------------------------------------
2136
// AnnotMarkup
2137
//------------------------------------------------------------------------
2138
0
AnnotMarkup::AnnotMarkup(PDFDoc *docA, const PDFRectangle &rectA) : Annot(docA, rectA)
2139
0
{
2140
0
    initialize(docA, annotObj.getDict());
2141
0
}
2142
2143
0
AnnotMarkup::AnnotMarkup(PDFDoc *docA, Object &&dictObject, const Object &obj) : Annot(docA, std::move(dictObject), obj)
2144
0
{
2145
0
    initialize(docA, annotObj.getDict());
2146
0
}
2147
2148
0
AnnotMarkup::~AnnotMarkup() = default;
2149
2150
void AnnotMarkup::initialize(PDFDoc *docA, Dict *dict)
2151
0
{
2152
0
    Object obj1;
2153
2154
0
    obj1 = dict->lookup("T");
2155
0
    if (obj1.isString()) {
2156
0
        label = obj1.takeString();
2157
0
    }
2158
2159
0
    Object popupObj = dict->lookup("Popup");
2160
0
    const Object &obj2 = dict->lookupNF("Popup");
2161
0
    if (popupObj.isDict() && obj2.isRef()) {
2162
0
        popup = std::make_unique<AnnotPopup>(docA, std::move(popupObj), obj2);
2163
0
    }
2164
2165
0
    opacity = dict->lookup("CA").getNumWithDefaultValue(1.0);
2166
2167
0
    obj1 = dict->lookup("CreationDate");
2168
0
    if (obj1.isString()) {
2169
0
        date = obj1.takeString();
2170
0
    }
2171
2172
0
    const Object &irtObj = dict->lookupNF("IRT");
2173
0
    if (irtObj.isRef()) {
2174
0
        inReplyTo = irtObj.getRef();
2175
0
    } else {
2176
0
        inReplyTo = Ref::INVALID();
2177
0
    }
2178
2179
0
    obj1 = dict->lookup("Subj");
2180
0
    if (obj1.isString()) {
2181
0
        subject = obj1.takeString();
2182
0
    }
2183
2184
0
    obj1 = dict->lookup("RT");
2185
0
    if (obj1.isName()) {
2186
0
        const std::string &replyName = obj1.getNameString();
2187
2188
0
        if (replyName == "R") {
2189
0
            replyTo = replyTypeR;
2190
0
        } else if (replyName == "Group") {
2191
0
            replyTo = replyTypeGroup;
2192
0
        } else {
2193
0
            replyTo = replyTypeR;
2194
0
        }
2195
0
    } else {
2196
0
        replyTo = replyTypeR;
2197
0
    }
2198
2199
0
    obj1 = dict->lookup("ExData");
2200
0
    if (obj1.isDict()) {
2201
0
        exData = parseAnnotExternalData(obj1.getDict());
2202
0
    } else {
2203
0
        exData = annotExternalDataMarkupUnknown;
2204
0
    }
2205
0
}
2206
2207
void AnnotMarkup::setLabel(std::unique_ptr<GooString> &&new_label)
2208
0
{
2209
0
    if (new_label) {
2210
0
        label = std::move(new_label);
2211
        // append the unicode marker <FE FF> if needed
2212
0
        if (!hasUnicodeByteOrderMark(label->toStr())) {
2213
0
            prependUnicodeByteOrderMark(label->toNonConstStr());
2214
0
        }
2215
0
    } else {
2216
0
        label = std::make_unique<GooString>();
2217
0
    }
2218
2219
0
    update("T", Object(std::string { label->toStr() }));
2220
0
}
2221
2222
void AnnotMarkup::setPopup(std::shared_ptr<AnnotPopup> new_popup)
2223
0
{
2224
    // If there exists an old popup annotation that is already
2225
    // associated with a page, then we need to remove that
2226
    // popup annotation from the page. Otherwise we would have
2227
    // dangling references to it.
2228
0
    if (popup && popup->getPageNum() != 0) {
2229
0
        Page *pageobj = doc->getPage(popup->getPageNum());
2230
0
        if (pageobj) {
2231
0
            pageobj->removeAnnot(popup);
2232
0
        }
2233
0
    }
2234
2235
0
    if (new_popup) {
2236
0
        const Ref popupRef = new_popup->getRef();
2237
0
        update("Popup", Object(popupRef));
2238
2239
0
        new_popup->setParent(this);
2240
0
        popup = std::move(new_popup);
2241
2242
        // If this annotation is already added to a page, then we
2243
        // add the new popup annotation to the same page.
2244
0
        if (page != 0) {
2245
0
            Page *pageobj = doc->getPage(page);
2246
0
            assert(pageobj != nullptr); // pageobj should exist in doc (see setPage())
2247
2248
0
            pageobj->addAnnot(popup);
2249
0
        }
2250
0
    } else {
2251
0
        popup = nullptr;
2252
0
    }
2253
0
}
2254
2255
void AnnotMarkup::setOpacity(double opacityA)
2256
0
{
2257
0
    opacity = opacityA;
2258
0
    update("CA", Object(opacity));
2259
0
    invalidateAppearance();
2260
0
}
2261
2262
void AnnotMarkup::setDate(std::unique_ptr<GooString> new_date)
2263
0
{
2264
0
    if (new_date) {
2265
0
        date = std::move(new_date);
2266
0
        update("CreationDate", Object(std::string { date->toStr() }));
2267
0
    } else {
2268
0
        date.reset(nullptr);
2269
0
        update("CreationDate", Object::null());
2270
0
    }
2271
0
}
2272
2273
void AnnotMarkup::removeReferencedObjects()
2274
0
{
2275
0
    Page *pageobj = doc->getPage(page);
2276
0
    assert(pageobj != nullptr); // We're called when removing an annot from a page
2277
2278
    // Remove popup
2279
0
    if (popup) {
2280
0
        pageobj->removeAnnot(popup);
2281
0
    }
2282
2283
0
    Annot::removeReferencedObjects();
2284
0
}
2285
2286
//------------------------------------------------------------------------
2287
// AnnotText
2288
//------------------------------------------------------------------------
2289
2290
0
AnnotText::AnnotText(PDFDoc *docA, const PDFRectangle &rectA) : AnnotMarkup(docA, rectA)
2291
0
{
2292
0
    type = typeText;
2293
0
    flags |= flagNoZoom | flagNoRotate;
2294
2295
0
    annotObj.dictSet("Subtype", Object::name("Text"));
2296
0
    initialize(annotObj.getDict());
2297
0
}
2298
2299
0
AnnotText::AnnotText(PDFDoc *docA, Object &&dictObject, const Object &obj) : AnnotMarkup(docA, std::move(dictObject), obj)
2300
0
{
2301
2302
0
    type = typeText;
2303
0
    flags |= flagNoZoom | flagNoRotate;
2304
0
    initialize(annotObj.getDict());
2305
0
}
2306
2307
0
AnnotText::~AnnotText() = default;
2308
2309
void AnnotText::initialize(Dict *dict)
2310
0
{
2311
0
    Object obj1;
2312
2313
0
    open = dict->lookup("Open").getBoolWithDefaultValue(false);
2314
2315
0
    obj1 = dict->lookup("Name");
2316
0
    if (obj1.isName()) {
2317
0
        icon = obj1.getNameString();
2318
0
    } else {
2319
0
        icon = std::string { "Note" };
2320
0
    }
2321
2322
0
    obj1 = dict->lookup("StateModel");
2323
0
    if (obj1.isString()) {
2324
0
        const std::string &modelName = obj1.getString();
2325
2326
0
        Object obj2 = dict->lookup("State");
2327
0
        if (obj2.isString()) {
2328
0
            const std::string &stateName = obj2.getString();
2329
2330
0
            if (stateName == "Marked") {
2331
0
                state = stateMarked;
2332
0
            } else if (stateName == "Unmarked") {
2333
0
                state = stateUnmarked;
2334
0
            } else if (stateName == "Accepted") {
2335
0
                state = stateAccepted;
2336
0
            } else if (stateName == "Rejected") {
2337
0
                state = stateRejected;
2338
0
            } else if (stateName == "Cancelled") {
2339
0
                state = stateCancelled;
2340
0
            } else if (stateName == "Completed") {
2341
0
                state = stateCompleted;
2342
0
            } else if (stateName == "None") {
2343
0
                state = stateNone;
2344
0
            } else {
2345
0
                state = stateUnknown;
2346
0
            }
2347
0
        } else {
2348
0
            state = stateUnknown;
2349
0
        }
2350
2351
0
        if (modelName == "Marked") {
2352
0
            switch (state) {
2353
0
            case stateUnknown:
2354
0
                state = stateMarked;
2355
0
                break;
2356
0
            case stateAccepted:
2357
0
            case stateRejected:
2358
0
            case stateCancelled:
2359
0
            case stateCompleted:
2360
0
            case stateNone:
2361
0
                state = stateUnknown;
2362
0
                break;
2363
0
            default:
2364
0
                break;
2365
0
            }
2366
0
        } else if (modelName == "Review") {
2367
0
            switch (state) {
2368
0
            case stateUnknown:
2369
0
                state = stateNone;
2370
0
                break;
2371
0
            case stateMarked:
2372
0
            case stateUnmarked:
2373
0
                state = stateUnknown;
2374
0
                break;
2375
0
            default:
2376
0
                break;
2377
0
            }
2378
0
        } else {
2379
0
            state = stateUnknown;
2380
0
        }
2381
0
    } else {
2382
0
        state = stateUnknown;
2383
0
    }
2384
0
}
2385
2386
void AnnotText::setOpen(bool openA)
2387
0
{
2388
0
    open = openA;
2389
0
    update("Open", Object(open));
2390
0
}
2391
2392
void AnnotText::setIcon(const std::string &new_icon)
2393
0
{
2394
0
    if (icon == new_icon) {
2395
0
        return;
2396
0
    }
2397
2398
0
    if (!new_icon.empty()) {
2399
0
        icon = new_icon;
2400
0
    } else {
2401
0
        icon = "Note";
2402
0
    }
2403
2404
0
    update("Name", Object::name(icon));
2405
0
    invalidateAppearance();
2406
0
}
2407
2408
#define ANNOT_TEXT_AP_NOTE                                                                                                                                                                                                                     \
2409
0
    "3.602 24 m 20.398 24 l 22.387 24 24 22.387 24 20.398 c 24 3.602 l 24\n"                                                                                                                                                                   \
2410
0
    "1.613 22.387 0 20.398 0 c 3.602 0 l 1.613 0 0 1.613 0 3.602 c 0 20.398\n"                                                                                                                                                                 \
2411
0
    "l 0 22.387 1.613 24 3.602 24 c h\n"                                                                                                                                                                                                       \
2412
0
    "3.602 24 m f\n"                                                                                                                                                                                                                           \
2413
0
    "0.533333 0.541176 0.521569 RG 2 w\n"                                                                                                                                                                                                      \
2414
0
    "1 J\n"                                                                                                                                                                                                                                    \
2415
0
    "1 j\n"                                                                                                                                                                                                                                    \
2416
0
    "[] 0.0 d\n"                                                                                                                                                                                                                               \
2417
0
    "4 M 9 18 m 4 18 l 4 7 4 4 6 3 c 20 3 l 18 4 18 7 18 18 c 17 18 l S\n"                                                                                                                                                                     \
2418
0
    "1.5 w\n"                                                                                                                                                                                                                                  \
2419
0
    "0 j\n"                                                                                                                                                                                                                                    \
2420
0
    "10 16 m 14 21 l S\n"                                                                                                                                                                                                                      \
2421
0
    "1.85625 w\n"                                                                                                                                                                                                                              \
2422
0
    "1 j\n"                                                                                                                                                                                                                                    \
2423
0
    "15.07 20.523 m 15.07 19.672 14.379 18.977 13.523 18.977 c 12.672 18.977\n"                                                                                                                                                                \
2424
0
    "11.977 19.672 11.977 20.523 c 11.977 21.379 12.672 22.07 13.523 22.07 c\n"                                                                                                                                                                \
2425
0
    "14.379 22.07 15.07 21.379 15.07 20.523 c h\n"                                                                                                                                                                                             \
2426
0
    "15.07 20.523 m S\n"                                                                                                                                                                                                                       \
2427
0
    "1 w\n"                                                                                                                                                                                                                                    \
2428
0
    "0 j\n"                                                                                                                                                                                                                                    \
2429
0
    "6.5 13.5 m 15.5 13.5 l S\n"                                                                                                                                                                                                               \
2430
0
    "6.5 10.5 m 13.5 10.5 l S\n"                                                                                                                                                                                                               \
2431
0
    "6.801 7.5 m 15.5 7.5 l S\n"                                                                                                                                                                                                               \
2432
0
    "0.729412 0.741176 0.713725 RG 2 w\n"                                                                                                                                                                                                      \
2433
0
    "1 j\n"                                                                                                                                                                                                                                    \
2434
0
    "9 19 m 4 19 l 4 8 4 5 6 4 c 20 4 l 18 5 18 8 18 19 c 17 19 l S\n"                                                                                                                                                                         \
2435
0
    "1.5 w\n"                                                                                                                                                                                                                                  \
2436
0
    "0 j\n"                                                                                                                                                                                                                                    \
2437
0
    "10 17 m 14 22 l S\n"                                                                                                                                                                                                                      \
2438
0
    "1.85625 w\n"                                                                                                                                                                                                                              \
2439
0
    "1 j\n"                                                                                                                                                                                                                                    \
2440
0
    "15.07 21.523 m 15.07 20.672 14.379 19.977 13.523 19.977 c 12.672 19.977\n"                                                                                                                                                                \
2441
0
    "11.977 20.672 11.977 21.523 c 11.977 22.379 12.672 23.07 13.523 23.07 c\n"                                                                                                                                                                \
2442
0
    "14.379 23.07 15.07 22.379 15.07 21.523 c h\n"                                                                                                                                                                                             \
2443
0
    "15.07 21.523 m S\n"                                                                                                                                                                                                                       \
2444
0
    "1 w\n"                                                                                                                                                                                                                                    \
2445
0
    "0 j\n"                                                                                                                                                                                                                                    \
2446
0
    "6.5 14.5 m 15.5 14.5 l S\n"                                                                                                                                                                                                               \
2447
0
    "6.5 11.5 m 13.5 11.5 l S\n"                                                                                                                                                                                                               \
2448
0
    "6.801 8.5 m 15.5 8.5 l S\n"
2449
2450
#define ANNOT_TEXT_AP_COMMENT                                                                                                                                                                                                                  \
2451
0
    "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n"                                                                                                                                                                   \
2452
0
    "2.477 21.523 1 19.699 1 c 4.301 1 l 2.477 1 1 2.477 1 4.301 c 1 19.699\n"                                                                                                                                                                 \
2453
0
    "l 1 21.523 2.477 23 4.301 23 c h\n"                                                                                                                                                                                                       \
2454
0
    "4.301 23 m f\n"                                                                                                                                                                                                                           \
2455
0
    "0.533333 0.541176 0.521569 RG 2 w\n"                                                                                                                                                                                                      \
2456
0
    "0 J\n"                                                                                                                                                                                                                                    \
2457
0
    "1 j\n"                                                                                                                                                                                                                                    \
2458
0
    "[] 0.0 d\n"                                                                                                                                                                                                                               \
2459
0
    "4 M 8 20 m 16 20 l 18.363 20 20 18.215 20 16 c 20 13 l 20 10.785 18.363 9\n"                                                                                                                                                              \
2460
0
    "16 9 c 13 9 l 8 3 l 8 9 l 8 9 l 5.637 9 4 10.785 4 13 c 4 16 l 4 18.215\n"                                                                                                                                                                \
2461
0
    "5.637 20 8 20 c h\n"                                                                                                                                                                                                                      \
2462
0
    "8 20 m S\n"                                                                                                                                                                                                                               \
2463
0
    "0.729412 0.741176 0.713725 RG 8 21 m 16 21 l 18.363 21 20 19.215 20 17\n"                                                                                                                                                                 \
2464
0
    "c 20 14 l 20 11.785 18.363 10\n"                                                                                                                                                                                                          \
2465
0
    "16 10 c 13 10 l 8 4 l 8 10 l 8 10 l 5.637 10 4 11.785 4 14 c 4 17 l 4\n"                                                                                                                                                                  \
2466
0
    "19.215 5.637 21 8 21 c h\n"                                                                                                                                                                                                               \
2467
0
    "8 21 m S\n"
2468
2469
#define ANNOT_TEXT_AP_KEY                                                                                                                                                                                                                      \
2470
0
    "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n"                                                                                                                                                                   \
2471
0
    "2.477 21.523 1 19.699 1 c 4.301 1 l 2.477 1 1 2.477 1 4.301 c 1 19.699\n"                                                                                                                                                                 \
2472
0
    "l 1 21.523 2.477 23 4.301 23 c h\n"                                                                                                                                                                                                       \
2473
0
    "4.301 23 m f\n"                                                                                                                                                                                                                           \
2474
0
    "0.533333 0.541176 0.521569 RG 2 w\n"                                                                                                                                                                                                      \
2475
0
    "1 J\n"                                                                                                                                                                                                                                    \
2476
0
    "0 j\n"                                                                                                                                                                                                                                    \
2477
0
    "[] 0.0 d\n"                                                                                                                                                                                                                               \
2478
0
    "4 M 11.895 18.754 m 13.926 20.625 17.09 20.496 18.961 18.465 c 20.832\n"                                                                                                                                                                  \
2479
0
    "16.434 20.699 13.27 18.668 11.398 c 17.164 10.016 15.043 9.746 13.281\n"                                                                                                                                                                  \
2480
0
    "10.516 c 12.473 9.324 l 11.281 10.078 l 9.547 8.664 l 9.008 6.496 l\n"                                                                                                                                                                    \
2481
0
    "7.059 6.059 l 6.34 4.121 l 5.543 3.668 l 3.375 4.207 l 2.938 6.156 l\n"                                                                                                                                                                   \
2482
0
    "10.57 13.457 l 9.949 15.277 10.391 17.367 11.895 18.754 c h\n"                                                                                                                                                                            \
2483
0
    "11.895 18.754 m S\n"                                                                                                                                                                                                                      \
2484
0
    "1.5 w\n"                                                                                                                                                                                                                                  \
2485
0
    "16.059 15.586 m 16.523 15.078 17.316 15.043 17.824 15.512 c 18.332\n"                                                                                                                                                                     \
2486
0
    "15.98 18.363 16.77 17.895 17.277 c 17.43 17.785 16.637 17.816 16.129\n"                                                                                                                                                                   \
2487
0
    "17.352 c 15.621 16.883 15.59 16.094 16.059 15.586 c h\n"                                                                                                                                                                                  \
2488
0
    "16.059 15.586 m S\n"                                                                                                                                                                                                                      \
2489
0
    "0.729412 0.741176 0.713725 RG 2 w\n"                                                                                                                                                                                                      \
2490
0
    "11.895 19.754 m 13.926 21.625 17.09 21.496 18.961 19.465 c 20.832\n"                                                                                                                                                                      \
2491
0
    "17.434 20.699 14.27 18.668 12.398 c 17.164 11.016 15.043 10.746 13.281\n"                                                                                                                                                                 \
2492
0
    "11.516 c 12.473 10.324 l 11.281 11.078 l 9.547 9.664 l 9.008 7.496 l\n"                                                                                                                                                                   \
2493
0
    "7.059 7.059 l 6.34 5.121 l 5.543 4.668 l 3.375 5.207 l 2.938 7.156 l\n"                                                                                                                                                                   \
2494
0
    "10.57 14.457 l 9.949 16.277 10.391 18.367 11.895 19.754 c h\n"                                                                                                                                                                            \
2495
0
    "11.895 19.754 m S\n"                                                                                                                                                                                                                      \
2496
0
    "1.5 w\n"                                                                                                                                                                                                                                  \
2497
0
    "16.059 16.586 m 16.523 16.078 17.316 16.043 17.824 16.512 c 18.332\n"                                                                                                                                                                     \
2498
0
    "16.98 18.363 17.77 17.895 18.277 c 17.43 18.785 16.637 18.816 16.129\n"                                                                                                                                                                   \
2499
0
    "18.352 c 15.621 17.883 15.59 17.094 16.059 16.586 c h\n"                                                                                                                                                                                  \
2500
0
    "16.059 16.586 m S\n"
2501
2502
#define ANNOT_TEXT_AP_HELP                                                                                                                                                                                                                     \
2503
0
    "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n"                                                                                                                                                                   \
2504
0
    "2.477 21.523 1 19.699 1 c 4.301 1 l 2.477 1 1 2.477 1 4.301 c 1 19.699\n"                                                                                                                                                                 \
2505
0
    "l 1 21.523 2.477 23 4.301 23 c h\n"                                                                                                                                                                                                       \
2506
0
    "4.301 23 m f\n"                                                                                                                                                                                                                           \
2507
0
    "0.533333 0.541176 0.521569 RG 2.5 w\n"                                                                                                                                                                                                    \
2508
0
    "1 J\n"                                                                                                                                                                                                                                    \
2509
0
    "1 j\n"                                                                                                                                                                                                                                    \
2510
0
    "[] 0.0 d\n"                                                                                                                                                                                                                               \
2511
0
    "4 M 8.289 16.488 m 8.824 17.828 10.043 18.773 11.473 18.965 c 12.902 19.156\n"                                                                                                                                                            \
2512
0
    "14.328 18.559 15.195 17.406 c 16.062 16.254 16.242 14.723 15.664 13.398\n"                                                                                                                                                                \
2513
0
    "c S\n"                                                                                                                                                                                                                                    \
2514
0
    "0 j\n"                                                                                                                                                                                                                                    \
2515
0
    "12 8 m 12 12 16 11 16 15 c S\n"                                                                                                                                                                                                           \
2516
0
    "1.539286 w\n"                                                                                                                                                                                                                             \
2517
0
    "1 j\n"                                                                                                                                                                                                                                    \
2518
0
    "q 1 0 0 -0.999991 0 24 cm\n"                                                                                                                                                                                                              \
2519
0
    "12.684 20.891 m 12.473 21.258 12.004 21.395 11.629 21.196 c 11.254\n"                                                                                                                                                                     \
2520
0
    "20.992 11.105 20.531 11.297 20.149 c 11.488 19.77 11.945 19.61 12.332\n"                                                                                                                                                                  \
2521
0
    "19.789 c 12.719 19.969 12.891 20.426 12.719 20.817 c S Q\n"                                                                                                                                                                               \
2522
0
    "0.729412 0.741176 0.713725 RG 2.5 w\n"                                                                                                                                                                                                    \
2523
0
    "8.289 17.488 m 9.109 19.539 11.438 20.535 13.488 19.711 c 15.539 18.891\n"                                                                                                                                                                \
2524
0
    "16.535 16.562 15.711 14.512 c 15.699 14.473 15.684 14.438 15.664 14.398\n"                                                                                                                                                                \
2525
0
    "c S\n"                                                                                                                                                                                                                                    \
2526
0
    "0 j\n"                                                                                                                                                                                                                                    \
2527
0
    "12 9 m 12 13 16 12 16 16 c S\n"                                                                                                                                                                                                           \
2528
0
    "1.539286 w\n"                                                                                                                                                                                                                             \
2529
0
    "1 j\n"                                                                                                                                                                                                                                    \
2530
0
    "q 1 0 0 -0.999991 0 24 cm\n"                                                                                                                                                                                                              \
2531
0
    "12.684 19.891 m 12.473 20.258 12.004 20.395 11.629 20.195 c 11.254\n"                                                                                                                                                                     \
2532
0
    "19.992 11.105 19.531 11.297 19.149 c 11.488 18.77 11.945 18.61 12.332\n"                                                                                                                                                                  \
2533
0
    "18.789 c 12.719 18.969 12.891 19.426 12.719 19.817 c S Q\n"
2534
2535
#define ANNOT_TEXT_AP_NEW_PARAGRAPH                                                                                                                                                                                                            \
2536
0
    "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n"                                                                                                                                                                   \
2537
0
    "2.477 21.523 1 19.699 1 c 4.301 1 l 2.477 1 1 2.477 1 4.301 c 1 19.699\n"                                                                                                                                                                 \
2538
0
    "l 1 21.523 2.477 23 4.301 23 c h\n"                                                                                                                                                                                                       \
2539
0
    "4.301 23 m f\n"                                                                                                                                                                                                                           \
2540
0
    "0.533333 0.541176 0.521569 RG 4 w\n"                                                                                                                                                                                                      \
2541
0
    "0 J\n"                                                                                                                                                                                                                                    \
2542
0
    "2 j\n"                                                                                                                                                                                                                                    \
2543
0
    "[] 0.0 d\n"                                                                                                                                                                                                                               \
2544
0
    "4 M q 1 0 0 -1 0 24 cm\n"                                                                                                                                                                                                                 \
2545
0
    "9.211 11.988 m 8.449 12.07 7.711 11.707 7.305 11.059 c 6.898 10.41\n"                                                                                                                                                                     \
2546
0
    "6.898 9.59 7.305 8.941 c 7.711 8.293 8.449 7.93 9.211 8.012 c S Q\n"                                                                                                                                                                      \
2547
0
    "1.004413 w\n"                                                                                                                                                                                                                             \
2548
0
    "1 J\n"                                                                                                                                                                                                                                    \
2549
0
    "1 j\n"                                                                                                                                                                                                                                    \
2550
0
    "q 1 0 0 -0.991232 0 24 cm\n"                                                                                                                                                                                                              \
2551
0
    "18.07 11.511 m 15.113 10.014 l 12.199 11.602 l 12.711 8.323 l 10.301\n"                                                                                                                                                                   \
2552
0
    "6.045 l 13.574 5.517 l 14.996 2.522 l 16.512 5.474 l 19.801 5.899 l\n"                                                                                                                                                                    \
2553
0
    "17.461 8.252 l 18.07 11.511 l h\n"                                                                                                                                                                                                        \
2554
0
    "18.07 11.511 m S Q\n"                                                                                                                                                                                                                     \
2555
0
    "2 w\n"                                                                                                                                                                                                                                    \
2556
0
    "0 j\n"                                                                                                                                                                                                                                    \
2557
0
    "11 17 m 10 17 l 10 3 l S\n"                                                                                                                                                                                                               \
2558
0
    "14 3 m 14 13 l S\n"                                                                                                                                                                                                                       \
2559
0
    "0.729412 0.741176 0.713725 RG 4 w\n"                                                                                                                                                                                                      \
2560
0
    "0 J\n"                                                                                                                                                                                                                                    \
2561
0
    "2 j\n"                                                                                                                                                                                                                                    \
2562
0
    "q 1 0 0 -1 0 24 cm\n"                                                                                                                                                                                                                     \
2563
0
    "9.211 10.988 m 8.109 11.105 7.125 10.309 7.012 9.211 c 6.895 8.109\n"                                                                                                                                                                     \
2564
0
    "7.691 7.125 8.789 7.012 c 8.93 6.996 9.07 6.996 9.211 7.012 c S Q\n"                                                                                                                                                                      \
2565
0
    "1.004413 w\n"                                                                                                                                                                                                                             \
2566
0
    "1 J\n"                                                                                                                                                                                                                                    \
2567
0
    "1 j\n"                                                                                                                                                                                                                                    \
2568
0
    "q 1 0 0 -0.991232 0 24 cm\n"                                                                                                                                                                                                              \
2569
0
    "18.07 10.502 m 15.113 9.005 l 12.199 10.593 l 12.711 7.314 l 10.301\n"                                                                                                                                                                    \
2570
0
    "5.036 l 13.574 4.508 l 14.996 1.513 l 16.512 4.465 l 19.801 4.891 l\n"                                                                                                                                                                    \
2571
0
    "17.461 7.243 l 18.07 10.502 l h\n"                                                                                                                                                                                                        \
2572
0
    "18.07 10.502 m S Q\n"                                                                                                                                                                                                                     \
2573
0
    "2 w\n"                                                                                                                                                                                                                                    \
2574
0
    "0 j\n"                                                                                                                                                                                                                                    \
2575
0
    "11 18 m 10 18 l 10 4 l S\n"                                                                                                                                                                                                               \
2576
0
    "14 4 m 14 14 l S\n"
2577
2578
#define ANNOT_TEXT_AP_PARAGRAPH                                                                                                                                                                                                                \
2579
0
    "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n"                                                                                                                                                                   \
2580
0
    "2.477 21.523 1 19.699 1 c 4.301 1 l 2.477 1 1 2.477 1 4.301 c 1 19.699\n"                                                                                                                                                                 \
2581
0
    "l 1 21.523 2.477 23 4.301 23 c h\n"                                                                                                                                                                                                       \
2582
0
    "4.301 23 m f\n"                                                                                                                                                                                                                           \
2583
0
    "0.533333 0.541176 0.521569 RG 2 w\n"                                                                                                                                                                                                      \
2584
0
    "1 J\n"                                                                                                                                                                                                                                    \
2585
0
    "1 j\n"                                                                                                                                                                                                                                    \
2586
0
    "[] 0.0 d\n"                                                                                                                                                                                                                               \
2587
0
    "4 M 15 3 m 15 18 l 11 18 l 11 3 l S\n"                                                                                                                                                                                                    \
2588
0
    "4 w\n"                                                                                                                                                                                                                                    \
2589
0
    "q 1 0 0 -1 0 24 cm\n"                                                                                                                                                                                                                     \
2590
0
    "9.777 10.988 m 8.746 10.871 7.973 9.988 8 8.949 c 8.027 7.91 8.844\n"                                                                                                                                                                     \
2591
0
    "7.066 9.879 7.004 c S Q\n"                                                                                                                                                                                                                \
2592
0
    "0.729412 0.741176 0.713725 RG 2 w\n"                                                                                                                                                                                                      \
2593
0
    "15 4 m 15 19 l 11 19 l 11 4 l S\n"                                                                                                                                                                                                        \
2594
0
    "4 w\n"                                                                                                                                                                                                                                    \
2595
0
    "q 1 0 0 -1 0 24 cm\n"                                                                                                                                                                                                                     \
2596
0
    "9.777 9.988 m 8.746 9.871 7.973 8.988 8 7.949 c 8.027 6.91 8.844 6.066\n"                                                                                                                                                                 \
2597
0
    "9.879 6.004 c S Q\n"
2598
2599
#define ANNOT_TEXT_AP_INSERT                                                                                                                                                                                                                   \
2600
0
    "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n"                                                                                                                                                                   \
2601
0
    "2.477 21.523 1 19.699 1 c 4.301 1 l 2.477 1 1 2.477 1 4.301 c 1 19.699\n"                                                                                                                                                                 \
2602
0
    "l 1 21.523 2.477 23 4.301 23 c h\n"                                                                                                                                                                                                       \
2603
0
    "4.301 23 m f\n"                                                                                                                                                                                                                           \
2604
0
    "0.533333 0.541176 0.521569 RG 2 w\n"                                                                                                                                                                                                      \
2605
0
    "1 J\n"                                                                                                                                                                                                                                    \
2606
0
    "0 j\n"                                                                                                                                                                                                                                    \
2607
0
    "[] 0.0 d\n"                                                                                                                                                                                                                               \
2608
0
    "4 M 12 18.012 m 20 18 l S\n"                                                                                                                                                                                                              \
2609
0
    "9 10 m 17 10 l S\n"                                                                                                                                                                                                                       \
2610
0
    "12 14.012 m 20 14 l S\n"                                                                                                                                                                                                                  \
2611
0
    "12 6.012 m 20 6.012 l S\n"                                                                                                                                                                                                                \
2612
0
    "4 12 m 6 10 l 4 8 l S\n"                                                                                                                                                                                                                  \
2613
0
    "4 12 m 4 8 l S\n"                                                                                                                                                                                                                         \
2614
0
    "0.729412 0.741176 0.713725 RG 12 19.012 m 20 19 l S\n"                                                                                                                                                                                    \
2615
0
    "9 11 m 17 11 l S\n"                                                                                                                                                                                                                       \
2616
0
    "12 15.012 m 20 15 l S\n"                                                                                                                                                                                                                  \
2617
0
    "12 7.012 m 20 7.012 l S\n"                                                                                                                                                                                                                \
2618
0
    "4 13 m 6 11 l 4 9 l S\n"                                                                                                                                                                                                                  \
2619
0
    "4 13 m 4 9 l S\n"
2620
2621
#define ANNOT_TEXT_AP_CROSS                                                                                                                                                                                                                    \
2622
0
    "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n"                                                                                                                                                                   \
2623
0
    "2.477 21.523 1 19.699 1 c 4.301 1 l 2.477 1 1 2.477 1 4.301 c 1 19.699\n"                                                                                                                                                                 \
2624
0
    "l 1 21.523 2.477 23 4.301 23 c h\n"                                                                                                                                                                                                       \
2625
0
    "4.301 23 m f\n"                                                                                                                                                                                                                           \
2626
0
    "0.533333 0.541176 0.521569 RG 2.5 w\n"                                                                                                                                                                                                    \
2627
0
    "1 J\n"                                                                                                                                                                                                                                    \
2628
0
    "0 j\n"                                                                                                                                                                                                                                    \
2629
0
    "[] 0.0 d\n"                                                                                                                                                                                                                               \
2630
0
    "4 M 18 5 m 6 17 l S\n"                                                                                                                                                                                                                    \
2631
0
    "6 5 m 18 17 l S\n"                                                                                                                                                                                                                        \
2632
0
    "0.729412 0.741176 0.713725 RG 18 6 m 6 18 l S\n"                                                                                                                                                                                          \
2633
0
    "6 6 m 18 18 l S\n"
2634
2635
#define ANNOT_TEXT_AP_CIRCLE                                                                                                                                                                                                                   \
2636
0
    "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n"                                                                                                                                                                   \
2637
0
    "2.477 21.523 1 19.699 1 c 4.301 1 l 2.477 1 1 2.477 1 4.301 c 1 19.699\n"                                                                                                                                                                 \
2638
0
    "l 1 21.523 2.477 23 4.301 23 c h\n"                                                                                                                                                                                                       \
2639
0
    "4.301 23 m f\n"                                                                                                                                                                                                                           \
2640
0
    "0.533333 0.541176 0.521569 RG 2.5 w\n"                                                                                                                                                                                                    \
2641
0
    "1 J\n"                                                                                                                                                                                                                                    \
2642
0
    "1 j\n"                                                                                                                                                                                                                                    \
2643
0
    "[] 0.0 d\n"                                                                                                                                                                                                                               \
2644
0
    "4 M 19.5 11.5 m 19.5 7.359 16.141 4 12 4 c 7.859 4 4.5 7.359 4.5 11.5 c 4.5\n"                                                                                                                                                            \
2645
0
    "15.641 7.859 19 12 19 c 16.141 19 19.5 15.641 19.5 11.5 c h\n"                                                                                                                                                                            \
2646
0
    "19.5 11.5 m S\n"                                                                                                                                                                                                                          \
2647
0
    "0.729412 0.741176 0.713725 RG 19.5 12.5 m 19.5 8.359 16.141 5 12 5 c\n"                                                                                                                                                                   \
2648
0
    "7.859 5 4.5 8.359 4.5 12.5 c 4.5\n"                                                                                                                                                                                                       \
2649
0
    "16.641 7.859 20 12 20 c 16.141 20 19.5 16.641 19.5 12.5 c h\n"                                                                                                                                                                            \
2650
0
    "19.5 12.5 m S\n"
2651
2652
#define ANNOT_TEXT_AP_CHECK                                                                                                                                                                                                                    \
2653
0
    "0.8 0.2 0.8 RG 4.0 w\n"                                                                                                                                                                                                                   \
2654
0
    "1 J\n"                                                                                                                                                                                                                                    \
2655
0
    "1 j\n"                                                                                                                                                                                                                                    \
2656
0
    "[] 0.0 d\n"                                                                                                                                                                                                                               \
2657
0
    "4 M 6 12 m 10 8 l S\n"                                                                                                                                                                                                                    \
2658
0
    "10 8 m 18 16 l S\n"
2659
2660
#define ANNOT_TEXT_AP_STAR                                                                                                                                                                                                                     \
2661
0
    "0.2 0.8 0.6 RG 2.5 w\n"                                                                                                                                                                                                                   \
2662
0
    "1 J\n"                                                                                                                                                                                                                                    \
2663
0
    "1 j\n"                                                                                                                                                                                                                                    \
2664
0
    "[] 0.0 d\n"                                                                                                                                                                                                                               \
2665
0
    "4 M 12 22 m 15.4 15.8 l 22 14.8 l 17 10 l 18.2 3.2 l 12 6.4 l 5.8 3.2 l 7 10 l 2 14.8 l 8.6 15.8 l h\n"                                                                                                                                   \
2666
0
    "S\n"
2667
2668
#define ANNOT_TEXT_AP_RIGHT_ARROW                                                                                                                                                                                                              \
2669
0
    "0.533333 0.541176 0.521569 RG 2.5 w\n"                                                                                                                                                                                                    \
2670
0
    "1 J\n"                                                                                                                                                                                                                                    \
2671
0
    "1 j\n"                                                                                                                                                                                                                                    \
2672
0
    "[] 0.0 d\n"                                                                                                                                                                                                                               \
2673
0
    "4 M 19.5 12 m 19.5 15.866 16.366 19 12.5 19 c 8.634 19 5.5 15.866 5.5\n"                                                                                                                                                                  \
2674
0
    "12 c 5.5 8.134 8.634 5 12.5 5 c 16.366 5 19.5 8.134 19.5 12 c h\n"                                                                                                                                                                        \
2675
0
    "19.5 12 m S\n"                                                                                                                                                                                                                            \
2676
0
    "8 12 m 16 12 l S\n"                                                                                                                                                                                                                       \
2677
0
    "13 9 m 16 12 l 13 15 l S\n"
2678
2679
#define ANNOT_TEXT_AP_RIGHT_POINTER                                                                                                                                                                                                            \
2680
0
    "0.9 0.4 0.2 rg\n"                                                                                                                                                                                                                         \
2681
0
    "4 M 6 8 m 6 16 l 18 12 l h\n"                                                                                                                                                                                                             \
2682
0
    "f\n"
2683
2684
#define ANNOT_TEXT_AP_UP_ARROW                                                                                                                                                                                                                 \
2685
0
    "0.2 0.6 0.9 RG 2.5 w\n"                                                                                                                                                                                                                   \
2686
0
    "1 J\n"                                                                                                                                                                                                                                    \
2687
0
    "1 j\n"                                                                                                                                                                                                                                    \
2688
0
    "[] 0.0 d\n"                                                                                                                                                                                                                               \
2689
0
    "4 M 12 6 m 12 18 l S\n"                                                                                                                                                                                                                   \
2690
0
    "9 15 m 12 18 l 15 15 l S\n"
2691
2692
#define ANNOT_TEXT_AP_UP_LEFT_ARROW                                                                                                                                                                                                            \
2693
0
    "0.9 0.2 0.8 RG 2.5 w\n"                                                                                                                                                                                                                   \
2694
0
    "1 J\n"                                                                                                                                                                                                                                    \
2695
0
    "1 j\n"                                                                                                                                                                                                                                    \
2696
0
    "[] 0.0 d\n"                                                                                                                                                                                                                               \
2697
0
    "4 M 18 6 m 6 18 l S\n"                                                                                                                                                                                                                    \
2698
0
    "6 18 m 11 18 l S\n"                                                                                                                                                                                                                       \
2699
0
    "6 18 m 6 13 l S\n"
2700
2701
#define ANNOT_TEXT_AP_CROSS_HAIRS                                                                                                                                                                                                              \
2702
0
    "0.2 0.8 0.3 RG 2.5 w\n"                                                                                                                                                                                                                   \
2703
0
    "1 J\n"                                                                                                                                                                                                                                    \
2704
0
    "1 j\n"                                                                                                                                                                                                                                    \
2705
0
    "[] 0.0 d\n"                                                                                                                                                                                                                               \
2706
0
    "4 M 12 10 m 12 14 l S\n"                                                                                                                                                                                                                  \
2707
0
    "10 12 m 14 12 l S\n"                                                                                                                                                                                                                      \
2708
0
    "17 12 m 17 9.239 14.761 7 12 7 c 9.239 7 7 9.239 7 12 c 7 14.761 9.239 17 12 17 c 14.761 17 17 14.761 17 12 c h S\n"
2709
2710
void AnnotText::draw(Gfx *gfx, bool printing)
2711
0
{
2712
0
    double ca = 1;
2713
2714
0
    if (!isVisible(printing)) {
2715
0
        return;
2716
0
    }
2717
2718
0
    annotLocker();
2719
0
    if (appearance.isNull()) {
2720
0
        ca = opacity;
2721
2722
0
        AnnotAppearanceBuilder appearBuilder;
2723
2724
0
        appearBuilder.append("q\n");
2725
0
        if (color) {
2726
0
            appearBuilder.setDrawColor(*color, true);
2727
0
        } else {
2728
0
            appearBuilder.append("1 1 1 rg\n");
2729
0
        }
2730
0
        if (icon == "Note") {
2731
0
            appearBuilder.append(ANNOT_TEXT_AP_NOTE);
2732
0
        } else if (icon == "Comment") {
2733
0
            appearBuilder.append(ANNOT_TEXT_AP_COMMENT);
2734
0
        } else if (icon == "Key") {
2735
0
            appearBuilder.append(ANNOT_TEXT_AP_KEY);
2736
0
        } else if (icon == "Help") {
2737
0
            appearBuilder.append(ANNOT_TEXT_AP_HELP);
2738
0
        } else if (icon == "NewParagraph") {
2739
0
            appearBuilder.append(ANNOT_TEXT_AP_NEW_PARAGRAPH);
2740
0
        } else if (icon == "Paragraph") {
2741
0
            appearBuilder.append(ANNOT_TEXT_AP_PARAGRAPH);
2742
0
        } else if (icon == "Insert") {
2743
0
            appearBuilder.append(ANNOT_TEXT_AP_INSERT);
2744
0
        } else if (icon == "Cross") {
2745
0
            appearBuilder.append(ANNOT_TEXT_AP_CROSS);
2746
0
        } else if (icon == "Circle") {
2747
0
            appearBuilder.append(ANNOT_TEXT_AP_CIRCLE);
2748
0
        } else if (icon == "Check") {
2749
0
            appearBuilder.append(ANNOT_TEXT_AP_CHECK);
2750
0
        } else if (icon == "Star") {
2751
0
            appearBuilder.append(ANNOT_TEXT_AP_STAR);
2752
0
        } else if (icon == "RightArrow") {
2753
0
            appearBuilder.append(ANNOT_TEXT_AP_RIGHT_ARROW);
2754
0
        } else if (icon == "RightPointer") {
2755
0
            appearBuilder.append(ANNOT_TEXT_AP_RIGHT_POINTER);
2756
0
        } else if (icon == "UpArrow") {
2757
0
            appearBuilder.append(ANNOT_TEXT_AP_UP_ARROW);
2758
0
        } else if (icon == "UpLeftArrow") {
2759
0
            appearBuilder.append(ANNOT_TEXT_AP_UP_LEFT_ARROW);
2760
0
        } else if (icon == "CrossHairs") {
2761
0
            appearBuilder.append(ANNOT_TEXT_AP_CROSS_HAIRS);
2762
0
        }
2763
0
        appearBuilder.append("Q\n");
2764
2765
        // Force 24x24 rectangle
2766
0
        PDFRectangle fixedRect(rect->x1, rect->y2 - 24, rect->x1 + 24, rect->y2);
2767
0
        appearBBox = std::make_unique<AnnotAppearanceBBox>(fixedRect);
2768
0
        const std::array<double, 4> bbox = appearBBox->getBBoxRect();
2769
0
        if (ca == 1) {
2770
0
            appearance = createForm(appearBuilder.buffer(), bbox, false, Object {});
2771
0
        } else {
2772
0
            Object aStream = createForm(appearBuilder.buffer(), bbox, true, Object {});
2773
2774
0
            std::string appearBuf("/GS0 gs\n/Fm0 Do");
2775
0
            std::unique_ptr<Dict> resDict = createResourcesDict("Fm0", std::move(aStream), "GS0", ca, nullptr);
2776
0
            appearance = createForm(appearBuf, bbox, false, std::move(resDict));
2777
0
        }
2778
0
    }
2779
2780
    // draw the appearance stream
2781
0
    Object obj = appearance.fetch(gfx->getXRef());
2782
0
    if (appearBBox) {
2783
0
        gfx->drawAnnot(&obj, nullptr, color.get(), appearBBox->getPageXMin(), appearBBox->getPageYMin(), appearBBox->getPageXMax(), appearBBox->getPageYMax(), getRotation());
2784
0
    } else {
2785
0
        gfx->drawAnnot(&obj, nullptr, color.get(), rect->x1, rect->y1, rect->x2, rect->y2, getRotation());
2786
0
    }
2787
0
}
2788
2789
//------------------------------------------------------------------------
2790
// AnnotLink
2791
//------------------------------------------------------------------------
2792
0
AnnotLink::AnnotLink(PDFDoc *docA, const PDFRectangle &rectA) : Annot(docA, rectA)
2793
0
{
2794
0
    type = typeLink;
2795
0
    annotObj.dictSet("Subtype", Object::name("Link"));
2796
0
    initialize(annotObj.getDict());
2797
0
}
2798
2799
0
AnnotLink::AnnotLink(PDFDoc *docA, Object &&dictObject, const Object &obj) : Annot(docA, std::move(dictObject), obj)
2800
0
{
2801
2802
0
    type = typeLink;
2803
0
    initialize(annotObj.getDict());
2804
0
}
2805
2806
0
AnnotLink::~AnnotLink() = default;
2807
2808
void AnnotLink::initialize(Dict *dict)
2809
0
{
2810
0
    Object obj1;
2811
2812
    // look for destination
2813
0
    obj1 = dict->lookup("Dest");
2814
0
    if (!obj1.isNull()) {
2815
0
        action = LinkAction::parseDest(&obj1);
2816
        // look for action
2817
0
    } else {
2818
0
        obj1 = dict->lookup("A");
2819
0
        if (obj1.isDict()) {
2820
0
            action = LinkAction::parseAction(&obj1, doc->getCatalog()->getBaseURI());
2821
0
        }
2822
0
    }
2823
2824
0
    obj1 = dict->lookup("H");
2825
0
    if (obj1.isName()) {
2826
0
        const std::string &effect = obj1.getNameString();
2827
2828
0
        if (effect == "N") {
2829
0
            linkEffect = effectNone;
2830
0
        } else if (effect == "I") {
2831
0
            linkEffect = effectInvert;
2832
0
        } else if (effect == "O") {
2833
0
            linkEffect = effectOutline;
2834
0
        } else if (effect == "P") {
2835
0
            linkEffect = effectPush;
2836
0
        } else {
2837
0
            linkEffect = effectInvert;
2838
0
        }
2839
0
    } else {
2840
0
        linkEffect = effectInvert;
2841
0
    }
2842
    /*
2843
    obj1 = dict->lookup("PA");
2844
    if (obj1.isDict()) {
2845
      uriAction = NULL;
2846
    } else {
2847
      uriAction = NULL;
2848
    }
2849
    obj1.free();
2850
    */
2851
0
    obj1 = dict->lookup("QuadPoints");
2852
0
    if (obj1.isArray()) {
2853
0
        quadrilaterals = std::make_unique<AnnotQuadrilaterals>(*obj1.getArray());
2854
0
    }
2855
2856
0
    obj1 = dict->lookup("BS");
2857
0
    if (obj1.isDict()) {
2858
0
        border = std::make_unique<AnnotBorderBS>(obj1.getDict());
2859
0
    } else if (!border) {
2860
0
        border = std::make_unique<AnnotBorderBS>();
2861
0
    }
2862
0
}
2863
2864
void AnnotLink::draw(Gfx *gfx, bool printing)
2865
0
{
2866
0
    if (!isVisible(printing)) {
2867
0
        return;
2868
0
    }
2869
2870
0
    annotLocker();
2871
    // draw the appearance stream
2872
0
    Object obj = appearance.fetch(gfx->getXRef());
2873
0
    gfx->drawAnnot(&obj, border.get(), color.get(), rect->x1, rect->y1, rect->x2, rect->y2, getRotation());
2874
0
}
2875
2876
//------------------------------------------------------------------------
2877
// AnnotFreeText
2878
//------------------------------------------------------------------------
2879
const double AnnotFreeText::undefinedFontPtSize = 10.;
2880
2881
0
AnnotFreeText::AnnotFreeText(PDFDoc *docA, const PDFRectangle &rectA) : AnnotMarkup(docA, rectA)
2882
0
{
2883
0
    type = typeFreeText;
2884
2885
0
    annotObj.dictSet("Subtype", Object::name("FreeText"));
2886
0
    annotObj.dictSet("DA", Object(std::string {}));
2887
2888
0
    initialize(annotObj.getDict());
2889
0
}
2890
2891
0
AnnotFreeText::AnnotFreeText(PDFDoc *docA, Object &&dictObject, const Object &obj) : AnnotMarkup(docA, std::move(dictObject), obj)
2892
0
{
2893
0
    type = typeFreeText;
2894
0
    initialize(annotObj.getDict());
2895
0
}
2896
2897
0
AnnotFreeText::~AnnotFreeText() = default;
2898
2899
void AnnotFreeText::initialize(Dict *dict)
2900
0
{
2901
0
    Object obj1;
2902
2903
0
    obj1 = dict->lookup("DA");
2904
0
    if (obj1.isString()) {
2905
0
        appearanceString = std::move(obj1.takeString()->toNonConstStr());
2906
0
    } else {
2907
0
        appearanceString.clear();
2908
0
        error(errSyntaxWarning, -1, "Bad appearance for annotation");
2909
0
    }
2910
2911
0
    obj1 = dict->lookup("Q");
2912
0
    if (obj1.isInt()) {
2913
0
        quadding = static_cast<VariableTextQuadding>(obj1.getInt());
2914
0
    } else {
2915
0
        quadding = VariableTextQuadding::leftJustified;
2916
0
    }
2917
2918
0
    obj1 = dict->lookup("DS");
2919
0
    if (obj1.isString()) {
2920
0
        styleString = obj1.takeString();
2921
0
    }
2922
2923
0
    obj1 = dict->lookup("CL");
2924
0
    if (obj1.isArrayOfLengthAtLeast(4)) {
2925
0
        const double x1 = obj1.arrayGet(0).getNumWithDefaultValue(0);
2926
0
        const double y1 = obj1.arrayGet(1).getNumWithDefaultValue(0);
2927
0
        const double x2 = obj1.arrayGet(2).getNumWithDefaultValue(0);
2928
0
        const double y2 = obj1.arrayGet(3).getNumWithDefaultValue(0);
2929
2930
0
        if (obj1.arrayGetLength() == 6) {
2931
0
            const double x3 = obj1.arrayGet(4).getNumWithDefaultValue(0);
2932
0
            const double y3 = obj1.arrayGet(5).getNumWithDefaultValue(0);
2933
0
            calloutLine = std::make_unique<AnnotCalloutMultiLine>(x1, y1, x2, y2, x3, y3);
2934
0
        } else {
2935
0
            calloutLine = std::make_unique<AnnotCalloutLine>(x1, y1, x2, y2);
2936
0
        }
2937
0
    }
2938
2939
0
    obj1 = dict->lookup("IT");
2940
0
    if (obj1.isName()) {
2941
0
        const std::string &intentName = obj1.getNameString();
2942
2943
0
        if (intentName == "FreeText") {
2944
0
            intent = intentFreeText;
2945
0
        } else if (intentName == "FreeTextCallout") {
2946
0
            intent = intentFreeTextCallout;
2947
0
        } else if (intentName == "FreeTextTypeWriter") {
2948
0
            intent = intentFreeTextTypeWriter;
2949
0
        } else {
2950
0
            intent = intentFreeText;
2951
0
        }
2952
0
    } else {
2953
0
        intent = intentFreeText;
2954
0
    }
2955
2956
0
    obj1 = dict->lookup("BS");
2957
0
    if (obj1.isDict()) {
2958
0
        border = std::make_unique<AnnotBorderBS>(obj1.getDict());
2959
0
    } else if (!border) {
2960
0
        border = std::make_unique<AnnotBorderBS>();
2961
0
    }
2962
2963
0
    obj1 = dict->lookup("BE");
2964
0
    if (obj1.isDict()) {
2965
0
        borderEffect = std::make_unique<AnnotBorderEffect>(obj1.getDict());
2966
0
    }
2967
2968
0
    obj1 = dict->lookup("RD");
2969
0
    if (obj1.isArray()) {
2970
0
        rectangle = parseDiffRectangle(obj1.getArray(), rect.get());
2971
0
    }
2972
2973
0
    obj1 = dict->lookup("LE");
2974
0
    if (obj1.isName()) {
2975
0
        endStyle = parseAnnotLineEndingStyle(obj1);
2976
0
    } else {
2977
0
        endStyle = annotLineEndingNone;
2978
0
    }
2979
0
}
2980
2981
void AnnotFreeText::setContents(std::unique_ptr<GooString> &&new_content)
2982
0
{
2983
0
    Annot::setContents(std::move(new_content));
2984
0
    invalidateAppearance();
2985
0
}
2986
2987
void AnnotFreeText::setDefaultAppearance(const DefaultAppearance &da)
2988
0
{
2989
0
    appearanceString = da.toAppearanceString();
2990
2991
0
    update("DA", Object(std::string { appearanceString }));
2992
0
    invalidateAppearance();
2993
0
}
2994
2995
void AnnotFreeText::setQuadding(VariableTextQuadding new_quadding)
2996
0
{
2997
0
    quadding = new_quadding;
2998
0
    update("Q", Object(static_cast<int>(quadding)));
2999
0
    invalidateAppearance();
3000
0
}
3001
3002
void AnnotFreeText::setStyleString(GooString *new_string)
3003
0
{
3004
0
    if (new_string) {
3005
0
        styleString = new_string->copy();
3006
        // append the unicode marker <FE FF> if needed
3007
0
        if (!hasUnicodeByteOrderMark(styleString->toStr())) {
3008
0
            prependUnicodeByteOrderMark(styleString->toNonConstStr());
3009
0
        }
3010
0
    } else {
3011
0
        styleString = std::make_unique<GooString>();
3012
0
    }
3013
3014
0
    update("DS", Object(std::string { styleString->toStr() }));
3015
0
}
3016
3017
void AnnotFreeText::setCalloutLine(std::unique_ptr<AnnotCalloutLine> &&line)
3018
0
{
3019
0
    Object obj1;
3020
0
    if (line == nullptr) {
3021
0
        obj1.setToNull();
3022
0
        calloutLine = nullptr;
3023
0
    } else {
3024
0
        double x1 = line->getX1(), y1 = line->getY1();
3025
0
        double x2 = line->getX2(), y2 = line->getY2();
3026
0
        auto array = std::make_unique<Array>(doc->getXRef());
3027
0
        array->add(Object(x1));
3028
0
        array->add(Object(y1));
3029
0
        array->add(Object(x2));
3030
0
        array->add(Object(y2));
3031
3032
0
        auto *mline = dynamic_cast<AnnotCalloutMultiLine *>(line.get());
3033
0
        if (mline) {
3034
0
            double x3 = mline->getX3(), y3 = mline->getY3();
3035
0
            array->add(Object(x3));
3036
0
            array->add(Object(y3));
3037
0
        }
3038
0
        obj1 = Object(std::move(array));
3039
0
        calloutLine = std::move(line);
3040
0
    }
3041
3042
0
    update("CL", std::move(obj1));
3043
0
    invalidateAppearance();
3044
0
}
3045
3046
void AnnotFreeText::setIntent(AnnotFreeTextIntent new_intent)
3047
0
{
3048
0
    const char *intentName;
3049
3050
0
    intent = new_intent;
3051
0
    if (new_intent == intentFreeText) {
3052
0
        intentName = "FreeText";
3053
0
    } else if (new_intent == intentFreeTextCallout) {
3054
0
        intentName = "FreeTextCallout";
3055
0
    } else { // intentFreeTextTypeWriter
3056
0
        intentName = "FreeTextTypeWriter";
3057
0
    }
3058
0
    update("IT", Object::name(intentName));
3059
0
}
3060
3061
std::unique_ptr<DefaultAppearance> AnnotFreeText::getDefaultAppearance() const
3062
0
{
3063
0
    return std::make_unique<DefaultAppearance>(appearanceString);
3064
0
}
3065
3066
static std::unique_ptr<GfxFont> createAnnotDrawFont(XRef *xref, Dict *fontParentDict, std::string_view resourceName = "AnnotDrawFont", const char *fontname = "Helvetica")
3067
0
{
3068
0
    const Ref dummyRef = { .num = -1, .gen = -1 };
3069
3070
0
    auto fontDict = std::make_unique<Dict>(xref);
3071
0
    fontDict->add("BaseFont", Object::name(fontname));
3072
0
    fontDict->add("Subtype", Object::name("Type1"));
3073
0
    if ((strcmp(fontname, "ZapfDingbats") != 0) && (strcmp(fontname, "Symbol") != 0)) {
3074
0
        fontDict->add("Encoding", Object::name("WinAnsiEncoding"));
3075
0
    }
3076
3077
0
    Object fontsDictObj = fontParentDict->lookup("Font");
3078
0
    if (!fontsDictObj.isDict()) {
3079
0
        fontsDictObj = Object(std::make_unique<Dict>(xref));
3080
0
        fontParentDict->add("Font", fontsDictObj.copy()); // This is not a copy it's a ref
3081
0
    }
3082
3083
0
    auto font = GfxFont::makeFont(xref, resourceName, dummyRef, *fontDict);
3084
0
    fontsDictObj.dictSet(resourceName, Object(std::move(fontDict)));
3085
0
    return font;
3086
0
}
3087
3088
class HorizontalTextLayouter
3089
{
3090
public:
3091
    HorizontalTextLayouter() = default;
3092
3093
    HorizontalTextLayouter(const std::string &text, const Form *form, const GfxFont &font, std::optional<double> availableWidth, const bool noReencode)
3094
0
    {
3095
0
        size_t i = 0;
3096
0
        double blockWidth;
3097
0
        bool newFontNeeded = false;
3098
0
        GooString outputText;
3099
0
        const bool isUnicode = hasUnicodeByteOrderMark(text);
3100
0
        int charCount;
3101
3102
0
        Annot::layoutText(text, &outputText, &i, font, &blockWidth, availableWidth ? *availableWidth : 0.0, &charCount, noReencode, !noReencode ? &newFontNeeded : nullptr);
3103
0
        data.emplace_back(outputText.toStr(), std::string(), blockWidth, charCount);
3104
0
        if (availableWidth) {
3105
0
            *availableWidth -= blockWidth;
3106
0
        }
3107
3108
0
        while (newFontNeeded && (!availableWidth || *availableWidth > 0 || (isUnicode && i == 2) || (!isUnicode && i == 0))) {
3109
0
            if (!form) {
3110
                // There's no fonts to look for, so just skip the characters
3111
0
                i += isUnicode ? 2 : 1;
3112
0
                error(errSyntaxError, -1, "HorizontalTextLayouter, found character that the font can't represent");
3113
0
                newFontNeeded = false;
3114
0
            } else {
3115
0
                Unicode uChar;
3116
0
                if (isUnicode) {
3117
0
                    uChar = static_cast<unsigned char>(text[i]) << 8;
3118
0
                    uChar += static_cast<unsigned char>(text[i + 1]);
3119
0
                } else {
3120
0
                    uChar = pdfDocEncoding[text[i] & 0xff];
3121
0
                }
3122
0
                const std::string auxFontName = form->getFallbackFontForChar(uChar, font);
3123
0
                if (!auxFontName.empty()) {
3124
0
                    std::shared_ptr<GfxFont> auxFont = form->getDefaultResources()->lookupFont(auxFontName);
3125
3126
                    // Here we just layout one char, we don't know if the one afterwards can be layouted with the original font
3127
0
                    GooString auxContents = GooString(text.substr(i, isUnicode ? 2 : 1));
3128
0
                    if (isUnicode) {
3129
0
                        prependUnicodeByteOrderMark(auxContents.toNonConstStr());
3130
0
                    }
3131
0
                    size_t auxI = 0;
3132
0
                    Annot::layoutText(auxContents.toStr(), &outputText, &auxI, *auxFont, &blockWidth, availableWidth ? *availableWidth : 0.0, &charCount, false, &newFontNeeded);
3133
0
                    assert(!newFontNeeded);
3134
0
                    if (availableWidth) {
3135
0
                        *availableWidth -= blockWidth;
3136
0
                    }
3137
                    // layoutText will always at least layout one character even if it doesn't fit in
3138
                    // the given space which makes sense (except in the case of switching fonts, so we control if we ran out of space here manually)
3139
                    // we also need to allow the character if we have not layouted anything yet because otherwise we will end up in an infinite loop
3140
                    // because it is assumed we at least layout one character
3141
0
                    if (!availableWidth || *availableWidth > 0 || (isUnicode && i == 2) || (!isUnicode && i == 0)) {
3142
0
                        i += isUnicode ? 2 : 1;
3143
0
                        data.emplace_back(outputText.toStr(), auxFontName, blockWidth, charCount);
3144
0
                    }
3145
0
                } else {
3146
0
                    error(errSyntaxError, -1, "HorizontalTextLayouter, couldn't find a font for character U+{0:04uX}", uChar);
3147
0
                    newFontNeeded = false;
3148
0
                    i += isUnicode ? 2 : 1;
3149
0
                }
3150
0
            }
3151
            // Now layout the rest of the text with the original font
3152
0
            if (!availableWidth || *availableWidth > 0) {
3153
0
                Annot::layoutText(text, &outputText, &i, font, &blockWidth, availableWidth ? *availableWidth : 0.0, &charCount, false, &newFontNeeded);
3154
0
                if (availableWidth) {
3155
0
                    *availableWidth -= blockWidth;
3156
0
                }
3157
                // layoutText will always at least layout one character even if it doesn't fit in
3158
                // the given space which makes sense (except in the case of switching fonts, so we control if we ran out of space here manually)
3159
0
                if (!availableWidth || *availableWidth > 0) {
3160
0
                    data.emplace_back(outputText.toStr(), std::string(), blockWidth, charCount);
3161
0
                } else {
3162
0
                    i -= isUnicode ? 2 : 1;
3163
0
                }
3164
0
            }
3165
0
        }
3166
0
        consumedText = i;
3167
0
    }
3168
3169
    HorizontalTextLayouter(const HorizontalTextLayouter &) = delete;
3170
    HorizontalTextLayouter &operator=(const HorizontalTextLayouter &) = delete;
3171
3172
    double totalWidth() const
3173
0
    {
3174
0
        double totalWidth = 0;
3175
0
        for (const Data &d : data) {
3176
0
            totalWidth += d.width;
3177
0
        }
3178
0
        return totalWidth;
3179
0
    }
3180
3181
    int totalCharCount() const
3182
0
    {
3183
0
        int total = 0;
3184
0
        for (const Data &d : data) {
3185
0
            total += d.charCount;
3186
0
        }
3187
0
        return total;
3188
0
    }
3189
3190
    struct Data
3191
    {
3192
0
        Data(std::string t, std::string fName, double w, int cc) : text(std::move(t)), fontName(std::move(fName)), width(w), charCount(cc) { }
3193
3194
        const std::string text;
3195
        const std::string fontName;
3196
        const double width;
3197
        const int charCount;
3198
    };
3199
3200
    std::vector<Data> data;
3201
    int consumedText;
3202
};
3203
3204
double Annot::calculateFontSize(const Form *form, const GfxFont &font, const std::string &text, double wMax, double hMax, const bool forceZapfDingbats)
3205
0
{
3206
0
    const bool isUnicode = hasUnicodeByteOrderMark(text);
3207
0
    double fontSize;
3208
3209
0
    for (fontSize = 20; fontSize > 1; --fontSize) {
3210
0
        const double availableWidthInFontSize = wMax / fontSize;
3211
0
        double y = hMax - 3;
3212
0
        size_t i = 0;
3213
0
        while (i < text.size()) {
3214
0
            GooString lineText(text.substr(i));
3215
0
            if (!hasUnicodeByteOrderMark(lineText.toStr()) && isUnicode) {
3216
0
                prependUnicodeByteOrderMark(lineText.toNonConstStr());
3217
0
            }
3218
0
            const HorizontalTextLayouter textLayouter(lineText.toStr(), form, font, availableWidthInFontSize, forceZapfDingbats);
3219
0
            y -= fontSize;
3220
0
            if (i == 0) {
3221
0
                i += textLayouter.consumedText;
3222
0
            } else {
3223
0
                i += textLayouter.consumedText - (isUnicode ? 2 : 0);
3224
0
            }
3225
0
        }
3226
        // approximate the descender for the last line
3227
0
        if (y >= 0.33 * fontSize) {
3228
0
            break;
3229
0
        }
3230
0
    }
3231
0
    return fontSize;
3232
0
}
3233
3234
struct DrawMultiLineTextResult
3235
{
3236
    std::string text;
3237
    int nLines = 0;
3238
};
3239
3240
// if fontName is empty it is assumed it is sent from the outside
3241
// so for text that is in font no Tf is added and for text that is in the aux fonts
3242
// a pair of q/Q is added
3243
static DrawMultiLineTextResult drawMultiLineText(const std::string &text, double availableWidth, const Form *form, const GfxFont &font, const std::string &fontName, double fontSize, VariableTextQuadding quadding, double borderWidth)
3244
0
{
3245
0
    DrawMultiLineTextResult result;
3246
0
    size_t i = 0;
3247
0
    double xPosPrev = 0;
3248
0
    const double availableTextWidthInFontPtSize = availableWidth / fontSize;
3249
0
    while (i < text.size()) {
3250
0
        GooString lineText(text.substr(i));
3251
0
        if (!hasUnicodeByteOrderMark(lineText.toStr()) && hasUnicodeByteOrderMark(text)) {
3252
0
            prependUnicodeByteOrderMark(lineText.toNonConstStr());
3253
0
        }
3254
0
        const HorizontalTextLayouter textLayouter(lineText.toStr(), form, font, availableTextWidthInFontPtSize, false);
3255
3256
0
        const double totalWidth = textLayouter.totalWidth() * fontSize;
3257
3258
0
        auto calculateX = [quadding, availableWidth, totalWidth, borderWidth] {
3259
0
            switch (quadding) {
3260
0
            case VariableTextQuadding::centered:
3261
0
                return (availableWidth - totalWidth) / 2;
3262
0
                break;
3263
0
            case VariableTextQuadding::rightJustified:
3264
0
                return availableWidth - totalWidth - borderWidth;
3265
0
                break;
3266
0
            default: // VariableTextQuadding::lLeftJustified:
3267
0
                return borderWidth;
3268
0
                break;
3269
0
            }
3270
0
        };
3271
0
        const double xPos = calculateX();
3272
3273
0
        AnnotAppearanceBuilder builder;
3274
0
        bool first = true;
3275
0
        double prevBlockWidth = 0;
3276
0
        for (const HorizontalTextLayouter::Data &d : textLayouter.data) {
3277
0
            const std::string &fName = d.fontName.empty() ? fontName : d.fontName;
3278
0
            if (!fName.empty()) {
3279
0
                if (fontName.empty()) {
3280
0
                    builder.append(" q\n");
3281
0
                }
3282
0
                builder.appendf("/{0:s} {1:.2f} Tf\n", fName.c_str(), fontSize);
3283
0
            }
3284
3285
0
            const double yDiff = first ? -fontSize : 0;
3286
0
            const double xDiff = first ? xPos - xPosPrev : prevBlockWidth;
3287
3288
0
            builder.appendf("{0:.2f} {1:.2f} Td\n", xDiff, yDiff);
3289
0
            builder.writeString(d.text);
3290
0
            builder.append(" Tj\n");
3291
0
            first = false;
3292
0
            prevBlockWidth = d.width * fontSize;
3293
3294
0
            if (!fName.empty() && fontName.empty()) {
3295
0
                builder.append(" Q\n");
3296
0
            }
3297
0
        }
3298
0
        xPosPrev = xPos + totalWidth - prevBlockWidth;
3299
3300
0
        result.text += builder.buffer();
3301
0
        result.nLines += 1;
3302
0
        if (i == 0) {
3303
0
            i += textLayouter.consumedText;
3304
0
        } else {
3305
0
            i += textLayouter.consumedText - (hasUnicodeByteOrderMark(text) ? 2 : 0);
3306
0
        }
3307
0
    }
3308
0
    return result;
3309
0
}
3310
3311
void AnnotFreeText::generateFreeTextAppearance()
3312
0
{
3313
0
    double borderWidth, ca = opacity;
3314
3315
0
    AnnotAppearanceBuilder appearBuilder;
3316
0
    appearBuilder.append("q\n");
3317
3318
0
    borderWidth = border->getWidth();
3319
0
    if (borderWidth > 0) {
3320
0
        appearBuilder.setLineStyleForBorder(*border);
3321
0
    }
3322
3323
    // Box size
3324
0
    const double width = rect->x2 - rect->x1;
3325
0
    const double height = rect->y2 - rect->y1;
3326
3327
    // Parse some properties from the appearance string
3328
0
    DefaultAppearance da { appearanceString };
3329
3330
    // Default values
3331
0
    if (da.getFontName().empty()) {
3332
0
        da.setFontName("AnnotDrawFont");
3333
0
    }
3334
0
    if (da.getFontPtSize() <= 0) {
3335
0
        da.setFontPtSize(undefinedFontPtSize);
3336
0
    }
3337
0
    if (!da.getFontColor()) {
3338
0
        da.setFontColor(std::make_unique<AnnotColor>(0, 0, 0));
3339
0
    }
3340
3341
    // Draw box
3342
0
    bool doFill = (color && color->getSpace() != AnnotColor::colorTransparent);
3343
0
    bool doStroke = (borderWidth != 0);
3344
0
    if (doFill || doStroke) {
3345
0
        if (doStroke) {
3346
0
            appearBuilder.setDrawColor(*da.getFontColor(), false); // Border color: same as font color
3347
0
        }
3348
0
        appearBuilder.appendf("{0:.2f} {0:.2f} {1:.2f} {2:.2f} re\n", borderWidth / 2, width - borderWidth, height - borderWidth);
3349
0
        if (doFill) {
3350
0
            appearBuilder.setDrawColor(*color, true);
3351
0
            appearBuilder.append(doStroke ? "B\n" : "f\n");
3352
0
        } else {
3353
0
            appearBuilder.append("S\n");
3354
0
        }
3355
0
    }
3356
3357
    // Setup text clipping
3358
0
    const double textmargin = borderWidth * 2;
3359
0
    const double textwidth = width - 2 * textmargin;
3360
0
    appearBuilder.appendf("{0:.2f} {0:.2f} {1:.2f} {2:.2f} re W n\n", textmargin, textwidth, height - 2 * textmargin);
3361
3362
0
    std::unique_ptr<const GfxFont> font = nullptr;
3363
3364
    // look for font name in the default resources
3365
0
    Form *form = doc->getCatalog()->getForm(); // form is owned by catalog, no need to clean it up
3366
3367
0
    Object resourceObj;
3368
0
    if (form && form->getDefaultResourcesObj() && form->getDefaultResourcesObj()->isDict()) {
3369
0
        resourceObj = form->getDefaultResourcesObj()->copy(); // No real copy, but increment refcount of /DR Dict
3370
3371
0
        Dict *resDict = resourceObj.getDict();
3372
0
        Object fontResources = resDict->lookup("Font"); // The 'Font' subdictionary
3373
3374
0
        if (!fontResources.isDict()) {
3375
0
            error(errSyntaxWarning, -1, "Font subdictionary is not a dictionary");
3376
0
        } else {
3377
            // Get the font dictionary for the actual requested font
3378
0
            Ref fontReference;
3379
0
            Object fontDictionary = fontResources.getDict()->lookup(da.getFontName(), &fontReference);
3380
3381
0
            if (fontDictionary.isDict()) {
3382
0
                font = GfxFont::makeFont(doc->getXRef(), da.getFontName(), fontReference, *fontDictionary.getDict());
3383
0
            } else {
3384
0
                error(errSyntaxWarning, -1, "Font dictionary is not a dictionary");
3385
0
            }
3386
0
        }
3387
0
    }
3388
3389
    // if fontname is not in the default resources, create a Helvetica fake font
3390
0
    if (!font) {
3391
0
        auto fontResDict = std::make_unique<Dict>(doc->getXRef());
3392
0
        font = createAnnotDrawFont(doc->getXRef(), fontResDict.get(), da.getFontName());
3393
0
        resourceObj = Object(std::move(fontResDict));
3394
0
    }
3395
3396
    // Set font state
3397
0
    appearBuilder.setDrawColor(*da.getFontColor(), true);
3398
0
    appearBuilder.appendf("BT 1 0 0 1 {0:.2f} {1:.2f} Tm\n", textmargin, height - textmargin);
3399
0
    const DrawMultiLineTextResult textCommands = drawMultiLineText(contents.toStr(), textwidth, form, *font, da.getFontName(), da.getFontPtSize(), quadding, 0 /*borderWidth*/);
3400
0
    appearBuilder.append(textCommands.text.c_str());
3401
0
    appearBuilder.append("ET Q\n");
3402
3403
0
    const std::array<double, 4> bbox = { 0, 0, rect->x2 - rect->x1, rect->y2 - rect->y1 };
3404
3405
0
    Object newAppearance;
3406
0
    if (ca == 1) {
3407
0
        newAppearance = createForm(appearBuilder.buffer(), bbox, false, std::move(resourceObj));
3408
0
    } else {
3409
0
        Object aStream = createForm(appearBuilder.buffer(), bbox, true, std::move(resourceObj));
3410
3411
0
        std::string appearBuf("/GS0 gs\n/Fm0 Do");
3412
0
        std::unique_ptr<Dict> resDict = createResourcesDict("Fm0", std::move(aStream), "GS0", ca, nullptr);
3413
0
        newAppearance = createForm(appearBuf, bbox, false, std::move(resDict));
3414
0
    }
3415
0
    if (hasBeenUpdated) {
3416
        // We should technically do this for all annots but AnnotFreeText
3417
        // is particularly special since we're potentially embeddeing a font so we really need
3418
        // to set the AP and not let other renderers guess it from the contents
3419
0
        setNewAppearance(std::move(newAppearance));
3420
0
    } else {
3421
0
        appearance = std::move(newAppearance);
3422
0
    }
3423
0
}
3424
3425
void AnnotFreeText::draw(Gfx *gfx, bool printing)
3426
0
{
3427
0
    if (!isVisible(printing)) {
3428
0
        return;
3429
0
    }
3430
3431
0
    annotLocker();
3432
0
    if (appearance.isNull()) {
3433
0
        generateFreeTextAppearance();
3434
0
    }
3435
3436
    // draw the appearance stream
3437
0
    Object obj = appearance.fetch(gfx->getXRef());
3438
0
    gfx->drawAnnot(&obj, nullptr, color.get(), rect->x1, rect->y1, rect->x2, rect->y2, getRotation());
3439
0
}
3440
3441
// Before retrieving the res dict, regenerate the appearance stream if needed,
3442
// because AnnotFreeText::draw needs to store font info in the res dict
3443
Object AnnotFreeText::getAppearanceResDict()
3444
0
{
3445
0
    if (appearance.isNull()) {
3446
0
        generateFreeTextAppearance();
3447
0
    }
3448
0
    return Annot::getAppearanceResDict();
3449
0
}
3450
3451
//------------------------------------------------------------------------
3452
// AnnotLine
3453
//------------------------------------------------------------------------
3454
3455
0
AnnotLine::AnnotLine(PDFDoc *docA, const PDFRectangle &rectA) : AnnotMarkup(docA, rectA)
3456
0
{
3457
0
    type = typeLine;
3458
0
    annotObj.dictSet("Subtype", Object::name("Line"));
3459
3460
0
    initialize(annotObj.getDict());
3461
0
}
3462
3463
0
AnnotLine::AnnotLine(PDFDoc *docA, Object &&dictObject, const Object &obj) : AnnotMarkup(docA, std::move(dictObject), obj)
3464
0
{
3465
0
    type = typeLine;
3466
0
    initialize(annotObj.getDict());
3467
0
}
3468
3469
0
AnnotLine::~AnnotLine() = default;
3470
3471
void AnnotLine::initialize(Dict *dict)
3472
0
{
3473
0
    Object obj1;
3474
3475
0
    obj1 = dict->lookup("L");
3476
0
    if (obj1.isArrayOfLength(4)) {
3477
0
        const double x1 = obj1.arrayGet(0).getNumWithDefaultValue(0);
3478
0
        const double y1 = obj1.arrayGet(1).getNumWithDefaultValue(0);
3479
0
        const double x2 = obj1.arrayGet(2).getNumWithDefaultValue(0);
3480
0
        const double y2 = obj1.arrayGet(3).getNumWithDefaultValue(0);
3481
3482
0
        coord1 = std::make_unique<AnnotCoord>(x1, y1);
3483
0
        coord2 = std::make_unique<AnnotCoord>(x2, y2);
3484
0
    } else {
3485
0
        coord1 = std::make_unique<AnnotCoord>();
3486
0
        coord2 = std::make_unique<AnnotCoord>();
3487
0
    }
3488
3489
0
    obj1 = dict->lookup("LE");
3490
0
    if (obj1.isArrayOfLength(2)) {
3491
0
        Object obj2;
3492
3493
0
        obj2 = obj1.arrayGet(0);
3494
0
        if (obj2.isName()) {
3495
0
            startStyle = parseAnnotLineEndingStyle(obj2);
3496
0
        } else {
3497
0
            startStyle = annotLineEndingNone;
3498
0
        }
3499
3500
0
        obj2 = obj1.arrayGet(1);
3501
0
        if (obj2.isName()) {
3502
0
            endStyle = parseAnnotLineEndingStyle(obj2);
3503
0
        } else {
3504
0
            endStyle = annotLineEndingNone;
3505
0
        }
3506
3507
0
    } else {
3508
0
        startStyle = endStyle = annotLineEndingNone;
3509
0
    }
3510
3511
0
    obj1 = dict->lookup("IC");
3512
0
    if (obj1.isArray()) {
3513
0
        interiorColor = std::make_unique<AnnotColor>(*obj1.getArray());
3514
0
    }
3515
3516
0
    leaderLineLength = dict->lookup("LL").getNumWithDefaultValue(0);
3517
3518
0
    leaderLineExtension = dict->lookup("LLE").getNumWithDefaultValue(0);
3519
0
    if (leaderLineExtension < 0) {
3520
0
        leaderLineExtension = 0;
3521
0
    }
3522
3523
0
    caption = dict->lookup("Cap").getBoolWithDefaultValue(false);
3524
3525
0
    obj1 = dict->lookup("IT");
3526
0
    if (obj1.isName()) {
3527
0
        const std::string &intentName = obj1.getNameString();
3528
3529
0
        if (intentName == "LineArrow") {
3530
0
            intent = intentLineArrow;
3531
0
        } else if (intentName == "LineDimension") {
3532
0
            intent = intentLineDimension;
3533
0
        } else {
3534
0
            intent = intentLineArrow;
3535
0
        }
3536
0
    } else {
3537
0
        intent = intentLineArrow;
3538
0
    }
3539
3540
0
    leaderLineOffset = dict->lookup("LLO").getNumWithDefaultValue(0);
3541
0
    if (leaderLineOffset < 0) {
3542
0
        leaderLineOffset = 0;
3543
0
    }
3544
3545
0
    obj1 = dict->lookup("CP");
3546
0
    if (obj1.isName()) {
3547
0
        const std::string &captionName = obj1.getNameString();
3548
3549
0
        if (captionName == "Inline") {
3550
0
            captionPos = captionPosInline;
3551
0
        } else if (captionName == "Top") {
3552
0
            captionPos = captionPosTop;
3553
0
        } else {
3554
0
            captionPos = captionPosInline;
3555
0
        }
3556
0
    } else {
3557
0
        captionPos = captionPosInline;
3558
0
    }
3559
3560
0
    obj1 = dict->lookup("Measure");
3561
0
    if (obj1.isDict()) {
3562
0
        measure = nullptr;
3563
0
    } else {
3564
0
        measure = nullptr;
3565
0
    }
3566
3567
0
    obj1 = dict->lookup("CO");
3568
0
    if (obj1.isArrayOfLength(2)) {
3569
0
        captionTextHorizontal = obj1.arrayGet(0).getNumWithDefaultValue(0);
3570
0
        captionTextVertical = obj1.arrayGet(1).getNumWithDefaultValue(0);
3571
0
    } else {
3572
0
        captionTextHorizontal = captionTextVertical = 0;
3573
0
    }
3574
3575
0
    obj1 = dict->lookup("BS");
3576
0
    if (obj1.isDict()) {
3577
0
        border = std::make_unique<AnnotBorderBS>(obj1.getDict());
3578
0
    } else if (!border) {
3579
0
        border = std::make_unique<AnnotBorderBS>();
3580
0
    }
3581
0
}
3582
3583
void AnnotLine::setContents(std::unique_ptr<GooString> &&new_content)
3584
0
{
3585
0
    Annot::setContents(std::move(new_content));
3586
0
    if (caption) {
3587
0
        invalidateAppearance();
3588
0
    }
3589
0
}
3590
3591
void AnnotLine::setVertices(double x1, double y1, double x2, double y2)
3592
0
{
3593
0
    coord1 = std::make_unique<AnnotCoord>(x1, y1);
3594
0
    coord2 = std::make_unique<AnnotCoord>(x2, y2);
3595
3596
0
    auto lArray = std::make_unique<Array>(doc->getXRef());
3597
0
    lArray->add(Object(x1));
3598
0
    lArray->add(Object(y1));
3599
0
    lArray->add(Object(x2));
3600
0
    lArray->add(Object(y2));
3601
3602
0
    update("L", Object(std::move(lArray)));
3603
0
    invalidateAppearance();
3604
0
}
3605
3606
void AnnotLine::setStartEndStyle(AnnotLineEndingStyle start, AnnotLineEndingStyle end)
3607
0
{
3608
0
    startStyle = start;
3609
0
    endStyle = end;
3610
3611
0
    auto leArray = std::make_unique<Array>(doc->getXRef());
3612
0
    leArray->add(Object::name(convertAnnotLineEndingStyle(startStyle)));
3613
0
    leArray->add(Object::name(convertAnnotLineEndingStyle(endStyle)));
3614
3615
0
    update("LE", Object(std::move(leArray)));
3616
0
    invalidateAppearance();
3617
0
}
3618
3619
void AnnotLine::setInteriorColor(std::unique_ptr<AnnotColor> &&new_color)
3620
0
{
3621
0
    if (new_color) {
3622
0
        Object obj1 = new_color->writeToObject(doc->getXRef());
3623
0
        update("IC", std::move(obj1));
3624
0
        interiorColor = std::move(new_color);
3625
0
    } else {
3626
0
        interiorColor = nullptr;
3627
0
    }
3628
0
    invalidateAppearance();
3629
0
}
3630
3631
void AnnotLine::setLeaderLineLength(double len)
3632
0
{
3633
0
    leaderLineLength = len;
3634
0
    update("LL", Object(len));
3635
0
    invalidateAppearance();
3636
0
}
3637
3638
void AnnotLine::setLeaderLineExtension(double len)
3639
0
{
3640
0
    leaderLineExtension = len;
3641
0
    update("LLE", Object(len));
3642
3643
    // LL is required if LLE is present
3644
0
    update("LL", Object(leaderLineLength));
3645
0
    invalidateAppearance();
3646
0
}
3647
3648
void AnnotLine::setCaption(bool new_cap)
3649
0
{
3650
0
    caption = new_cap;
3651
0
    update("Cap", Object(new_cap));
3652
0
    invalidateAppearance();
3653
0
}
3654
3655
void AnnotLine::setIntent(AnnotLineIntent new_intent)
3656
0
{
3657
0
    const char *intentName;
3658
3659
0
    intent = new_intent;
3660
0
    if (new_intent == intentLineArrow) {
3661
0
        intentName = "LineArrow";
3662
0
    } else { // intentLineDimension
3663
0
        intentName = "LineDimension";
3664
0
    }
3665
0
    update("IT", Object::name(intentName));
3666
0
}
3667
3668
void AnnotLine::generateLineAppearance()
3669
0
{
3670
0
    double borderWidth, ca = opacity;
3671
0
    bool fill = false;
3672
3673
0
    appearBBox = std::make_unique<AnnotAppearanceBBox>(*rect);
3674
0
    AnnotAppearanceBuilder appearBuilder;
3675
0
    appearBuilder.append("q\n");
3676
0
    if (color) {
3677
0
        appearBuilder.setDrawColor(*color, false);
3678
0
    }
3679
0
    if (interiorColor) {
3680
0
        appearBuilder.setDrawColor(*interiorColor, true);
3681
0
        fill = true;
3682
0
    }
3683
0
    appearBuilder.setLineStyleForBorder(*border);
3684
0
    borderWidth = border->getWidth();
3685
0
    appearBBox->setBorderWidth(std::max(1., borderWidth));
3686
3687
0
    const double x1 = coord1->getX();
3688
0
    const double y1 = coord1->getY();
3689
0
    const double x2 = coord2->getX();
3690
0
    const double y2 = coord2->getY();
3691
3692
    // Main segment length
3693
0
    const double main_len = sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
3694
3695
    // Main segment becomes positive x direction, coord1 becomes (0,0)
3696
0
    Matrix matr;
3697
0
    const double angle = atan2(y2 - y1, x2 - x1);
3698
0
    matr.m[0] = matr.m[3] = cos(angle);
3699
0
    matr.m[1] = sin(angle);
3700
0
    matr.m[2] = -matr.m[1];
3701
0
    matr.m[4] = x1 - rect->x1;
3702
0
    matr.m[5] = y1 - rect->y1;
3703
3704
0
    double tx, ty, captionwidth = 0, captionheight = 0;
3705
0
    AnnotLineCaptionPos actualCaptionPos = captionPos;
3706
0
    const double fontsize = 9;
3707
0
    const double captionhmargin = 2; // Left and right margin (inline caption only)
3708
0
    const double captionmaxwidth = main_len - 2 * captionhmargin;
3709
0
    const double lineendingSize = std::min(6. * borderWidth, main_len / 2);
3710
3711
0
    std::unique_ptr<Dict> fontResDict;
3712
0
    std::unique_ptr<const GfxFont> font;
3713
3714
    // Calculate caption width and height
3715
0
    if (caption) {
3716
0
        fontResDict = std::make_unique<Dict>(doc->getXRef());
3717
0
        font = createAnnotDrawFont(doc->getXRef(), fontResDict.get());
3718
0
        int lines = 0;
3719
0
        size_t i = 0;
3720
0
        while (i < contents.size()) {
3721
0
            GooString out;
3722
0
            double linewidth;
3723
0
            layoutText(contents.toStr(), &out, &i, *font, &linewidth, 0, nullptr, false);
3724
0
            linewidth *= fontsize;
3725
0
            if (linewidth > captionwidth) {
3726
0
                captionwidth = linewidth;
3727
0
            }
3728
0
            ++lines;
3729
0
        }
3730
0
        captionheight = lines * fontsize;
3731
        // If text is longer than available space, turn into captionPosTop
3732
0
        if (captionwidth > captionmaxwidth) {
3733
0
            actualCaptionPos = captionPosTop;
3734
0
        }
3735
0
    } else {
3736
0
        fontResDict = nullptr;
3737
0
        font = nullptr;
3738
0
    }
3739
3740
    // Draw main segment
3741
0
    matr.transform(AnnotAppearanceBuilder::lineEndingXShorten(startStyle, lineendingSize), leaderLineLength, &tx, &ty);
3742
0
    appearBuilder.appendf("{0:.2f} {1:.2f} m\n", tx, ty);
3743
0
    appearBBox->extendTo(tx, ty);
3744
3745
0
    if (captionwidth != 0 && actualCaptionPos == captionPosInline) { // Break in the middle
3746
0
        matr.transform((main_len - captionwidth) / 2 - captionhmargin, leaderLineLength, &tx, &ty);
3747
0
        appearBuilder.appendf("{0:.2f} {1:.2f} l S\n", tx, ty);
3748
3749
0
        matr.transform((main_len + captionwidth) / 2 + captionhmargin, leaderLineLength, &tx, &ty);
3750
0
        appearBuilder.appendf("{0:.2f} {1:.2f} m\n", tx, ty);
3751
0
    }
3752
3753
0
    matr.transform(main_len - AnnotAppearanceBuilder::lineEndingXShorten(endStyle, lineendingSize), leaderLineLength, &tx, &ty);
3754
0
    appearBuilder.appendf("{0:.2f} {1:.2f} l S\n", tx, ty);
3755
0
    appearBBox->extendTo(tx, ty);
3756
3757
0
    if (startStyle != annotLineEndingNone) {
3758
0
        const double extendX { -AnnotAppearanceBuilder::lineEndingXExtendBBox(startStyle, lineendingSize) };
3759
0
        appearBuilder.drawLineEnding(startStyle, 0, leaderLineLength, -lineendingSize, fill, matr);
3760
0
        matr.transform(extendX, leaderLineLength + lineendingSize / 2., &tx, &ty);
3761
0
        appearBBox->extendTo(tx, ty);
3762
0
        matr.transform(extendX, leaderLineLength - lineendingSize / 2., &tx, &ty);
3763
0
        appearBBox->extendTo(tx, ty);
3764
0
    }
3765
3766
0
    if (endStyle != annotLineEndingNone) {
3767
0
        const double extendX { AnnotAppearanceBuilder::lineEndingXExtendBBox(endStyle, lineendingSize) };
3768
0
        appearBuilder.drawLineEnding(endStyle, main_len, leaderLineLength, lineendingSize, fill, matr);
3769
0
        matr.transform(main_len + extendX, leaderLineLength + lineendingSize / 2., &tx, &ty);
3770
0
        appearBBox->extendTo(tx, ty);
3771
0
        matr.transform(main_len + extendX, leaderLineLength - lineendingSize / 2., &tx, &ty);
3772
0
        appearBBox->extendTo(tx, ty);
3773
0
    }
3774
3775
    // Draw caption text
3776
0
    if (caption) {
3777
0
        double tlx = (main_len - captionwidth) / 2, tly; // Top-left coords
3778
0
        if (actualCaptionPos == captionPosInline) {
3779
0
            tly = leaderLineLength + captionheight / 2;
3780
0
        } else {
3781
0
            tly = leaderLineLength + captionheight + 2 * borderWidth;
3782
0
        }
3783
3784
0
        tlx += captionTextHorizontal;
3785
0
        tly += captionTextVertical;
3786
3787
        // Adjust bounding box
3788
0
        matr.transform(tlx, tly - captionheight, &tx, &ty);
3789
0
        appearBBox->extendTo(tx, ty);
3790
0
        matr.transform(tlx + captionwidth, tly - captionheight, &tx, &ty);
3791
0
        appearBBox->extendTo(tx, ty);
3792
0
        matr.transform(tlx + captionwidth, tly, &tx, &ty);
3793
0
        appearBBox->extendTo(tx, ty);
3794
0
        matr.transform(tlx, tly, &tx, &ty);
3795
0
        appearBBox->extendTo(tx, ty);
3796
3797
        // Setup text state (reusing transformed top-left coord)
3798
0
        appearBuilder.appendf("0 g BT /AnnotDrawFont {0:.2f} Tf\n", fontsize); // Font color: black
3799
0
        appearBuilder.appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} Tm\n", matr.m[0], matr.m[1], matr.m[2], matr.m[3], tx, ty);
3800
0
        appearBuilder.appendf("0 {0:.2f} Td\n", -fontsize * font->getDescent());
3801
        // Draw text
3802
0
        size_t i = 0;
3803
0
        double xposPrev = 0;
3804
0
        while (i < contents.size()) {
3805
0
            GooString out;
3806
0
            double linewidth, xpos;
3807
0
            layoutText(contents.toStr(), &out, &i, *font, &linewidth, 0, nullptr, false);
3808
0
            linewidth *= fontsize;
3809
0
            xpos = (captionwidth - linewidth) / 2;
3810
0
            appearBuilder.appendf("{0:.2f} {1:.2f} Td\n", xpos - xposPrev, -fontsize);
3811
0
            appearBuilder.writeString(out.toStr());
3812
0
            appearBuilder.append("Tj\n");
3813
0
            xposPrev = xpos;
3814
0
        }
3815
0
        appearBuilder.append("ET\n");
3816
0
    }
3817
3818
    // Draw leader lines
3819
0
    double ll_len = fabs(leaderLineLength) + leaderLineExtension;
3820
0
    double sign = leaderLineLength >= 0 ? 1 : -1;
3821
0
    if (ll_len != 0) {
3822
0
        matr.transform(0, 0, &tx, &ty);
3823
0
        appearBuilder.appendf("{0:.2f} {1:.2f} m\n", tx, ty);
3824
0
        appearBBox->extendTo(tx, ty);
3825
0
        matr.transform(0, sign * ll_len, &tx, &ty);
3826
0
        appearBuilder.appendf("{0:.2f} {1:.2f} l S\n", tx, ty);
3827
0
        appearBBox->extendTo(tx, ty);
3828
3829
0
        matr.transform(main_len, 0, &tx, &ty);
3830
0
        appearBuilder.appendf("{0:.2f} {1:.2f} m\n", tx, ty);
3831
0
        appearBBox->extendTo(tx, ty);
3832
0
        matr.transform(main_len, sign * ll_len, &tx, &ty);
3833
0
        appearBuilder.appendf("{0:.2f} {1:.2f} l S\n", tx, ty);
3834
0
        appearBBox->extendTo(tx, ty);
3835
0
    }
3836
3837
0
    appearBuilder.append("Q\n");
3838
3839
0
    const std::array<double, 4> bbox = appearBBox->getBBoxRect();
3840
0
    if (ca == 1) {
3841
0
        appearance = createForm(appearBuilder.buffer(), bbox, false, std::move(fontResDict));
3842
0
    } else {
3843
0
        Object aStream = createForm(appearBuilder.buffer(), bbox, true, std::move(fontResDict));
3844
3845
0
        std::string appearBuf("/GS0 gs\n/Fm0 Do");
3846
0
        std::unique_ptr<Dict> resDict = createResourcesDict("Fm0", std::move(aStream), "GS0", ca, nullptr);
3847
0
        appearance = createForm(appearBuf, bbox, false, std::move(resDict));
3848
0
    }
3849
0
}
3850
3851
void AnnotLine::draw(Gfx *gfx, bool printing)
3852
0
{
3853
0
    if (!isVisible(printing)) {
3854
0
        return;
3855
0
    }
3856
3857
0
    annotLocker();
3858
0
    if (appearance.isNull()) {
3859
0
        generateLineAppearance();
3860
0
    }
3861
3862
    // draw the appearance stream
3863
0
    Object obj = appearance.fetch(gfx->getXRef());
3864
0
    if (appearBBox) {
3865
0
        gfx->drawAnnot(&obj, nullptr, color.get(), appearBBox->getPageXMin(), appearBBox->getPageYMin(), appearBBox->getPageXMax(), appearBBox->getPageYMax(), getRotation());
3866
0
    } else {
3867
0
        gfx->drawAnnot(&obj, nullptr, color.get(), rect->x1, rect->y1, rect->x2, rect->y2, getRotation());
3868
0
    }
3869
0
}
3870
3871
// Before retrieving the res dict, regenerate the appearance stream if needed,
3872
// because AnnotLine::draw may need to store font info in the res dict
3873
Object AnnotLine::getAppearanceResDict()
3874
0
{
3875
0
    if (appearance.isNull()) {
3876
0
        generateLineAppearance();
3877
0
    }
3878
0
    return Annot::getAppearanceResDict();
3879
0
}
3880
3881
//------------------------------------------------------------------------
3882
// AnnotTextMarkup
3883
//------------------------------------------------------------------------
3884
0
AnnotTextMarkup::AnnotTextMarkup(PDFDoc *docA, const PDFRectangle &rectA, AnnotSubtype subType) : AnnotMarkup(docA, rectA)
3885
0
{
3886
0
    switch (subType) {
3887
0
    case typeHighlight:
3888
0
        annotObj.dictSet("Subtype", Object::name("Highlight"));
3889
0
        break;
3890
0
    case typeUnderline:
3891
0
        annotObj.dictSet("Subtype", Object::name("Underline"));
3892
0
        break;
3893
0
    case typeSquiggly:
3894
0
        annotObj.dictSet("Subtype", Object::name("Squiggly"));
3895
0
        break;
3896
0
    case typeStrikeOut:
3897
0
        annotObj.dictSet("Subtype", Object::name("StrikeOut"));
3898
0
        break;
3899
0
    default:
3900
0
        assert(0 && "Invalid subtype for AnnotTextMarkup\n");
3901
0
    }
3902
3903
    // Store dummy quadrilateral with null coordinates
3904
0
    auto quadPoints = std::make_unique<Array>(doc->getXRef());
3905
0
    for (int i = 0; i < 4 * 2; ++i) {
3906
0
        quadPoints->add(Object(0.));
3907
0
    }
3908
0
    annotObj.dictSet("QuadPoints", Object(std::move(quadPoints)));
3909
3910
0
    initialize(annotObj.getDict());
3911
0
}
3912
3913
0
AnnotTextMarkup::AnnotTextMarkup(PDFDoc *docA, Object &&dictObject, const Object &obj) : AnnotMarkup(docA, std::move(dictObject), obj)
3914
0
{
3915
    // the real type will be read in initialize()
3916
0
    type = typeHighlight;
3917
0
    initialize(annotObj.getDict());
3918
0
}
3919
3920
0
AnnotTextMarkup::~AnnotTextMarkup() = default;
3921
3922
void AnnotTextMarkup::initialize(Dict *dict)
3923
0
{
3924
0
    Object obj1;
3925
3926
0
    obj1 = dict->lookup("Subtype");
3927
0
    if (obj1.isName()) {
3928
0
        if (obj1.isName("Highlight")) {
3929
0
            type = typeHighlight;
3930
0
        } else if (obj1.isName("Underline")) {
3931
0
            type = typeUnderline;
3932
0
        } else if (obj1.isName("Squiggly")) {
3933
0
            type = typeSquiggly;
3934
0
        } else if (obj1.isName("StrikeOut")) {
3935
0
            type = typeStrikeOut;
3936
0
        }
3937
0
    }
3938
3939
0
    obj1 = dict->lookup("QuadPoints");
3940
0
    if (obj1.isArray()) {
3941
0
        quadrilaterals = std::make_unique<AnnotQuadrilaterals>(*obj1.getArray());
3942
0
    } else {
3943
0
        error(errSyntaxError, -1, "Bad Annot Text Markup QuadPoints");
3944
0
        ok = false;
3945
0
    }
3946
0
}
3947
3948
void AnnotTextMarkup::setType(AnnotSubtype new_type)
3949
0
{
3950
0
    const char *typeName = nullptr; /* squelch bogus compiler warning */
3951
3952
0
    switch (new_type) {
3953
0
    case typeHighlight:
3954
0
        typeName = "Highlight";
3955
0
        break;
3956
0
    case typeUnderline:
3957
0
        typeName = "Underline";
3958
0
        break;
3959
0
    case typeSquiggly:
3960
0
        typeName = "Squiggly";
3961
0
        break;
3962
0
    case typeStrikeOut:
3963
0
        typeName = "StrikeOut";
3964
0
        break;
3965
0
    default:
3966
0
        assert(!"Invalid subtype");
3967
0
    }
3968
3969
0
    type = new_type;
3970
0
    update("Subtype", Object::name(typeName));
3971
0
    invalidateAppearance();
3972
0
}
3973
3974
void AnnotTextMarkup::setQuadrilaterals(const AnnotQuadrilaterals &quadPoints)
3975
0
{
3976
0
    auto a = std::make_unique<Array>(doc->getXRef());
3977
3978
0
    for (int i = 0; i < quadPoints.getQuadrilateralsLength(); ++i) {
3979
0
        a->add(Object(quadPoints.getX1(i)));
3980
0
        a->add(Object(quadPoints.getY1(i)));
3981
0
        a->add(Object(quadPoints.getX2(i)));
3982
0
        a->add(Object(quadPoints.getY2(i)));
3983
0
        a->add(Object(quadPoints.getX3(i)));
3984
0
        a->add(Object(quadPoints.getY3(i)));
3985
0
        a->add(Object(quadPoints.getX4(i)));
3986
0
        a->add(Object(quadPoints.getY4(i)));
3987
0
    }
3988
3989
0
    quadrilaterals = std::make_unique<AnnotQuadrilaterals>(*a);
3990
3991
0
    annotObj.dictSet("QuadPoints", Object(std::move(a)));
3992
0
    invalidateAppearance();
3993
0
}
3994
3995
bool AnnotTextMarkup::shouldCreateApperance(Gfx *gfx) const
3996
0
{
3997
0
    if (appearance.isNull()) {
3998
0
        return true;
3999
0
    }
4000
4001
    // Adobe Reader seems to have a complex condition for when to use the
4002
    // appearance stream of typeHighlight, which is "use it if it has a Resources dictionary with ExtGState"
4003
    // this is reverse engineering of me editing a file by hand and checking what it does so the real
4004
    // condition may be more or less complex
4005
0
    if (type == typeHighlight) {
4006
0
        XRef *xref = gfx->getXRef();
4007
0
        const Object fetchedApperance = appearance.fetch(xref);
4008
0
        if (fetchedApperance.isStream()) {
4009
0
            const Object resources = fetchedApperance.getStream()->getDict()->lookup("Resources");
4010
0
            if (resources.isDict()) {
4011
0
                if (resources.dictLookup("ExtGState").isDict()) {
4012
0
                    return false;
4013
0
                }
4014
0
            }
4015
0
        }
4016
0
        return true;
4017
0
    }
4018
4019
0
    return false;
4020
0
}
4021
4022
void AnnotTextMarkup::draw(Gfx *gfx, bool printing)
4023
0
{
4024
0
    double ca = 1;
4025
0
    int i;
4026
4027
0
    if (!isVisible(printing)) {
4028
0
        return;
4029
0
    }
4030
4031
0
    annotLocker();
4032
0
    if (shouldCreateApperance(gfx)) {
4033
0
        bool blendMultiply = true;
4034
0
        ca = opacity;
4035
4036
0
        AnnotAppearanceBuilder appearBuilder;
4037
0
        appearBuilder.append("q\n");
4038
4039
        /* Adjust BBox */
4040
0
        appearBBox = std::make_unique<AnnotAppearanceBBox>(*rect);
4041
0
        for (i = 0; i < quadrilaterals->getQuadrilateralsLength(); ++i) {
4042
0
            appearBBox->extendTo(quadrilaterals->getX1(i) - rect->x1, quadrilaterals->getY1(i) - rect->y1);
4043
0
            appearBBox->extendTo(quadrilaterals->getX2(i) - rect->x1, quadrilaterals->getY2(i) - rect->y1);
4044
0
            appearBBox->extendTo(quadrilaterals->getX3(i) - rect->x1, quadrilaterals->getY3(i) - rect->y1);
4045
0
            appearBBox->extendTo(quadrilaterals->getX4(i) - rect->x1, quadrilaterals->getY4(i) - rect->y1);
4046
0
        }
4047
4048
0
        switch (type) {
4049
0
        case typeUnderline:
4050
0
            if (color) {
4051
0
                appearBuilder.setDrawColor(*color, false);
4052
0
            }
4053
0
            appearBuilder.append("[] 0 d 1 w\n");
4054
            // use a borderwidth, which is consistent with the line width
4055
0
            appearBBox->setBorderWidth(1.0);
4056
4057
0
            for (i = 0; i < quadrilaterals->getQuadrilateralsLength(); ++i) {
4058
0
                double x3, y3, x4, y4;
4059
4060
0
                x3 = quadrilaterals->getX3(i);
4061
0
                y3 = quadrilaterals->getY3(i);
4062
0
                x4 = quadrilaterals->getX4(i);
4063
0
                y4 = quadrilaterals->getY4(i);
4064
4065
0
                appearBuilder.appendf("{0:.2f} {1:.2f} m\n", x3, y3);
4066
0
                appearBuilder.appendf("{0:.2f} {1:.2f} l\n", x4, y4);
4067
0
                appearBuilder.append("S\n");
4068
0
            }
4069
0
            break;
4070
0
        case typeStrikeOut:
4071
0
            if (color) {
4072
0
                appearBuilder.setDrawColor(*color, false);
4073
0
            }
4074
0
            blendMultiply = false;
4075
0
            appearBuilder.append("[] 0 d 1 w\n");
4076
4077
0
            for (i = 0; i < quadrilaterals->getQuadrilateralsLength(); ++i) {
4078
0
                double x1, y1, x2, y2;
4079
0
                double x3, y3, x4, y4;
4080
4081
0
                x1 = quadrilaterals->getX1(i);
4082
0
                y1 = quadrilaterals->getY1(i);
4083
0
                x2 = quadrilaterals->getX2(i);
4084
0
                y2 = quadrilaterals->getY2(i);
4085
4086
0
                x3 = quadrilaterals->getX3(i);
4087
0
                y3 = quadrilaterals->getY3(i);
4088
0
                x4 = quadrilaterals->getX4(i);
4089
0
                y4 = quadrilaterals->getY4(i);
4090
4091
0
                appearBuilder.appendf("{0:.2f} {1:.2f} m\n", (x1 + x3) / 2., (y1 + y3) / 2.);
4092
0
                appearBuilder.appendf("{0:.2f} {1:.2f} l\n", (x2 + x4) / 2., (y2 + y4) / 2.);
4093
0
                appearBuilder.append("S\n");
4094
0
            }
4095
0
            break;
4096
0
        case typeSquiggly:
4097
0
            if (color) {
4098
0
                appearBuilder.setDrawColor(*color, false);
4099
0
            }
4100
0
            appearBuilder.append("[] 0 d 1 w\n");
4101
4102
0
            for (i = 0; i < quadrilaterals->getQuadrilateralsLength(); ++i) {
4103
0
                double x1, y1, x2, y3;
4104
0
                double h6;
4105
4106
0
                x1 = quadrilaterals->getX1(i);
4107
0
                y1 = quadrilaterals->getY1(i);
4108
0
                x2 = quadrilaterals->getX2(i);
4109
0
                y3 = quadrilaterals->getY3(i);
4110
0
                h6 = (y1 - y3) / 6.0;
4111
4112
0
                appearBuilder.appendf("{0:.2f} {1:.2f} m\n", x1, y3 + h6);
4113
0
                bool down = false;
4114
0
                do {
4115
0
                    down = !down; // Zigzag line
4116
0
                    x1 += 2;
4117
0
                    appearBuilder.appendf("{0:.2f} {1:.2f} l\n", x1, y3 + (down ? 0 : h6));
4118
0
                } while (x1 < x2);
4119
0
                appearBuilder.append("S\n");
4120
0
            }
4121
0
            break;
4122
0
        default:
4123
0
        case typeHighlight:
4124
0
            if (color) {
4125
0
                appearBuilder.setDrawColor(*color, true);
4126
0
            }
4127
4128
0
            double biggestBorder = 0;
4129
0
            for (i = 0; i < quadrilaterals->getQuadrilateralsLength(); ++i) {
4130
0
                double x1, y1, x2, y2, x3, y3, x4, y4;
4131
0
                double h4;
4132
4133
0
                x1 = quadrilaterals->getX1(i);
4134
0
                y1 = quadrilaterals->getY1(i);
4135
0
                x2 = quadrilaterals->getX2(i);
4136
0
                y2 = quadrilaterals->getY2(i);
4137
0
                x3 = quadrilaterals->getX3(i);
4138
0
                y3 = quadrilaterals->getY3(i);
4139
0
                x4 = quadrilaterals->getX4(i);
4140
0
                y4 = quadrilaterals->getY4(i);
4141
0
                h4 = fabs(y1 - y3) / 4.0;
4142
4143
0
                if (h4 > biggestBorder) {
4144
0
                    biggestBorder = h4;
4145
0
                }
4146
4147
0
                appearBuilder.appendf("{0:.2f} {1:.2f} m\n", x3, y3);
4148
0
                appearBuilder.appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n", x3 - h4, y3 + h4, x1 - h4, y1 - h4, x1, y1);
4149
0
                appearBuilder.appendf("{0:.2f} {1:.2f} l\n", x2, y2);
4150
0
                appearBuilder.appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n", x2 + h4, y2 - h4, x4 + h4, y4 + h4, x4, y4);
4151
0
                appearBuilder.append("f\n");
4152
0
            }
4153
0
            appearBBox->setBorderWidth(biggestBorder);
4154
0
            break;
4155
0
        }
4156
0
        appearBuilder.append("Q\n");
4157
4158
0
        const std::array<double, 4> bbox = { appearBBox->getPageXMin(), appearBBox->getPageYMin(), appearBBox->getPageXMax(), appearBBox->getPageYMax() };
4159
0
        Object aStream = createForm(appearBuilder.buffer(), bbox, true, Object {});
4160
4161
0
        std::string appearBuf("/GS0 gs\n/Fm0 Do");
4162
0
        std::unique_ptr<Dict> resDict = createResourcesDict("Fm0", std::move(aStream), "GS0", 1, blendMultiply ? "Multiply" : nullptr);
4163
0
        if (ca == 1) {
4164
0
            appearance = createForm(appearBuf, bbox, false, std::move(resDict));
4165
0
        } else {
4166
0
            aStream = createForm(appearBuf, bbox, true, std::move(resDict));
4167
4168
0
            std::unique_ptr<Dict> resDict2 = createResourcesDict("Fm0", std::move(aStream), "GS0", ca, nullptr);
4169
0
            appearance = createForm(appearBuf, bbox, false, std::move(resDict2));
4170
0
        }
4171
0
    }
4172
4173
    // draw the appearance stream
4174
0
    Object obj = appearance.fetch(gfx->getXRef());
4175
0
    if (appearBBox) {
4176
0
        gfx->drawAnnot(&obj, nullptr, color.get(), appearBBox->getPageXMin(), appearBBox->getPageYMin(), appearBBox->getPageXMax(), appearBBox->getPageYMax(), getRotation());
4177
0
    } else {
4178
0
        gfx->drawAnnot(&obj, nullptr, color.get(), rect->x1, rect->y1, rect->x2, rect->y2, getRotation());
4179
0
    }
4180
0
}
4181
4182
//------------------------------------------------------------------------
4183
// AnnotWidget
4184
//------------------------------------------------------------------------
4185
4186
0
AnnotWidget::AnnotWidget(PDFDoc *docA, Object &&dictObject, const Object &obj) : Annot(docA, std::move(dictObject), obj)
4187
0
{
4188
0
    type = typeWidget;
4189
0
    field = nullptr;
4190
0
    initialize(annotObj.getDict());
4191
0
}
4192
4193
0
AnnotWidget::AnnotWidget(PDFDoc *docA, Object *dictObject, Object *obj, FormField *fieldA) : Annot(docA, dictObject->copy(), *obj)
4194
0
{
4195
0
    type = typeWidget;
4196
0
    field = fieldA;
4197
0
    initialize(dictObject->getDict());
4198
0
}
4199
4200
0
AnnotWidget::~AnnotWidget() = default;
4201
4202
void AnnotWidget::initialize(Dict *dict)
4203
0
{
4204
0
    Object obj1;
4205
4206
0
    form = doc->getCatalog()->getForm();
4207
4208
0
    obj1 = dict->lookup("H");
4209
0
    if (obj1.isName()) {
4210
0
        const std::string &modeName = obj1.getNameString();
4211
4212
0
        if (modeName == "N") {
4213
0
            mode = highlightModeNone;
4214
0
        } else if (modeName == "O") {
4215
0
            mode = highlightModeOutline;
4216
0
        } else if ((modeName == "P") || (modeName == "T")) {
4217
0
            mode = highlightModePush;
4218
0
        } else {
4219
0
            mode = highlightModeInvert;
4220
0
        }
4221
0
    } else {
4222
0
        mode = highlightModeInvert;
4223
0
    }
4224
4225
0
    obj1 = dict->lookup("MK");
4226
0
    if (obj1.isDict()) {
4227
0
        appearCharacs = std::make_unique<AnnotAppearanceCharacs>(obj1.getDict());
4228
0
    }
4229
4230
0
    obj1 = dict->lookup("A");
4231
0
    if (obj1.isDict()) {
4232
0
        action = LinkAction::parseAction(&obj1, doc->getCatalog()->getBaseURI());
4233
0
    }
4234
4235
0
    additionalActions = dict->lookupNF("AA").copy();
4236
4237
0
    obj1 = dict->lookup("Parent");
4238
0
    if (obj1.isDict()) {
4239
0
        parent = nullptr;
4240
0
    } else {
4241
0
        parent = nullptr;
4242
0
    }
4243
4244
0
    obj1 = dict->lookup("BS");
4245
0
    if (obj1.isDict()) {
4246
0
        border = std::make_unique<AnnotBorderBS>(obj1.getDict());
4247
0
    }
4248
4249
0
    updatedAppearanceStream = Ref::INVALID();
4250
0
}
4251
4252
std::unique_ptr<LinkAction> AnnotWidget::getAdditionalAction(AdditionalActionsType additionalActionType)
4253
0
{
4254
0
    return ::getAdditionalAction(additionalActionType, &additionalActions, doc);
4255
0
}
4256
4257
std::unique_ptr<LinkAction> AnnotWidget::getFormAdditionalAction(FormAdditionalActionsType formAdditionalActionType)
4258
0
{
4259
0
    Object additionalActionsObject = additionalActions.fetch(doc->getXRef());
4260
4261
0
    if (additionalActionsObject.isDict()) {
4262
0
        const char *key = getFormAdditionalActionKey(formAdditionalActionType);
4263
4264
0
        Object actionObject = additionalActionsObject.dictLookup(key);
4265
0
        if (actionObject.isDict()) {
4266
0
            return LinkAction::parseAction(&actionObject, doc->getCatalog()->getBaseURI());
4267
0
        }
4268
0
    }
4269
4270
0
    return nullptr;
4271
0
}
4272
4273
bool AnnotWidget::setFormAdditionalAction(FormAdditionalActionsType formAdditionalActionType, const std::string &js)
4274
0
{
4275
0
    Object additionalActionsObject = additionalActions.fetch(doc->getXRef());
4276
4277
0
    if (!additionalActionsObject.isDict()) {
4278
0
        additionalActionsObject = Object(std::make_unique<Dict>(doc->getXRef()));
4279
0
        annotObj.dictSet("AA", additionalActionsObject.copy());
4280
0
    }
4281
4282
0
    additionalActionsObject.dictSet(getFormAdditionalActionKey(formAdditionalActionType), LinkJavaScript::createObject(doc->getXRef(), js));
4283
4284
0
    if (additionalActions.isRef()) {
4285
0
        doc->getXRef()->setModifiedObject(&additionalActionsObject, additionalActions.getRef());
4286
0
    } else if (hasRef) {
4287
0
        doc->getXRef()->setModifiedObject(&annotObj, ref);
4288
0
    } else {
4289
0
        error(errInternal, -1, "AnnotWidget::setFormAdditionalAction, where neither additionalActions is ref nor annotobj itself is ref");
4290
0
        return false;
4291
0
    }
4292
0
    return true;
4293
0
}
4294
4295
// Grand unified handler for preparing text strings to be drawn into form
4296
// fields.  Takes as input a text string (in PDFDocEncoding or UTF-16).
4297
// Converts some or all of this string to the appropriate encoding for the
4298
// specified font, and computes the width of the text.  Can optionally stop
4299
// converting when a specified width has been reached, to perform line-breaking
4300
// for multi-line fields.
4301
//
4302
// Parameters:
4303
//   text: input text string to convert
4304
//   outBuf: buffer for writing re-encoded string
4305
//   i: index at which to start converting; will be updated to point just after
4306
//      last character processed
4307
//   font: the font which will be used for display
4308
//   width: computed width (unscaled by font size) will be stored here
4309
//   widthLimit: if non-zero, stop converting to keep width under this value
4310
//      (should be scaled down by font size)
4311
//   charCount: count of number of characters will be stored here
4312
//   noReencode: if set, do not try to translate the character encoding
4313
//      (useful for Zapf Dingbats or other unusual encodings)
4314
//      can only be used with simple fonts, not CID-keyed fonts
4315
//
4316
// TODO: Handle surrogate pairs in UTF-16.
4317
//       Should be able to generate output for any CID-keyed font.
4318
//       Doesn't handle vertical fonts--should it?
4319
void Annot::layoutText(const std::string &text, GooString *outBuf, size_t *i, const GfxFont &font, double *width, double widthLimit, int *charCount, bool noReencode, bool *newFontNeeded)
4320
0
{
4321
0
    CharCode c;
4322
0
    Unicode uChar;
4323
0
    const Unicode *uAux;
4324
0
    double w = 0.0;
4325
0
    int uLen, n;
4326
0
    double dx, dy, ox, oy;
4327
4328
0
    if (newFontNeeded) {
4329
0
        *newFontNeeded = false;
4330
0
    }
4331
0
    if (width != nullptr) {
4332
0
        *width = 0.0;
4333
0
    }
4334
0
    if (charCount != nullptr) {
4335
0
        *charCount = 0;
4336
0
    }
4337
4338
0
    bool unicode = hasUnicodeByteOrderMark(text);
4339
0
    bool spacePrev; // previous character was a space
4340
4341
    // State for backtracking when more text has been processed than fits within
4342
    // widthLimit.  We track for both input (text) and output (outBuf) the offset
4343
    // to the first character to discard.
4344
    //
4345
    // We keep track of several points:
4346
    //   1 - end of previous completed word which fits
4347
    //   2 - previous character which fit
4348
0
    size_t last_i1, last_i2, last_o1, last_o2;
4349
4350
0
    if (unicode && text.size() % 2 != 0) {
4351
0
        error(errSyntaxError, -1, "AnnotWidget::layoutText, bad unicode string");
4352
0
        *i += 1;
4353
0
        return;
4354
0
    }
4355
4356
    // skip Unicode marker on string if needed
4357
0
    if (unicode && *i == 0) {
4358
0
        *i = 2;
4359
0
    }
4360
4361
    // Start decoding and copying characters, until either:
4362
    //   we reach the end of the string
4363
    //   we reach the maximum width
4364
    //   we reach a newline character
4365
    // As we copy characters, keep track of the last full word to fit, so that we
4366
    // can backtrack if we exceed the maximum width.
4367
0
    last_i1 = last_i2 = *i;
4368
0
    last_o1 = last_o2 = 0;
4369
0
    spacePrev = false;
4370
0
    outBuf->clear();
4371
4372
0
    while (*i < text.size()) {
4373
0
        last_i2 = *i;
4374
0
        last_o2 = outBuf->size();
4375
4376
0
        if (unicode) {
4377
0
            uChar = static_cast<unsigned char>(text[*i]) << 8;
4378
0
            uChar += static_cast<unsigned char>(text[*i + 1]);
4379
0
            *i += 2;
4380
0
        } else {
4381
0
            if (noReencode) {
4382
0
                uChar = text[*i] & 0xff;
4383
0
            } else {
4384
0
                uChar = pdfDocEncoding[text[*i] & 0xff];
4385
0
            }
4386
0
            *i += 1;
4387
0
        }
4388
4389
        // Explicit line break?
4390
0
        if (uChar == '\r' || uChar == '\n') {
4391
            // Treat a <CR><LF> sequence as a single line break
4392
0
            if (uChar == '\r' && *i < text.size()) {
4393
0
                if (unicode && text[*i] == '\0' && text[*i + 1] == '\n') {
4394
0
                    *i += 2;
4395
0
                } else if (!unicode && text[*i] == '\n') {
4396
0
                    *i += 1;
4397
0
                }
4398
0
            }
4399
4400
0
            break;
4401
0
        }
4402
4403
0
        if (noReencode) {
4404
0
            outBuf->push_back(uChar);
4405
0
        } else {
4406
0
            const CharCodeToUnicode *ccToUnicode = font.getToUnicode();
4407
0
            if (!ccToUnicode) {
4408
                // This assumes an identity CMap.
4409
0
                outBuf->push_back((uChar >> 8) & 0xff);
4410
0
                outBuf->push_back(uChar & 0xff);
4411
0
            } else if (ccToUnicode->mapToCharCode(&uChar, &c, 1)) {
4412
0
                if (font.isCIDFont()) {
4413
0
                    const auto *cidFont = static_cast<const GfxCIDFont *>(&font);
4414
0
                    if (c < cidFont->getCIDToGIDLen()) {
4415
0
                        const int glyph = cidFont->getCIDToGID()[c];
4416
0
                        if (glyph > 0 || c == 0) {
4417
0
                            outBuf->push_back((c >> 8) & 0xff);
4418
0
                            outBuf->push_back(c & 0xff);
4419
0
                        } else {
4420
0
                            if (newFontNeeded) {
4421
0
                                *newFontNeeded = true;
4422
0
                                *i -= unicode ? 2 : 1;
4423
0
                                break;
4424
0
                            }
4425
0
                            outBuf->push_back((c >> 8) & 0xff);
4426
0
                            outBuf->push_back(c & 0xff);
4427
0
                            error(errSyntaxError, -1, "AnnotWidget::layoutText, font doesn't have glyph for charcode U+{0:04uX}", c);
4428
0
                        }
4429
0
                    } else {
4430
                        // TODO: This assumes an identity CMap.  It should be extended to
4431
                        // handle the general case.
4432
0
                        outBuf->push_back((c >> 8) & 0xff);
4433
0
                        outBuf->push_back(c & 0xff);
4434
0
                    }
4435
0
                } else {
4436
                    // 8-bit font
4437
0
                    outBuf->push_back(c);
4438
0
                }
4439
0
            } else {
4440
0
                if (newFontNeeded) {
4441
0
                    *newFontNeeded = true;
4442
0
                    *i -= unicode ? 2 : 1;
4443
0
                    break;
4444
0
                }
4445
0
                error(errSyntaxError, -1, "AnnotWidget::layoutText, cannot convert U+{0:04uX}", uChar);
4446
0
            }
4447
0
        }
4448
4449
        // If we see a space, then we have a linebreak opportunity.
4450
0
        if (uChar == ' ') {
4451
0
            last_i1 = *i;
4452
0
            if (!spacePrev) {
4453
0
                last_o1 = last_o2;
4454
0
            }
4455
0
            spacePrev = true;
4456
0
        } else {
4457
0
            spacePrev = false;
4458
0
        }
4459
4460
        // Compute width of character just output
4461
0
        if (outBuf->size() > last_o2) {
4462
0
            dx = 0.0;
4463
0
            font.getNextChar(outBuf->c_str() + last_o2, outBuf->size() - last_o2, &c, &uAux, &uLen, &dx, &dy, &ox, &oy);
4464
0
            w += dx;
4465
0
        }
4466
4467
        // Current line over-full now?
4468
0
        if (widthLimit > 0.0 && w > widthLimit) {
4469
0
            if (last_o1 > 0) {
4470
                // Back up to the previous word which fit, if there was a previous
4471
                // word.
4472
0
                *i = last_i1;
4473
0
                outBuf->erase(last_o1, outBuf->size() - last_o1);
4474
0
            } else if (last_o2 > 0) {
4475
                // Otherwise, back up to the previous character (in the only word on
4476
                // this line)
4477
0
                *i = last_i2;
4478
0
                outBuf->erase(last_o2, outBuf->size() - last_o2);
4479
0
            } else {
4480
                // Otherwise, we were trying to fit the first character; include it
4481
                // anyway even if it overflows the space--no updates to make.
4482
0
            }
4483
0
            break;
4484
0
        }
4485
0
    }
4486
4487
    // If splitting input into lines because we reached the width limit, then
4488
    // consume any remaining trailing spaces that would go on this line from the
4489
    // input.  If in doing so we reach a newline, consume that also.  This code
4490
    // won't run if we stopped at a newline above, since in that case w <=
4491
    // widthLimit still.
4492
0
    if (widthLimit > 0.0 && w > widthLimit) {
4493
0
        if (unicode) {
4494
0
            while (*i < text.size() && text[*i] == '\0' && text[*i + 1] == ' ') {
4495
0
                *i += 2;
4496
0
            }
4497
0
            if (*i < text.size() && text[*i] == '\0' && text[*i + 1] == '\r') {
4498
0
                *i += 2;
4499
0
            }
4500
0
            if (*i < text.size() && text[*i] == '\0' && text[*i + 1] == '\n') {
4501
0
                *i += 2;
4502
0
            }
4503
0
        } else {
4504
0
            while (*i < text.size() && text[*i] == ' ') {
4505
0
                *i += 1;
4506
0
            }
4507
0
            if (*i < text.size() && text[*i] == '\r') {
4508
0
                *i += 1;
4509
0
            }
4510
0
            if (*i < text.size() && text[*i] == '\n') {
4511
0
                *i += 1;
4512
0
            }
4513
0
        }
4514
0
    }
4515
4516
    // Compute the actual width and character count of the final string, based on
4517
    // breakpoint, if this information is requested by the caller.
4518
0
    if (width != nullptr || charCount != nullptr) {
4519
0
        const char *s = outBuf->c_str();
4520
0
        int len = outBuf->size();
4521
4522
0
        while (len > 0) {
4523
0
            dx = 0.0;
4524
0
            n = font.getNextChar(s, len, &c, &uAux, &uLen, &dx, &dy, &ox, &oy);
4525
4526
0
            if (n == 0) {
4527
0
                break;
4528
0
            }
4529
4530
0
            if (width != nullptr) {
4531
0
                *width += dx;
4532
0
            }
4533
0
            if (charCount != nullptr) {
4534
0
                *charCount += 1;
4535
0
            }
4536
4537
0
            s += n;
4538
0
            len -= n;
4539
0
        }
4540
0
    }
4541
0
}
4542
4543
// Copy the given string to appearBuf, adding parentheses around it and
4544
// escaping characters as appropriate.
4545
void AnnotAppearanceBuilder::writeString(const std::string &str)
4546
0
{
4547
0
    appearBuf.push_back('(');
4548
4549
0
    for (const char c : str) {
4550
0
        if (c == '(' || c == ')' || c == '\\') {
4551
0
            appearBuf.push_back('\\');
4552
0
            appearBuf.push_back(c);
4553
0
        } else if (c < 0x20) {
4554
0
            GooString::appendf(appearBuf, "\\{0:03o}", static_cast<unsigned char>(c));
4555
0
        } else {
4556
0
            appearBuf.push_back(c);
4557
0
        }
4558
0
    }
4559
4560
0
    appearBuf.push_back(')');
4561
0
}
4562
4563
// Draw the variable text or caption for a field.
4564
bool AnnotAppearanceBuilder::drawText(const std::string &inputText, const Form *form, const std::string &inputDA, const GfxResources *resources, const AnnotBorder *border, const AnnotAppearanceCharacs *appearCharacs,
4565
                                      const PDFRectangle &rect, const VariableTextQuadding quadding, XRef &xref, Dict &resourcesDict, const int flags, const int nCombs)
4566
0
{
4567
0
    const bool forceZapfDingbats = flags & ForceZapfDingbatsDrawTextFlag;
4568
4569
0
    std::vector<std::string> daToks;
4570
0
    const GfxFont *font;
4571
0
    double fontSize;
4572
0
    int tfPos, tmPos;
4573
0
    std::unique_ptr<std::string> textToFree;
4574
0
    std::unique_ptr<const GfxFont> fontToFree = nullptr;
4575
4576
    //~ if there is no MK entry, this should use the existing content stream,
4577
    //~ and only replace the marked content portion of it
4578
    //~ (this is only relevant for Tx fields)
4579
4580
    // Checkbox fields may come without a DA entry, spec requires it
4581
    // for all fields containing variable text but it seems Checkbox
4582
    // fields are de-facto not considered as such - Issue #1055
4583
0
    static const std::string daStackString("/ZaDb 0 Tf 0 g");
4584
0
    const std::string &daToUse = inputDA.empty() && forceZapfDingbats ? daStackString : inputDA;
4585
4586
    // parse the default appearance string
4587
0
    tfPos = tmPos = -1;
4588
0
    FormFieldText::tokenizeDA(daToUse, &daToks, nullptr /*searchTok*/);
4589
0
    for (size_t i = 2; i < daToks.size(); ++i) {
4590
0
        if (i >= 2 && daToks[i] == "Tf") {
4591
0
            tfPos = i - 2;
4592
0
        } else if (i >= 6 && daToks[i] == "Tm") {
4593
0
            tmPos = i - 6;
4594
0
        }
4595
0
    }
4596
4597
    // get the font and font size
4598
0
    font = nullptr;
4599
0
    fontSize = 0;
4600
0
    if (tfPos >= 0) {
4601
0
        std::string &tok = daToks[tfPos];
4602
0
        if (forceZapfDingbats) {
4603
0
            if (tok != "/ZaDb") {
4604
0
                tok = "/ZaDb";
4605
0
            }
4606
0
        }
4607
0
        if (!tok.empty() && tok[0] == '/') {
4608
0
            const auto fontName = std::string_view(tok).substr(1);
4609
0
            if (!resources || !(font = resources->lookupFont(fontName).get())) {
4610
0
                const char *fallback = determineFallbackFont(tok, forceZapfDingbats ? "ZapfDingbats" : "Helvetica");
4611
                // The font variable sometimes points to an object that needs to be deleted
4612
                // and sometimes not, depending on whether the call to lookupFont above fails.
4613
                // When the code path right here is taken, the destructor of fontToFree
4614
                // (which is a std::unique_ptr) will delete the font object at the end of this method.
4615
0
                fontToFree = createAnnotDrawFont(&xref, &resourcesDict, fontName, fallback);
4616
0
                font = fontToFree.get();
4617
0
                if (font && forceZapfDingbats) {
4618
0
                    addedDingbatsResource = true;
4619
0
                }
4620
0
            }
4621
0
        } else {
4622
0
            error(errSyntaxError, -1, "Invalid font name in 'Tf' operator in field's DA string");
4623
0
        }
4624
0
        fontSize = gatof(daToks[tfPos + 1].c_str());
4625
0
    } else {
4626
0
        error(errSyntaxError, -1, "Missing 'Tf' operator in field's DA string");
4627
0
    }
4628
0
    if (!font) {
4629
0
        return false;
4630
0
    }
4631
4632
0
    if (tmPos < 0) {
4633
        // Add fake Tm to the DA tokens
4634
0
        tmPos = daToks.size();
4635
0
        daToks.insert(daToks.end(), { "1", "0", "0", "1", "0", "0", "Tm" });
4636
0
    }
4637
4638
    // get the border width
4639
0
    const double borderWidth = border ? border->getWidth() : 0;
4640
4641
    // for a password field, replace all characters with asterisks
4642
0
    if (flags & TurnTextToStarsDrawTextFlag) {
4643
0
        int len;
4644
0
        if (hasUnicodeByteOrderMark(inputText)) {
4645
0
            len = (inputText.size() - 2) / 2;
4646
0
        } else {
4647
0
            len = inputText.size();
4648
0
        }
4649
4650
0
        textToFree = std::make_unique<std::string>();
4651
0
        for (int i = 0; i < len; ++i) {
4652
0
            textToFree->push_back('*');
4653
0
        }
4654
0
    }
4655
0
    const std::string &textToDraw = textToFree ? *textToFree : inputText;
4656
4657
    // setup
4658
0
    if (flags & EmitMarkedContentDrawTextFlag) {
4659
0
        appearBuf.append("/Tx BMC\n");
4660
0
    }
4661
0
    appearBuf.append("q\n");
4662
0
    auto calculateDxDy = [this, appearCharacs, &rect]() -> std::tuple<double, double> {
4663
0
        const int rot = appearCharacs ? appearCharacs->getRotation() : 0;
4664
0
        switch (rot) {
4665
0
        case 90:
4666
0
            GooString::appendf(appearBuf, "0 1 -1 0 {0:.2f} 0 cm\n", rect.x2 - rect.x1);
4667
0
            return { rect.y2 - rect.y1, rect.x2 - rect.x1 };
4668
4669
0
        case 180:
4670
0
            GooString::appendf(appearBuf, "-1 0 0 -1 {0:.2f} {1:.2f} cm\n", rect.x2 - rect.x1, rect.y2 - rect.y1);
4671
0
            return { rect.x2 - rect.y2, rect.y2 - rect.y1 };
4672
4673
0
        case 270:
4674
0
            GooString::appendf(appearBuf, "0 -1 1 0 0 {0:.2f} cm\n", rect.y2 - rect.y1);
4675
0
            return { rect.y2 - rect.y1, rect.x2 - rect.x1 };
4676
4677
0
        default: // assume rot == 0
4678
0
            return { rect.x2 - rect.x1, rect.y2 - rect.y1 };
4679
0
        }
4680
0
    };
4681
0
    const auto dxdy = calculateDxDy();
4682
0
    const double dx = std::get<0>(dxdy);
4683
0
    const double dy = std::get<1>(dxdy);
4684
0
    appearBuf.append("BT\n");
4685
    // multi-line text
4686
0
    if (flags & MultilineDrawTextFlag) {
4687
        // note: comb is ignored in multiline mode as mentioned in the spec
4688
4689
0
        const double wMax = dx - 2 * borderWidth - 4;
4690
4691
        // compute font autosize
4692
0
        if (fontSize == 0) {
4693
0
            fontSize = Annot::calculateFontSize(form, *font, textToDraw, wMax, dy, forceZapfDingbats);
4694
0
            daToks[tfPos + 1] = GooString::format("{0:.2f}", fontSize);
4695
0
        }
4696
4697
        // starting y coordinate
4698
        // (note: each line of text starts with a Td operator that moves
4699
        // down a line)
4700
0
        const double y = dy - 3;
4701
4702
        // set the font matrix
4703
0
        daToks[tmPos + 4] = "0";
4704
0
        daToks[tmPos + 5] = GooString::format("{0:.2f}", y);
4705
4706
        // write the DA string
4707
0
        for (const std::string &daTok : daToks) {
4708
0
            appearBuf.append(daTok);
4709
0
            appearBuf.push_back(' ');
4710
0
        }
4711
4712
0
        const DrawMultiLineTextResult textCommands = drawMultiLineText(textToDraw, dx, form, *font, std::string(), fontSize, quadding, borderWidth + 2);
4713
0
        appearBuf.append(textCommands.text);
4714
4715
        // single-line text
4716
0
    } else {
4717
        //~ replace newlines with spaces? - what does Acrobat do?
4718
4719
        // comb formatting
4720
0
        if (nCombs > 0) {
4721
            // compute comb spacing
4722
0
            const double w = (dx - 2 * borderWidth) / nCombs;
4723
4724
            // compute font autosize
4725
0
            if (fontSize == 0) {
4726
0
                fontSize = dy - 2 * borderWidth;
4727
0
                if (w < fontSize) {
4728
0
                    fontSize = w;
4729
0
                }
4730
0
                fontSize = floor(fontSize);
4731
0
                daToks[tfPos + 1] = GooString::format("{0:.2f}", fontSize);
4732
0
            }
4733
4734
0
            const HorizontalTextLayouter textLayouter(textToDraw, form, *font, {}, forceZapfDingbats);
4735
4736
0
            const int charCount = std::min(textLayouter.totalCharCount(), nCombs);
4737
4738
            // compute starting text cell
4739
0
            auto calculateX = [quadding, borderWidth, nCombs, charCount, w] {
4740
0
                switch (quadding) {
4741
0
                case VariableTextQuadding::leftJustified:
4742
0
                default:
4743
0
                    return borderWidth;
4744
0
                case VariableTextQuadding::centered:
4745
0
                    return borderWidth + (nCombs - charCount) / 2.0 * w;
4746
0
                case VariableTextQuadding::rightJustified:
4747
0
                    return borderWidth + (nCombs - charCount) * w;
4748
0
                }
4749
0
            };
4750
0
            const double x = calculateX();
4751
0
            const double y = 0.5 * dy - 0.4 * fontSize;
4752
4753
            // set the font matrix
4754
0
            daToks[tmPos + 4] = GooString::format("{0:.2f}", x);
4755
0
            daToks[tmPos + 5] = GooString::format("{0:.2f}", y);
4756
4757
            // write the DA string
4758
0
            for (const std::string &daTok : daToks) {
4759
0
                appearBuf.append(daTok);
4760
0
                appearBuf.push_back(' ');
4761
0
            }
4762
4763
            // write the text string
4764
0
            int i = 0;
4765
0
            double xPrev = w; // so that first character is placed properly
4766
0
            for (const HorizontalTextLayouter::Data &d : textLayouter.data) {
4767
0
                const char *s = d.text.c_str();
4768
0
                int len = d.text.size();
4769
0
                while (i < nCombs && len > 0) {
4770
0
                    CharCode code;
4771
0
                    const Unicode *uAux;
4772
0
                    int uLen, n;
4773
0
                    double char_dx, char_dy, ox, oy;
4774
4775
0
                    const GfxFont *currentFont = font;
4776
0
                    if (!d.fontName.empty()) {
4777
0
                        appearBuf.append(" q\n");
4778
0
                        GooString::appendf(appearBuf, "/{0:s} {1:.2f} Tf\n", d.fontName.c_str(), fontSize);
4779
0
                        currentFont = form->getDefaultResources()->lookupFont(d.fontName).get();
4780
0
                    }
4781
4782
0
                    char_dx = 0.0;
4783
0
                    n = currentFont->getNextChar(s, len, &code, &uAux, &uLen, &char_dx, &char_dy, &ox, &oy);
4784
0
                    char_dx *= fontSize;
4785
4786
                    // center each character within its cell, by advancing the text
4787
                    // position the appropriate amount relative to the start of the
4788
                    // previous character
4789
0
                    const double combX = 0.5 * (w - char_dx);
4790
0
                    GooString::appendf(appearBuf, "{0:.2f} 0 Td\n", combX - xPrev + w);
4791
4792
0
                    GooString charBuf(s, n);
4793
0
                    writeString(charBuf.toStr());
4794
0
                    appearBuf.append(" Tj\n");
4795
4796
0
                    if (!d.fontName.empty()) {
4797
0
                        appearBuf.append(" Q\n");
4798
0
                    }
4799
4800
0
                    i++;
4801
0
                    s += n;
4802
0
                    len -= n;
4803
0
                    xPrev = combX;
4804
0
                }
4805
0
            }
4806
4807
            // regular (non-comb) formatting
4808
0
        } else {
4809
0
            const HorizontalTextLayouter textLayouter(textToDraw, form, *font, {}, forceZapfDingbats);
4810
4811
0
            const double usedWidthUnscaled = textLayouter.totalWidth();
4812
4813
            // compute font autosize
4814
0
            if (fontSize == 0) {
4815
0
                fontSize = dy - 2 * borderWidth;
4816
0
                if (usedWidthUnscaled > 0) {
4817
0
                    const double fontSize2 = (dx - 4 - 2 * borderWidth) / usedWidthUnscaled;
4818
0
                    if (fontSize2 < fontSize) {
4819
0
                        fontSize = fontSize2;
4820
0
                    }
4821
0
                }
4822
0
                fontSize = floor(fontSize);
4823
0
                daToks[tfPos + 1] = GooString::format("{0:.2f}", fontSize);
4824
0
            }
4825
4826
            // compute text start position
4827
0
            const double usedWidth = usedWidthUnscaled * fontSize;
4828
0
            auto calculateX = [quadding, borderWidth, dx, usedWidth] {
4829
0
                switch (quadding) {
4830
0
                case VariableTextQuadding::leftJustified:
4831
0
                default:
4832
0
                    return borderWidth + 2;
4833
0
                case VariableTextQuadding::centered:
4834
0
                    return (dx - usedWidth) / 2;
4835
0
                case VariableTextQuadding::rightJustified:
4836
0
                    return dx - borderWidth - 2 - usedWidth;
4837
0
                }
4838
0
            };
4839
0
            const double x = calculateX();
4840
0
            const double y = 0.5 * dy - 0.4 * fontSize;
4841
4842
            // set the font matrix
4843
0
            daToks[tmPos + 4] = GooString::format("{0:.2f}", x);
4844
0
            daToks[tmPos + 5] = GooString::format("{0:.2f}", y);
4845
4846
            // write the DA string
4847
0
            for (const std::string &daTok : daToks) {
4848
0
                appearBuf.append(daTok);
4849
0
                appearBuf.push_back(' ');
4850
0
            }
4851
            // This newline is not neeed at all but it makes for easier reading
4852
            // and our auto tests "wrongly" assume it will be there, so add it anyway
4853
0
            appearBuf.append("\n");
4854
4855
            // write the text strings
4856
0
            for (const HorizontalTextLayouter::Data &d : textLayouter.data) {
4857
0
                if (!d.fontName.empty()) {
4858
0
                    appearBuf.append(" q\n");
4859
0
                    GooString::appendf(appearBuf, "/{0:s} {1:.2f} Tf\n", d.fontName.c_str(), fontSize);
4860
0
                }
4861
0
                writeString(d.text);
4862
0
                appearBuf.append(" Tj\n");
4863
0
                if (!d.fontName.empty()) {
4864
0
                    appearBuf.append(" Q\n");
4865
0
                }
4866
0
            }
4867
0
        }
4868
0
    }
4869
    // cleanup
4870
0
    appearBuf.append("ET\n");
4871
0
    appearBuf.append("Q\n");
4872
0
    if (flags & EmitMarkedContentDrawTextFlag) {
4873
0
        appearBuf.append("EMC\n");
4874
0
    }
4875
4876
0
    return true;
4877
0
}
4878
4879
// Draw the variable text or caption for a field.
4880
bool AnnotAppearanceBuilder::drawListBox(const FormFieldChoice &fieldChoice, const AnnotBorder *border, const PDFRectangle &rect, const std::string &da, const GfxResources *resources, VariableTextQuadding quadding, XRef *xref,
4881
                                         Dict *resourcesDict)
4882
0
{
4883
0
    std::vector<std::string> daToks;
4884
0
    GooString convertedText;
4885
0
    const GfxFont *font;
4886
0
    double fontSize, borderWidth, x, y;
4887
0
    int tfPos, tmPos;
4888
0
    std::unique_ptr<const GfxFont> fontToFree;
4889
4890
    //~ if there is no MK entry, this should use the existing content stream,
4891
    //~ and only replace the marked content portion of it
4892
    //~ (this is only relevant for Tx fields)
4893
4894
    // parse the default appearance string
4895
0
    tfPos = tmPos = -1;
4896
0
    FormFieldText::tokenizeDA(da, &daToks, nullptr /*searchTok*/);
4897
0
    for (std::size_t k = 2; k < daToks.size(); ++k) {
4898
0
        if (k >= 2 && (daToks[k] == "Tf")) {
4899
0
            tfPos = k - 2;
4900
0
        } else if (k >= 6 && (daToks[k] == "Tm")) {
4901
0
            tmPos = k - 6;
4902
0
        }
4903
0
    }
4904
4905
    // get the font and font size
4906
0
    font = nullptr;
4907
0
    fontSize = 0;
4908
0
    if (tfPos >= 0) {
4909
0
        const std::string &tok = daToks[tfPos];
4910
0
        if (!tok.empty() && tok[0] == '/') {
4911
0
            const auto fontName = std::string_view(tok).substr(1);
4912
0
            if (!resources || !(font = resources->lookupFont(fontName).get())) {
4913
0
                if (xref != nullptr && resourcesDict != nullptr) {
4914
0
                    const char *fallback = determineFallbackFont(tok, "Helvetica");
4915
                    // The font variable sometimes points to an object that needs to be deleted
4916
                    // and sometimes not, depending on whether the call to lookupFont above fails.
4917
                    // When the code path right here is taken, the destructor of fontToFree
4918
                    // (which is a std::unique_ptr) will delete the font object at the end of this method.
4919
0
                    fontToFree = createAnnotDrawFont(xref, resourcesDict, fontName, fallback);
4920
0
                    font = fontToFree.get();
4921
0
                } else {
4922
0
                    error(errSyntaxError, -1, "Unknown font in field's DA string");
4923
0
                }
4924
0
            }
4925
0
        } else {
4926
0
            error(errSyntaxError, -1, "Invalid font name in 'Tf' operator in field's DA string");
4927
0
        }
4928
0
        fontSize = gatof(daToks[tfPos + 1].c_str());
4929
0
    } else {
4930
0
        error(errSyntaxError, -1, "Missing 'Tf' operator in field's DA string");
4931
0
    }
4932
0
    if (!font) {
4933
0
        return false;
4934
0
    }
4935
4936
    // get the border width
4937
0
    borderWidth = border ? border->getWidth() : 0;
4938
4939
0
    const std::vector<FormFieldChoiceOption> &choices = fieldChoice.getChoices();
4940
4941
    // compute font autosize
4942
0
    if (fontSize == 0) {
4943
0
        double wMax = 0;
4944
0
        for (const FormFieldChoiceOption &choice : choices) {
4945
0
            if (!choice.optionName) {
4946
0
                error(errSyntaxError, -1, "Invalid annotation listbox");
4947
0
                return false;
4948
0
            }
4949
0
            size_t j = 0;
4950
0
            double w;
4951
0
            Annot::layoutText(choice.optionName->toStr(), &convertedText, &j, *font, &w, 0.0, nullptr, false);
4952
0
            if (w > wMax) {
4953
0
                wMax = w;
4954
0
            }
4955
0
        }
4956
0
        fontSize = rect.y2 - rect.y1 - 2 * borderWidth;
4957
0
        const double fontSize2 = (rect.x2 - rect.x1 - 4 - 2 * borderWidth) / wMax;
4958
0
        if (fontSize2 < fontSize) {
4959
0
            fontSize = fontSize2;
4960
0
        }
4961
0
        fontSize = floor(fontSize);
4962
0
        if (tfPos >= 0) {
4963
0
            std::string &tok = daToks[tfPos + 1];
4964
0
            tok.clear();
4965
0
            GooString::appendf(tok, "{0:.2f}", fontSize);
4966
0
        }
4967
0
    }
4968
    // draw the text
4969
0
    y = rect.y2 - rect.y1 - 1.1 * fontSize;
4970
0
    for (size_t i = fieldChoice.getTopIndex(); i < choices.size(); ++i) {
4971
        // setup
4972
0
        appearBuf.append("q\n");
4973
4974
        // draw the background if selected
4975
0
        if (choices[i].selected) {
4976
0
            appearBuf.append("0 g f\n");
4977
0
            GooString::appendf(appearBuf, "{0:.2f} {1:.2f} {2:.2f} {3:.2f} re f\n", borderWidth, y - 0.2 * fontSize, rect.x2 - rect.x1 - 2 * borderWidth, 1.1 * fontSize);
4978
0
        }
4979
4980
        // setup
4981
0
        appearBuf.append("BT\n");
4982
4983
        // compute text width and start position
4984
0
        size_t j = 0;
4985
0
        double w = 0.0;
4986
0
        if (choices[i].optionName) {
4987
0
            Annot::layoutText(choices[i].optionName->toStr(), &convertedText, &j, *font, &w, 0.0, nullptr, false);
4988
0
        }
4989
0
        w *= fontSize;
4990
0
        switch (quadding) {
4991
0
        case VariableTextQuadding::leftJustified:
4992
0
        default:
4993
0
            x = borderWidth + 2;
4994
0
            break;
4995
0
        case VariableTextQuadding::centered:
4996
0
            x = (rect.x2 - rect.x1 - w) / 2;
4997
0
            break;
4998
0
        case VariableTextQuadding::rightJustified:
4999
0
            x = rect.x2 - rect.x1 - borderWidth - 2 - w;
5000
0
            break;
5001
0
        }
5002
5003
        // set the font matrix
5004
0
        if (tmPos >= 0) {
5005
0
            {
5006
0
                std::string &tok = daToks[tmPos + 4];
5007
0
                tok.clear();
5008
0
                GooString::appendf(tok, "{0:.2f}", x);
5009
0
            }
5010
0
            {
5011
0
                std::string &tok = daToks[tmPos + 5];
5012
0
                tok.clear();
5013
0
                GooString::appendf(tok, "{0:.2f}", y);
5014
0
            }
5015
0
        }
5016
5017
        // write the DA string
5018
0
        for (const std::string &daTok : daToks) {
5019
0
            appearBuf.append(daTok);
5020
0
            appearBuf.push_back(' ');
5021
0
        }
5022
5023
        // write the font matrix (if not part of the DA string)
5024
0
        if (tmPos < 0) {
5025
0
            GooString::appendf(appearBuf, "1 0 0 1 {0:.2f} {1:.2f} Tm\n", x, y);
5026
0
        }
5027
5028
        // change the text color if selected
5029
0
        if (choices[i].selected) {
5030
0
            appearBuf.append("1 g\n");
5031
0
        }
5032
5033
        // write the text string
5034
0
        writeString(convertedText.toStr());
5035
0
        appearBuf.append(" Tj\n");
5036
5037
        // cleanup
5038
0
        appearBuf.append("ET\n");
5039
0
        appearBuf.append("Q\n");
5040
5041
        // next line
5042
0
        y -= 1.1 * fontSize;
5043
0
    }
5044
5045
0
    return true;
5046
0
}
5047
5048
void AnnotAppearanceBuilder::drawFieldBorder(const FormField &field, const AnnotBorder &border, const AnnotAppearanceCharacs &appearCharacs, const PDFRectangle &rect)
5049
0
{
5050
0
    AnnotColor adjustedColor;
5051
0
    const double w = border.getWidth();
5052
5053
0
    const AnnotColor *aColor = appearCharacs.getBorderColor();
5054
0
    if (!aColor) {
5055
0
        aColor = appearCharacs.getBackColor();
5056
0
    }
5057
0
    if (!aColor) {
5058
0
        return;
5059
0
    }
5060
5061
0
    const double dx = rect.x2 - rect.x1;
5062
0
    const double dy = rect.y2 - rect.y1;
5063
5064
    // radio buttons with no caption have a round border
5065
0
    const bool hasCaption = appearCharacs.getNormalCaption() != nullptr;
5066
0
    if (field.getType() == formButton && static_cast<const FormFieldButton &>(field).getButtonType() == formButtonRadio && !hasCaption) {
5067
0
        double r = 0.5 * (dx < dy ? dx : dy);
5068
0
        switch (border.getStyle()) {
5069
0
        case AnnotBorder::borderDashed:
5070
0
            appearBuf.append("[");
5071
0
            for (double dash : border.getDash()) {
5072
0
                GooString::appendf(appearBuf, " {0:.2f}", dash);
5073
0
            }
5074
0
            appearBuf.append("] 0 d\n");
5075
            // fallthrough
5076
0
        case AnnotBorder::borderSolid:
5077
0
        case AnnotBorder::borderUnderlined:
5078
0
            GooString::appendf(appearBuf, "{0:.2f} w\n", w);
5079
0
            setDrawColor(*aColor, false);
5080
0
            drawCircle(0.5 * dx, 0.5 * dy, r - 0.5 * w, false);
5081
0
            break;
5082
0
        case AnnotBorder::borderBeveled:
5083
0
        case AnnotBorder::borderInset:
5084
0
            GooString::appendf(appearBuf, "{0:.2f} w\n", 0.5 * w);
5085
0
            setDrawColor(*aColor, false);
5086
0
            drawCircle(0.5 * dx, 0.5 * dy, r - 0.25 * w, false);
5087
0
            adjustedColor = AnnotColor(*aColor);
5088
0
            adjustedColor.adjustColor(border.getStyle() == AnnotBorder::borderBeveled ? 1 : -1);
5089
0
            setDrawColor(adjustedColor, false);
5090
0
            drawCircleTopLeft(0.5 * dx, 0.5 * dy, r - 0.75 * w);
5091
0
            adjustedColor = AnnotColor(*aColor);
5092
0
            adjustedColor.adjustColor(border.getStyle() == AnnotBorder::borderBeveled ? -1 : 1);
5093
0
            setDrawColor(adjustedColor, false);
5094
0
            drawCircleBottomRight(0.5 * dx, 0.5 * dy, r - 0.75 * w);
5095
0
            break;
5096
0
        }
5097
0
    } else {
5098
0
        switch (border.getStyle()) {
5099
0
        case AnnotBorder::borderDashed:
5100
0
            appearBuf.append("[");
5101
0
            for (double dash : border.getDash()) {
5102
0
                GooString::appendf(appearBuf, " {0:.2f}", dash);
5103
0
            }
5104
0
            appearBuf.append("] 0 d\n");
5105
            // fallthrough
5106
0
        case AnnotBorder::borderSolid:
5107
0
            GooString::appendf(appearBuf, "{0:.2f} w\n", w);
5108
0
            setDrawColor(*aColor, false);
5109
0
            GooString::appendf(appearBuf, "{0:.2f} {0:.2f} {1:.2f} {2:.2f} re s\n", 0.5 * w, dx - w, dy - w);
5110
0
            break;
5111
0
        case AnnotBorder::borderBeveled:
5112
0
        case AnnotBorder::borderInset:
5113
0
            adjustedColor = AnnotColor(*aColor);
5114
0
            adjustedColor.adjustColor(border.getStyle() == AnnotBorder::borderBeveled ? 1 : -1);
5115
0
            setDrawColor(adjustedColor, true);
5116
0
            appearBuf.append("0 0 m\n");
5117
0
            GooString::appendf(appearBuf, "0 {0:.2f} l\n", dy);
5118
0
            GooString::appendf(appearBuf, "{0:.2f} {1:.2f} l\n", dx, dy);
5119
0
            GooString::appendf(appearBuf, "{0:.2f} {1:.2f} l\n", dx - w, dy - w);
5120
0
            GooString::appendf(appearBuf, "{0:.2f} {1:.2f} l\n", w, dy - w);
5121
0
            GooString::appendf(appearBuf, "{0:.2f} {0:.2f} l\n", w);
5122
0
            appearBuf.append("f\n");
5123
0
            adjustedColor = AnnotColor(*aColor);
5124
0
            adjustedColor.adjustColor(border.getStyle() == AnnotBorder::borderBeveled ? -1 : 1);
5125
0
            setDrawColor(adjustedColor, true);
5126
0
            appearBuf.append("0 0 m\n");
5127
0
            GooString::appendf(appearBuf, "{0:.2f} 0 l\n", dx);
5128
0
            GooString::appendf(appearBuf, "{0:.2f} {1:.2f} l\n", dx, dy);
5129
0
            GooString::appendf(appearBuf, "{0:.2f} {1:.2f} l\n", dx - w, dy - w);
5130
0
            GooString::appendf(appearBuf, "{0:.2f} {1:.2f} l\n", dx - w, w);
5131
0
            GooString::appendf(appearBuf, "{0:.2f} {0:.2f} l\n", w);
5132
0
            appearBuf.append("f\n");
5133
0
            break;
5134
0
        case AnnotBorder::borderUnderlined:
5135
0
            GooString::appendf(appearBuf, "{0:.2f} w\n", w);
5136
0
            setDrawColor(*aColor, false);
5137
0
            GooString::appendf(appearBuf, "0 0 m {0:.2f} 0 l s\n", dx);
5138
0
            break;
5139
0
        }
5140
5141
        // clip to the inside of the border
5142
0
        GooString::appendf(appearBuf, "{0:.2f} {0:.2f} {1:.2f} {2:.2f} re W n\n", w, dx - 2 * w, dy - 2 * w);
5143
0
    }
5144
0
}
5145
5146
bool AnnotAppearanceBuilder::drawFormField(const FormField &field, const Form *form, const GfxResources *resources, const std::string &da, const AnnotBorder *border, const AnnotAppearanceCharacs *appearCharacs, const PDFRectangle &rect,
5147
                                           const std::optional<std::string> &appearState, XRef &xref, Dict &resourcesDict)
5148
0
{
5149
    // draw the field contents
5150
0
    switch (field.getType()) {
5151
0
    case formButton:
5152
0
        return drawFormFieldButton(static_cast<const FormFieldButton &>(field), form, resources, da, border, appearCharacs, rect, appearState, xref, resourcesDict);
5153
0
        break;
5154
0
    case formText:
5155
0
        return drawFormFieldText(static_cast<const FormFieldText &>(field), form, resources, da, border, appearCharacs, rect, xref, resourcesDict);
5156
0
    case formChoice:
5157
0
        return drawFormFieldChoice(static_cast<const FormFieldChoice &>(field), form, resources, da, border, appearCharacs, rect, xref, resourcesDict);
5158
0
        break;
5159
0
    case formSignature:
5160
0
        return drawSignatureFieldText(static_cast<const FormFieldSignature &>(field), form, da, border, rect, xref, resourcesDict);
5161
0
        break;
5162
0
    case formUndef:
5163
0
    default:
5164
0
        error(errSyntaxError, -1, "Unknown field type");
5165
0
    }
5166
5167
0
    return false;
5168
0
}
5169
5170
bool AnnotAppearanceBuilder::drawFormFieldButton(const FormFieldButton &field, const Form *form, const GfxResources *resources, const std::string &da, const AnnotBorder *border, const AnnotAppearanceCharacs *appearCharacs,
5171
                                                 const PDFRectangle &rect, const std::optional<std::string> &appearState, XRef &xref, Dict &resourcesDict)
5172
0
{
5173
0
    const GooString *caption = nullptr;
5174
0
    if (appearCharacs) {
5175
0
        caption = appearCharacs->getNormalCaption();
5176
0
    }
5177
5178
0
    switch (field.getButtonType()) {
5179
0
    case formButtonRadio: {
5180
        //~ Acrobat doesn't draw a caption if there is no AP dict (?)
5181
0
        if (appearState && appearState != "Off" && field.getState(appearState.value())) {
5182
0
            if (caption) {
5183
0
                return drawText(caption->toStr(), form, da, resources, border, appearCharacs, rect, VariableTextQuadding::centered, xref, resourcesDict, ForceZapfDingbatsDrawTextFlag);
5184
0
            }
5185
0
            if (appearCharacs) {
5186
0
                const AnnotColor *aColor = appearCharacs->getBorderColor();
5187
0
                if (aColor) {
5188
0
                    const double dx = rect.x2 - rect.x1;
5189
0
                    const double dy = rect.y2 - rect.y1;
5190
0
                    setDrawColor(*aColor, true);
5191
0
                    drawCircle(0.5 * dx, 0.5 * dy, 0.2 * (dx < dy ? dx : dy), true);
5192
0
                }
5193
0
                return true;
5194
0
            }
5195
0
        }
5196
0
    } break;
5197
0
    case formButtonPush:
5198
0
        if (caption) {
5199
0
            return drawText(caption->toStr(), form, da, resources, border, appearCharacs, rect, VariableTextQuadding::centered, xref, resourcesDict);
5200
0
        }
5201
0
        break;
5202
0
    case formButtonCheck:
5203
0
        if (appearState && appearState->compare("Off") != 0) {
5204
0
            if (!caption) {
5205
0
                std::string checkMark("3");
5206
0
                return drawText(checkMark, form, da, resources, border, appearCharacs, rect, VariableTextQuadding::centered, xref, resourcesDict, ForceZapfDingbatsDrawTextFlag);
5207
0
            }
5208
0
            return drawText(caption->toStr(), form, da, resources, border, appearCharacs, rect, VariableTextQuadding::centered, xref, resourcesDict, ForceZapfDingbatsDrawTextFlag);
5209
0
        }
5210
0
        break;
5211
0
    }
5212
5213
0
    return true;
5214
0
}
5215
5216
bool AnnotAppearanceBuilder::drawFormFieldText(const FormFieldText &fieldText, const Form *form, const GfxResources *resources, const std::string &da, const AnnotBorder *border, const AnnotAppearanceCharacs *appearCharacs,
5217
                                               const PDFRectangle &rect, XRef &xref, Dict &resourcesDict)
5218
0
{
5219
0
    VariableTextQuadding quadding;
5220
0
    const GooString *contents;
5221
5222
0
    contents = fieldText.getAppearanceContent();
5223
0
    if (contents) {
5224
0
        if (fieldText.hasTextQuadding()) {
5225
0
            quadding = fieldText.getTextQuadding();
5226
0
        } else if (form) {
5227
0
            quadding = form->getTextQuadding();
5228
0
        } else {
5229
0
            quadding = VariableTextQuadding::leftJustified;
5230
0
        }
5231
5232
0
        const int nCombs = fieldText.isComb() ? fieldText.getMaxLen() : 0;
5233
5234
0
        int flags = EmitMarkedContentDrawTextFlag;
5235
0
        if (fieldText.isMultiline()) {
5236
0
            flags = flags | MultilineDrawTextFlag;
5237
0
        }
5238
0
        if (fieldText.isPassword()) {
5239
0
            flags = flags | TurnTextToStarsDrawTextFlag;
5240
0
        }
5241
0
        return drawText(contents->toStr(), form, da, resources, border, appearCharacs, rect, quadding, xref, resourcesDict, flags, nCombs);
5242
0
    }
5243
5244
0
    return true;
5245
0
}
5246
5247
static void setChildDictEntryValue(Dict *parentDict, const char *childDictName, const char *childDictEntryName, const Ref childDictEntryValue, XRef *xref)
5248
0
{
5249
0
    Object childDictionaryObj = parentDict->lookup(childDictName);
5250
0
    if (!childDictionaryObj.isDict()) {
5251
0
        childDictionaryObj = Object(std::make_unique<Dict>(xref));
5252
0
        parentDict->set(childDictName, childDictionaryObj.copy());
5253
0
    }
5254
0
    childDictionaryObj.dictSet(childDictEntryName, Object(childDictEntryValue));
5255
0
}
5256
5257
bool AnnotAppearanceBuilder::drawSignatureFieldText(const FormFieldSignature &field, const Form *form, const std::string &_da, const AnnotBorder *border, const PDFRectangle &rect, XRef &xref, Dict &resourcesDict)
5258
0
{
5259
0
    const std::string &contents = field.getCustomAppearanceContent().toStr();
5260
0
    if (contents.empty()) {
5261
0
        return false;
5262
0
    }
5263
5264
0
    if (field.getImageResource() != Ref::INVALID()) {
5265
0
        const double width = rect.x2 - rect.x1;
5266
0
        const double height = rect.y2 - rect.y1;
5267
0
        static const char *imageResourceId = "SigImg";
5268
0
        setChildDictEntryValue(&resourcesDict, "XObject", imageResourceId, field.getImageResource(), &xref);
5269
0
        Matrix matrix = { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 };
5270
0
        matrix.scale(width, height);
5271
0
        const std::string imgBuffer = GooString::format("\nq {0:.1g} {1:.1g} {2:.1g} {3:.1g} {4:.1g} {5:.1g} cm /{6:s} Do Q\n", matrix.m[0], matrix.m[1], matrix.m[2], matrix.m[3], matrix.m[4], matrix.m[5], imageResourceId);
5272
0
        append(imgBuffer.c_str());
5273
0
    }
5274
5275
0
    const std::string &leftText = field.getCustomAppearanceLeftContent().toStr();
5276
0
    if (leftText.empty()) {
5277
0
        drawSignatureFieldText(contents, form, DefaultAppearance(_da), border, rect, xref, resourcesDict, 0, false /* don't center vertically */, false /* don't center horizontally */);
5278
0
    } else {
5279
0
        const double halfWidth = (rect.x2 - rect.x1) / 2;
5280
5281
0
        double borderWidth = 0;
5282
5283
0
        if (border) {
5284
0
            borderWidth = border->getWidth();
5285
0
        }
5286
5287
0
        const double wMax = (rect.x2 - rect.x1) - 2 * borderWidth - 4;
5288
0
        const double hMax = (rect.y2 - rect.y1) - 2 * borderWidth;
5289
5290
0
        DefaultAppearance daLeft(_da);
5291
5292
0
        double leftFontSize = field.getCustomAppearanceLeftFontSize();
5293
0
        if (leftFontSize == 0) {
5294
0
            std::shared_ptr<GfxFont> font = form->getDefaultResources()->lookupFont(daLeft.getFontName());
5295
0
            leftFontSize = Annot::calculateFontSize(form, *font, leftText, wMax / 2.0, hMax);
5296
0
        }
5297
0
        daLeft.setFontPtSize(leftFontSize);
5298
5299
0
        PDFRectangle rectLeft(rect.x1, rect.y1, rect.x1 + halfWidth, rect.y2);
5300
0
        drawSignatureFieldText(leftText, form, daLeft, border, rectLeft, xref, resourcesDict, 0, true /* center vertically */, true /* center horizontally */);
5301
5302
0
        DefaultAppearance daRight(_da);
5303
5304
0
        double fontSize = daRight.getFontPtSize();
5305
0
        if (fontSize == 0) {
5306
0
            std::shared_ptr<GfxFont> font = form->getDefaultResources()->lookupFont(daLeft.getFontName());
5307
0
            fontSize = Annot::calculateFontSize(form, *font, contents, wMax / 2.0, hMax);
5308
0
        }
5309
0
        daRight.setFontPtSize(fontSize);
5310
5311
0
        PDFRectangle rectRight(rectLeft.x2, rect.y1, rect.x2, rect.y2);
5312
0
        drawSignatureFieldText(contents, form, daRight, border, rectRight, xref, resourcesDict, halfWidth, true /* center vertically */, false /* don't center horizontally */);
5313
0
    }
5314
5315
0
    return true;
5316
0
}
5317
5318
void AnnotAppearanceBuilder::drawSignatureFieldText(const std::string &text, const Form *form, const DefaultAppearance &da, const AnnotBorder *border, const PDFRectangle &rect, XRef &xref, Dict &resourcesDict, double leftMargin,
5319
                                                    bool centerVertically, bool centerHorizontally)
5320
0
{
5321
0
    double borderWidth = 0;
5322
0
    append("q\n");
5323
5324
0
    if (border) {
5325
0
        borderWidth = border->getWidth();
5326
0
        if (borderWidth > 0) {
5327
0
            setLineStyleForBorder(*border);
5328
0
        }
5329
0
    }
5330
5331
    // Box size
5332
0
    const double width = rect.x2 - rect.x1;
5333
0
    const double height = rect.y2 - rect.y1;
5334
0
    const double textmargin = borderWidth * 2;
5335
0
    const double textwidth = width - 2 * textmargin;
5336
5337
    // create a Helvetica fake font
5338
0
    std::shared_ptr<const GfxFont> font = form ? form->getDefaultResources()->lookupFont(da.getFontName()) : nullptr;
5339
0
    if (!font) {
5340
0
        font = createAnnotDrawFont(&xref, &resourcesDict, da.getFontName());
5341
0
    }
5342
5343
    // Setup text clipping
5344
0
    appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} re W n\n", leftMargin + textmargin, textmargin, textwidth, height - 2 * textmargin);
5345
0
    setDrawColor(*da.getFontColor(), true);
5346
0
    const DrawMultiLineTextResult textCommands =
5347
0
            drawMultiLineText(text, textwidth, form, *font, da.getFontName(), da.getFontPtSize(), centerHorizontally ? VariableTextQuadding::centered : VariableTextQuadding::leftJustified, 0 /*borderWidth*/);
5348
5349
0
    double yDelta = height - textmargin;
5350
0
    if (centerVertically) {
5351
0
        const double outTextHeight = textCommands.nLines * da.getFontPtSize();
5352
0
        if (outTextHeight < height) {
5353
0
            yDelta = height - (height - outTextHeight) / 2;
5354
0
        }
5355
0
    }
5356
0
    appendf("BT 1 0 0 1 {0:.2f} {1:.2f} Tm\n", leftMargin + textmargin, yDelta);
5357
0
    append(textCommands.text.c_str());
5358
0
    append("ET Q\n");
5359
0
}
5360
5361
bool AnnotAppearanceBuilder::drawFormFieldChoice(const FormFieldChoice &fieldChoice, const Form *form, const GfxResources *resources, const std::string &da, const AnnotBorder *border, const AnnotAppearanceCharacs *appearCharacs,
5362
                                                 const PDFRectangle &rect, XRef &xref, Dict &resourcesDict)
5363
0
{
5364
0
    const GooString *selected;
5365
0
    VariableTextQuadding quadding;
5366
5367
0
    if (fieldChoice.hasTextQuadding()) {
5368
0
        quadding = fieldChoice.getTextQuadding();
5369
0
    } else if (form) {
5370
0
        quadding = form->getTextQuadding();
5371
0
    } else {
5372
0
        quadding = VariableTextQuadding::leftJustified;
5373
0
    }
5374
5375
0
    if (fieldChoice.isCombo()) {
5376
0
        selected = fieldChoice.getAppearanceSelectedChoice();
5377
0
        if (selected) {
5378
0
            return drawText(selected->toStr(), form, da, resources, border, appearCharacs, rect, quadding, xref, resourcesDict, EmitMarkedContentDrawTextFlag);
5379
            //~ Acrobat draws a popup icon on the right side
5380
0
        }
5381
        // list box
5382
0
    } else {
5383
0
        return drawListBox(fieldChoice, border, rect, da, resources, quadding, &xref, &resourcesDict);
5384
0
    }
5385
5386
0
    return true;
5387
0
}
5388
5389
// Should we also merge Arrays?
5390
static void recursiveMergeDicts(Dict *primary, const Dict *secondary, RefRecursionChecker *alreadySeenDicts)
5391
0
{
5392
0
    for (int i = 0; i < secondary->getLength(); ++i) {
5393
0
        const std::string &key = secondary->getKey(i);
5394
0
        if (!primary->hasKey(key)) {
5395
0
            primary->add(key, secondary->lookup(key).deepCopy());
5396
0
        } else {
5397
0
            Ref primaryRef;
5398
0
            Object primaryObj = primary->lookup(key, &primaryRef);
5399
0
            if (primaryObj.isDict()) {
5400
0
                Ref secondaryRef;
5401
0
                Object secondaryObj = secondary->lookup(key, &secondaryRef);
5402
0
                if (secondaryObj.isDict()) {
5403
0
                    if (!alreadySeenDicts->insert(primaryRef) || !alreadySeenDicts->insert(secondaryRef)) {
5404
                        // bad PDF
5405
0
                        return;
5406
0
                    }
5407
0
                    recursiveMergeDicts(primaryObj.getDict(), secondaryObj.getDict(), alreadySeenDicts);
5408
0
                }
5409
0
            }
5410
0
        }
5411
0
    }
5412
0
}
5413
5414
static void recursiveMergeDicts(Dict *primary, const Dict *secondary)
5415
0
{
5416
0
    RefRecursionChecker alreadySeenDicts;
5417
0
    recursiveMergeDicts(primary, secondary, &alreadySeenDicts);
5418
0
}
5419
5420
void AnnotWidget::generateFieldAppearance(bool *addedDingbatsResource)
5421
0
{
5422
0
    AnnotAppearanceBuilder appearBuilder;
5423
5424
    // draw the background
5425
0
    if (appearCharacs) {
5426
0
        const AnnotColor *aColor = appearCharacs->getBackColor();
5427
0
        if (aColor) {
5428
0
            appearBuilder.setDrawColor(*aColor, true);
5429
0
            appearBuilder.appendf("0 0 {0:.2f} {1:.2f} re f\n", rect->x2 - rect->x1, rect->y2 - rect->y1);
5430
0
        }
5431
0
    }
5432
5433
    // draw the border
5434
0
    if (appearCharacs && border && border->getWidth() > 0) {
5435
0
        appearBuilder.drawFieldBorder(*field, *border, *appearCharacs, *rect);
5436
0
    }
5437
5438
0
    const std::string &fieldDA = field->getDefaultAppearance();
5439
5440
    // Let's init resourcesDictObj and resources.
5441
    // In PDF 1.2, an additional entry in the field dictionary, DR, was defined.
5442
    // Beginning with PDF 1.5, this entry is obsolete.
5443
    // And yet Acrobat Reader seems to be taking a field's DR into account.
5444
0
    Object resourcesDictObj;
5445
0
    const GfxResources *resources = nullptr;
5446
0
    std::unique_ptr<GfxResources> resourcesToFree = nullptr;
5447
0
    if (field->getObj() && field->getObj()->isDict()) {
5448
        // Let's use a field's resource dictionary.
5449
0
        resourcesDictObj = field->getObj()->dictLookup("DR");
5450
0
        if (resourcesDictObj.isDict()) {
5451
0
            if (form && form->getDefaultResourcesObj()->isDict()) {
5452
0
                resourcesDictObj = resourcesDictObj.deepCopy();
5453
0
                recursiveMergeDicts(resourcesDictObj.getDict(), form->getDefaultResourcesObj()->getDict());
5454
0
            }
5455
0
            resourcesToFree = std::make_unique<GfxResources>(doc->getXRef(), resourcesDictObj.getDict(), nullptr);
5456
0
            resources = resourcesToFree.get();
5457
0
        }
5458
0
    }
5459
0
    if (!resourcesDictObj.isDict()) {
5460
        // No luck with a field's resource dictionary. Let's use an AcroForm's resource dictionary.
5461
0
        if (form && form->getDefaultResourcesObj()->isDict()) {
5462
0
            resourcesDictObj = form->getDefaultResourcesObj()->deepCopy();
5463
0
            resources = form->getDefaultResources();
5464
0
        }
5465
0
    }
5466
0
    if (!resourcesDictObj.isDict()) {
5467
0
        resourcesDictObj = Object(std::make_unique<Dict>(doc->getXRef()));
5468
0
    }
5469
5470
0
    auto appearDict = std::make_unique<Dict>(doc->getXRef());
5471
0
    {
5472
0
        const std::string &daToUse = fieldDA.empty() && form ? form->getDefaultAppearance() : fieldDA;
5473
0
        const bool success = appearBuilder.drawFormField(*field, form, resources, daToUse, border.get(), appearCharacs.get(), *rect, appearState, *doc->getXRef(), *resourcesDictObj.getDict());
5474
0
        if (!success && form && daToUse != form->getDefaultAppearance()) {
5475
0
            appearBuilder.drawFormField(*field, form, resources, form->getDefaultAppearance(), border.get(), appearCharacs.get(), *rect, appearState, *doc->getXRef(), *resourcesDictObj.getDict());
5476
0
        }
5477
0
    }
5478
5479
0
    if (addedDingbatsResource) {
5480
0
        *addedDingbatsResource = appearBuilder.getAddedDingbatsResource();
5481
0
    }
5482
5483
0
    const std::string &appearBuf = appearBuilder.buffer();
5484
    // fill the appearance stream dictionary
5485
0
    appearDict->add("Length", Object(static_cast<int>(appearBuf.size())));
5486
0
    appearDict->add("Subtype", Object::name("Form"));
5487
0
    auto bbox = std::make_unique<Array>(doc->getXRef());
5488
0
    bbox->add(Object(0));
5489
0
    bbox->add(Object(0));
5490
0
    bbox->add(Object(rect->x2 - rect->x1));
5491
0
    bbox->add(Object(rect->y2 - rect->y1));
5492
0
    appearDict->add("BBox", Object(std::move(bbox)));
5493
5494
    // set the resource dictionary
5495
0
    if (resourcesDictObj.getDict()->getLength() > 0) {
5496
0
        appearDict->set("Resources", std::move(resourcesDictObj));
5497
0
    }
5498
5499
    // build the appearance stream
5500
0
    std::vector<char> data { appearBuf.c_str(), appearBuf.c_str() + appearBuf.size() };
5501
0
    auto appearStream = std::make_unique<AutoFreeMemStream>(std::move(data), Object(std::move(appearDict)));
5502
0
    if (hasBeenUpdated) {
5503
        // We should technically do this for all annots but AnnotFreeText
5504
        // forms are particularly special since we're potentially embeddeing a font so we really need
5505
        // to set the AP and not let other renderers guess it from the contents
5506
        // In addition, we keep the appearState of checkboxes to prevent them from being deselected
5507
0
        bool keepAppearState = field->getType() == formButton && static_cast<const FormFieldButton *>(field)->getButtonType() == formButtonCheck;
5508
0
        setNewAppearance(Object(std::move(appearStream)), keepAppearState);
5509
0
    } else {
5510
0
        appearance = Object(std::move(appearStream));
5511
0
    }
5512
0
}
5513
5514
void AnnotWidget::updateAppearanceStream()
5515
0
{
5516
    // If this the first time updateAppearanceStream() is called on this widget,
5517
    // destroy the AP dictionary because we are going to create a new one.
5518
0
    if (updatedAppearanceStream == Ref::INVALID()) {
5519
0
        invalidateAppearance(); // Delete AP dictionary and all referenced streams
5520
0
    }
5521
5522
    // There's no need to create a new appearance stream if NeedAppearances is
5523
    // set, because it will be ignored next time anyway.
5524
    // except if signature type; most readers can't figure out how to create an
5525
    // appearance for those and thus renders nothing.
5526
0
    if (form && form->getNeedAppearances()) {
5527
0
        if (field->getType() != FormFieldType::formSignature) {
5528
0
            return;
5529
0
        }
5530
0
    }
5531
5532
    // Create the new appearance
5533
0
    generateFieldAppearance();
5534
5535
    // Fetch the appearance stream we've just created
5536
0
    Object obj1 = appearance.fetch(doc->getXRef());
5537
5538
    // If this the first time updateAppearanceStream() is called on this widget,
5539
    // create a new AP dictionary containing the new appearance stream.
5540
    // Otherwise, just update the stream we had created previously.
5541
0
    if (updatedAppearanceStream == Ref::INVALID()) {
5542
        // Write the appearance stream
5543
0
        updatedAppearanceStream = doc->getXRef()->addIndirectObject(obj1);
5544
5545
        // Write the AP dictionary
5546
0
        obj1 = Object(std::make_unique<Dict>(doc->getXRef()));
5547
0
        obj1.dictAdd("N", Object(updatedAppearanceStream));
5548
5549
        // Update our internal pointers to the appearance dictionary
5550
0
        appearStreams = std::make_unique<AnnotAppearance>(doc, &obj1);
5551
5552
0
        update("AP", std::move(obj1));
5553
0
    } else {
5554
        // Replace the existing appearance stream
5555
0
        doc->getXRef()->setModifiedObject(&obj1, updatedAppearanceStream);
5556
0
    }
5557
0
}
5558
5559
void AnnotWidget::draw(Gfx *gfx, bool printing)
5560
0
{
5561
0
    if (!isVisible(printing)) {
5562
0
        return;
5563
0
    }
5564
5565
0
    annotLocker();
5566
5567
    // Only construct the appearance stream when
5568
    // - annot doesn't have an AP or
5569
    // - NeedAppearances is true and it isn't a Signature. There isn't enough data in our objects to generate it for signatures
5570
0
    bool addedDingbatsResource = false;
5571
0
    if (field) {
5572
0
        if (appearance.isNull() || (field->getType() != FormFieldType::formSignature && form && form->getNeedAppearances())) {
5573
0
            generateFieldAppearance(&addedDingbatsResource);
5574
0
        }
5575
0
    }
5576
5577
    // draw the appearance stream
5578
0
    Object obj = appearance.fetch(gfx->getXRef());
5579
0
    if (addedDingbatsResource) {
5580
        // We are forcing ZaDb but the font does not exist
5581
        // so create a fake one
5582
        // If refactoring this code remember to test issue #1642 afterwards
5583
0
        auto fontDict = std::make_unique<Dict>(gfx->getXRef());
5584
0
        fontDict->add("BaseFont", Object::name("ZapfDingbats"));
5585
0
        fontDict->add("Subtype", Object::name("Type1"));
5586
5587
0
        auto fontsDict = std::make_unique<Dict>(gfx->getXRef());
5588
0
        fontsDict->add("ZaDb", Object(std::move(fontDict)));
5589
5590
0
        Dict dict(gfx->getXRef());
5591
0
        dict.add("Font", Object(std::move(fontsDict)));
5592
0
        gfx->pushResources(&dict);
5593
0
    }
5594
0
    gfx->drawAnnot(&obj, nullptr, color.get(), rect->x1, rect->y1, rect->x2, rect->y2, getRotation());
5595
0
    if (addedDingbatsResource) {
5596
0
        gfx->popResources();
5597
0
    }
5598
0
}
5599
5600
//------------------------------------------------------------------------
5601
// AnnotMovie
5602
//------------------------------------------------------------------------
5603
0
AnnotMovie::AnnotMovie(PDFDoc *docA, const PDFRectangle &rectA, const Movie &movieA) : Annot(docA, rectA)
5604
0
{
5605
0
    type = typeMovie;
5606
0
    annotObj.dictSet("Subtype", Object::name("Movie"));
5607
5608
0
    movie = movieA.copy();
5609
    // TODO: create movie dict from movieA
5610
5611
0
    initialize(annotObj.getDict());
5612
0
}
5613
5614
0
AnnotMovie::AnnotMovie(PDFDoc *docA, Object &&dictObject, const Object &obj) : Annot(docA, std::move(dictObject), obj)
5615
0
{
5616
0
    type = typeMovie;
5617
0
    initialize(annotObj.getDict());
5618
0
}
5619
5620
0
AnnotMovie::~AnnotMovie() = default;
5621
5622
void AnnotMovie::initialize(Dict *dict)
5623
0
{
5624
0
    Object obj1;
5625
5626
0
    obj1 = dict->lookup("T");
5627
0
    if (obj1.isString()) {
5628
0
        title = obj1.takeString();
5629
0
    }
5630
5631
0
    Object movieDict = dict->lookup("Movie");
5632
0
    if (movieDict.isDict()) {
5633
0
        Object obj2 = dict->lookup("A");
5634
0
        if (obj2.isDict()) {
5635
0
            movie = std::make_unique<Movie>(&movieDict, &obj2);
5636
0
        } else {
5637
0
            movie = std::make_unique<Movie>(&movieDict);
5638
0
        }
5639
0
        if (!movie->isOk()) {
5640
0
            movie = nullptr;
5641
0
            ok = false;
5642
0
        }
5643
0
    } else {
5644
0
        error(errSyntaxError, -1, "Bad Annot Movie");
5645
0
        ok = false;
5646
0
    }
5647
0
}
5648
5649
void AnnotMovie::draw(Gfx *gfx, bool printing)
5650
0
{
5651
0
    if (!isVisible(printing)) {
5652
0
        return;
5653
0
    }
5654
5655
0
    annotLocker();
5656
0
    if (appearance.isNull() && movie->getShowPoster()) {
5657
0
        int width, height;
5658
0
        Object poster = movie->getPoster();
5659
0
        movie->getAspect(&width, &height);
5660
5661
0
        if (width != -1 && height != -1 && !poster.isNone()) {
5662
0
            std::string appearBuf;
5663
0
            appearBuf.append("q\n");
5664
0
            GooString::appendf(appearBuf, "{0:d} 0 0 {1:d} 0 0 cm\n", width, height);
5665
0
            appearBuf.append("/MImg Do\n");
5666
0
            appearBuf.append("Q\n");
5667
5668
0
            auto imgDict = std::make_unique<Dict>(gfx->getXRef());
5669
0
            imgDict->set("MImg", std::move(poster));
5670
5671
0
            auto resDict = std::make_unique<Dict>(gfx->getXRef());
5672
0
            resDict->set("XObject", Object(std::move(imgDict)));
5673
5674
0
            auto formDict = std::make_unique<Dict>(gfx->getXRef());
5675
0
            formDict->set("Length", Object(static_cast<int>(appearBuf.size())));
5676
0
            formDict->set("Subtype", Object::name("Form"));
5677
0
            formDict->set("Name", Object::name("FRM"));
5678
0
            auto bboxArray = std::make_unique<Array>(gfx->getXRef());
5679
0
            bboxArray->add(Object(0));
5680
0
            bboxArray->add(Object(0));
5681
0
            bboxArray->add(Object(width));
5682
0
            bboxArray->add(Object(height));
5683
0
            formDict->set("BBox", Object(std::move(bboxArray)));
5684
0
            auto matrix = std::make_unique<Array>(gfx->getXRef());
5685
0
            matrix->add(Object(1));
5686
0
            matrix->add(Object(0));
5687
0
            matrix->add(Object(0));
5688
0
            matrix->add(Object(1));
5689
0
            matrix->add(Object(-width / 2));
5690
0
            matrix->add(Object(-height / 2));
5691
0
            formDict->set("Matrix", Object(std::move(matrix)));
5692
0
            formDict->set("Resources", Object(std::move(resDict)));
5693
5694
0
            std::vector<char> data { appearBuf.c_str(), appearBuf.c_str() + appearBuf.size() };
5695
0
            auto mStream = std::make_unique<AutoFreeMemStream>(std::move(data), Object(std::move(formDict)));
5696
5697
0
            auto dict = std::make_unique<Dict>(gfx->getXRef());
5698
0
            dict->set("FRM", Object(std::move(mStream)));
5699
5700
0
            auto resDict2 = std::make_unique<Dict>(gfx->getXRef());
5701
0
            resDict2->set("XObject", Object(std::move(dict)));
5702
5703
0
            appearBuf.clear();
5704
0
            appearBuf.append("q\n");
5705
0
            GooString::appendf(appearBuf, "0 0 {0:d} {1:d} re W n\n", width, height);
5706
0
            appearBuf.append("q\n");
5707
0
            GooString::appendf(appearBuf, "0 0 {0:d} {1:d} re W n\n", width, height);
5708
0
            GooString::appendf(appearBuf, "1 0 0 1 {0:d} {1:d} cm\n", width / 2, height / 2);
5709
0
            appearBuf.append("/FRM Do\n");
5710
0
            appearBuf.append("Q\n");
5711
0
            appearBuf.append("Q\n");
5712
5713
0
            const std::array<double, 4> bbox = { 0, 0, static_cast<double>(width), static_cast<double>(height) };
5714
0
            appearance = createForm(appearBuf, bbox, false, std::move(resDict2));
5715
0
        }
5716
0
    }
5717
5718
    // draw the appearance stream
5719
0
    Object obj = appearance.fetch(gfx->getXRef());
5720
0
    gfx->drawAnnot(&obj, nullptr, color.get(), rect->x1, rect->y1, rect->x2, rect->y2, getRotation());
5721
0
}
5722
5723
//------------------------------------------------------------------------
5724
// AnnotScreen
5725
//------------------------------------------------------------------------
5726
0
AnnotScreen::AnnotScreen(PDFDoc *docA, const PDFRectangle &rectA) : Annot(docA, rectA)
5727
0
{
5728
0
    type = typeScreen;
5729
5730
0
    annotObj.dictSet("Subtype", Object::name("Screen"));
5731
0
    initialize(annotObj.getDict());
5732
0
}
5733
5734
0
AnnotScreen::AnnotScreen(PDFDoc *docA, Object &&dictObject, const Object &obj) : Annot(docA, std::move(dictObject), obj)
5735
0
{
5736
0
    type = typeScreen;
5737
0
    initialize(annotObj.getDict());
5738
0
}
5739
5740
0
AnnotScreen::~AnnotScreen() = default;
5741
5742
void AnnotScreen::initialize(Dict *dict)
5743
0
{
5744
0
    Object obj1;
5745
5746
0
    obj1 = dict->lookup("T");
5747
0
    if (obj1.isString()) {
5748
0
        title = std::make_unique<GooString>(obj1.getString());
5749
0
    }
5750
5751
0
    obj1 = dict->lookup("A");
5752
0
    if (obj1.isDict()) {
5753
0
        action = LinkAction::parseAction(&obj1, doc->getCatalog()->getBaseURI());
5754
0
        if (action && action->getKind() == actionRendition && page == 0) {
5755
0
            error(errSyntaxError, -1, "Invalid Rendition action: associated screen annotation without P");
5756
0
            action = nullptr;
5757
0
            ok = false;
5758
0
        }
5759
0
    }
5760
5761
0
    additionalActions = dict->lookupNF("AA").copy();
5762
5763
0
    obj1 = dict->lookup("MK");
5764
0
    if (obj1.isDict()) {
5765
0
        appearCharacs = std::make_unique<AnnotAppearanceCharacs>(obj1.getDict());
5766
0
    }
5767
0
}
5768
5769
std::unique_ptr<LinkAction> AnnotScreen::getAdditionalAction(AdditionalActionsType additionalActionType)
5770
0
{
5771
0
    if (additionalActionType == actionFocusIn || additionalActionType == actionFocusOut) { // not defined for screen annotation
5772
0
        return nullptr;
5773
0
    }
5774
5775
0
    return ::getAdditionalAction(additionalActionType, &additionalActions, doc);
5776
0
}
5777
5778
//------------------------------------------------------------------------
5779
// AnnotStamp
5780
//------------------------------------------------------------------------
5781
0
AnnotStamp::AnnotStamp(PDFDoc *docA, const PDFRectangle &rectA) : AnnotMarkup(docA, rectA)
5782
0
{
5783
0
    type = typeStamp;
5784
0
    annotObj.dictSet("Subtype", Object::name("Stamp"));
5785
0
    initialize(annotObj.getDict());
5786
0
}
5787
5788
0
AnnotStamp::AnnotStamp(PDFDoc *docA, Object &&dictObject, const Object &obj) : AnnotMarkup(docA, std::move(dictObject), obj)
5789
0
{
5790
0
    type = typeStamp;
5791
0
    initialize(annotObj.getDict());
5792
0
}
5793
5794
0
AnnotStamp::~AnnotStamp() = default;
5795
5796
void AnnotStamp::initialize(Dict *dict)
5797
0
{
5798
0
    Object obj1 = dict->lookup("Name");
5799
0
    if (obj1.isName()) {
5800
0
        icon = obj1.getNameString();
5801
0
    } else {
5802
0
        icon = std::string { "Draft" };
5803
0
    }
5804
5805
0
    updatedAppearanceStream = Ref::INVALID();
5806
0
}
5807
5808
void AnnotStamp::generateStampCustomAppearance()
5809
0
{
5810
0
    Ref imgRef = stampImageHelper->getRef();
5811
0
    const std::string imgStrName = "X" + std::to_string(imgRef.num);
5812
5813
0
    AnnotAppearanceBuilder appearBuilder;
5814
0
    appearBuilder.append("q\n");
5815
0
    appearBuilder.append("/GS0 gs\n");
5816
0
    appearBuilder.appendf("{0:.3f} 0 0 {1:.3f} 0 0 cm\n", rect->x2 - rect->x1, rect->y2 - rect->y1);
5817
0
    appearBuilder.append("/");
5818
0
    appearBuilder.append(imgStrName.c_str());
5819
0
    appearBuilder.append(" Do\n");
5820
0
    appearBuilder.append("Q\n");
5821
5822
0
    std::unique_ptr<Dict> resDict = createResourcesDict(imgStrName.c_str(), Object(imgRef), "GS0", opacity, nullptr);
5823
5824
0
    const std::array<double, 4> bboxArray = { 0, 0, rect->x2 - rect->x1, rect->y2 - rect->y1 };
5825
0
    const std::string &appearBuf = appearBuilder.buffer();
5826
0
    appearance = createForm(appearBuf, bboxArray, false, std::move(resDict));
5827
5828
0
    if (updatedAppearanceStream == Ref::INVALID()) {
5829
0
        updatedAppearanceStream = doc->getXRef()->addIndirectObject(appearance);
5830
0
    } else {
5831
0
        Object obj1 = appearance.fetch(doc->getXRef());
5832
0
        doc->getXRef()->setModifiedObject(&obj1, updatedAppearanceStream);
5833
0
    }
5834
5835
0
    Object obj1 = Object(std::make_unique<Dict>(doc->getXRef()));
5836
0
    obj1.dictAdd("N", Object(updatedAppearanceStream));
5837
0
    update("AP", std::move(obj1));
5838
0
}
5839
5840
void AnnotStamp::updateAppearanceResDict()
5841
0
{
5842
0
    if (appearance.isNull()) {
5843
0
        if (stampImageHelper != nullptr) {
5844
0
            generateStampCustomAppearance();
5845
0
        } else {
5846
0
            generateStampDefaultAppearance();
5847
0
        }
5848
0
    }
5849
0
}
5850
5851
Object AnnotStamp::getAppearanceResDict()
5852
0
{
5853
0
    updateAppearanceResDict();
5854
5855
0
    return Annot::getAppearanceResDict();
5856
0
}
5857
5858
void AnnotStamp::generateStampDefaultAppearance()
5859
0
{
5860
0
    std::unique_ptr<Dict> extGStateDict;
5861
0
    AnnotAppearanceBuilder defaultAppearanceBuilder;
5862
5863
0
    double stampUnscaledWidth;
5864
0
    double stampUnscaledHeight;
5865
0
    const char *stampCode;
5866
0
    if (icon == "Approved") {
5867
0
        stampUnscaledWidth = ANNOT_STAMP_APPROVED_WIDTH;
5868
0
        stampUnscaledHeight = ANNOT_STAMP_APPROVED_HEIGHT;
5869
0
        stampCode = ANNOT_STAMP_APPROVED;
5870
0
        extGStateDict = getApprovedStampExtGStateDict(doc);
5871
0
    } else if (icon == "AsIs") {
5872
0
        stampUnscaledWidth = ANNOT_STAMP_AS_IS_WIDTH;
5873
0
        stampUnscaledHeight = ANNOT_STAMP_AS_IS_HEIGHT;
5874
0
        stampCode = ANNOT_STAMP_AS_IS;
5875
0
        extGStateDict = getAsIsStampExtGStateDict(doc);
5876
0
    } else if (icon == "Confidential") {
5877
0
        stampUnscaledWidth = ANNOT_STAMP_CONFIDENTIAL_WIDTH;
5878
0
        stampUnscaledHeight = ANNOT_STAMP_CONFIDENTIAL_HEIGHT;
5879
0
        stampCode = ANNOT_STAMP_CONFIDENTIAL;
5880
0
        extGStateDict = getConfidentialStampExtGStateDict(doc);
5881
0
    } else if (icon == "Final") {
5882
0
        stampUnscaledWidth = ANNOT_STAMP_FINAL_WIDTH;
5883
0
        stampUnscaledHeight = ANNOT_STAMP_FINAL_HEIGHT;
5884
0
        stampCode = ANNOT_STAMP_FINAL;
5885
0
        extGStateDict = getFinalStampExtGStateDict(doc);
5886
0
    } else if (icon == "Experimental") {
5887
0
        stampUnscaledWidth = ANNOT_STAMP_EXPERIMENTAL_WIDTH;
5888
0
        stampUnscaledHeight = ANNOT_STAMP_EXPERIMENTAL_HEIGHT;
5889
0
        stampCode = ANNOT_STAMP_EXPERIMENTAL;
5890
0
        extGStateDict = getExperimentalStampExtGStateDict(doc);
5891
0
    } else if (icon == "Expired") {
5892
0
        stampUnscaledWidth = ANNOT_STAMP_EXPIRED_WIDTH;
5893
0
        stampUnscaledHeight = ANNOT_STAMP_EXPIRED_HEIGHT;
5894
0
        stampCode = ANNOT_STAMP_EXPIRED;
5895
0
        extGStateDict = getExpiredStampExtGStateDict(doc);
5896
0
    } else if (icon == "NotApproved") {
5897
0
        stampUnscaledWidth = ANNOT_STAMP_NOT_APPROVED_WIDTH;
5898
0
        stampUnscaledHeight = ANNOT_STAMP_NOT_APPROVED_HEIGHT;
5899
0
        stampCode = ANNOT_STAMP_NOT_APPROVED;
5900
0
        extGStateDict = getNotApprovedStampExtGStateDict(doc);
5901
0
    } else if (icon == "NotForPublicRelease") {
5902
0
        stampUnscaledWidth = ANNOT_STAMP_NOT_FOR_PUBLIC_RELEASE_WIDTH;
5903
0
        stampUnscaledHeight = ANNOT_STAMP_NOT_FOR_PUBLIC_RELEASE_HEIGHT;
5904
0
        stampCode = ANNOT_STAMP_NOT_FOR_PUBLIC_RELEASE;
5905
0
        extGStateDict = getNotForPublicReleaseStampExtGStateDict(doc);
5906
0
    } else if (icon == "Sold") {
5907
0
        stampUnscaledWidth = ANNOT_STAMP_SOLD_WIDTH;
5908
0
        stampUnscaledHeight = ANNOT_STAMP_SOLD_HEIGHT;
5909
0
        stampCode = ANNOT_STAMP_SOLD;
5910
0
        extGStateDict = getSoldStampExtGStateDict(doc);
5911
0
    } else if (icon == "Departmental") {
5912
0
        stampUnscaledWidth = ANNOT_STAMP_DEPARTMENTAL_WIDTH;
5913
0
        stampUnscaledHeight = ANNOT_STAMP_DEPARTMENTAL_HEIGHT;
5914
0
        stampCode = ANNOT_STAMP_DEPARTMENTAL;
5915
0
        extGStateDict = getDepartmentalStampExtGStateDict(doc);
5916
0
    } else if (icon == "ForComment") {
5917
0
        stampUnscaledWidth = ANNOT_STAMP_FOR_COMMENT_WIDTH;
5918
0
        stampUnscaledHeight = ANNOT_STAMP_FOR_COMMENT_HEIGHT;
5919
0
        stampCode = ANNOT_STAMP_FOR_COMMENT;
5920
0
        extGStateDict = getForCommentStampExtGStateDict(doc);
5921
0
    } else if (icon == "ForPublicRelease") {
5922
0
        stampUnscaledWidth = ANNOT_STAMP_FOR_PUBLIC_RELEASE_WIDTH;
5923
0
        stampUnscaledHeight = ANNOT_STAMP_FOR_PUBLIC_RELEASE_HEIGHT;
5924
0
        stampCode = ANNOT_STAMP_FOR_PUBLIC_RELEASE;
5925
0
        extGStateDict = getForPublicReleaseStampExtGStateDict(doc);
5926
0
    } else if (icon == "TopSecret") {
5927
0
        stampUnscaledWidth = ANNOT_STAMP_TOP_SECRET_WIDTH;
5928
0
        stampUnscaledHeight = ANNOT_STAMP_TOP_SECRET_HEIGHT;
5929
0
        stampCode = ANNOT_STAMP_TOP_SECRET;
5930
0
        extGStateDict = getTopSecretStampExtGStateDict(doc);
5931
0
    } else {
5932
0
        stampUnscaledWidth = ANNOT_STAMP_DRAFT_WIDTH;
5933
0
        stampUnscaledHeight = ANNOT_STAMP_DRAFT_HEIGHT;
5934
0
        stampCode = ANNOT_STAMP_DRAFT;
5935
0
        extGStateDict = getDraftStampExtGStateDict(doc);
5936
0
    }
5937
5938
0
    const std::array<double, 4> bboxArray = { 0, 0, rect->x2 - rect->x1, rect->y2 - rect->y1 };
5939
0
    const std::string scale = GooString::format("{0:.6g} 0 0 {1:.6g} 0 0 cm\nq\n", bboxArray[2] / stampUnscaledWidth, bboxArray[3] / stampUnscaledHeight);
5940
0
    defaultAppearanceBuilder.append(scale.c_str());
5941
0
    defaultAppearanceBuilder.append(stampCode);
5942
0
    defaultAppearanceBuilder.append("Q\n");
5943
5944
0
    auto resDict = std::make_unique<Dict>(doc->getXRef());
5945
0
    resDict->add("ExtGState", Object(std::move(extGStateDict)));
5946
5947
0
    Object aStream = createForm(defaultAppearanceBuilder.buffer(), bboxArray, true, std::move(resDict));
5948
5949
0
    AnnotAppearanceBuilder appearanceBuilder;
5950
0
    appearanceBuilder.append("/GS0 gs\n/Fm0 Do");
5951
0
    resDict = createResourcesDict("Fm0", std::move(aStream), "GS0", opacity, nullptr);
5952
0
    appearance = createForm(appearanceBuilder.buffer(), bboxArray, false, std::move(resDict));
5953
0
}
5954
5955
void AnnotStamp::draw(Gfx *gfx, bool printing)
5956
0
{
5957
0
    if (!isVisible(printing)) {
5958
0
        return;
5959
0
    }
5960
5961
0
    annotLocker();
5962
5963
    // generate the appearance stream
5964
0
    updateAppearanceResDict();
5965
5966
    // draw the appearance stream
5967
0
    Object obj = appearance.fetch(gfx->getXRef());
5968
0
    if (appearBBox) {
5969
0
        gfx->drawAnnot(&obj, nullptr, color.get(), appearBBox->getPageXMin(), appearBBox->getPageYMin(), appearBBox->getPageXMax(), appearBBox->getPageYMax(), getRotation());
5970
0
    } else {
5971
0
        gfx->drawAnnot(&obj, nullptr, color.get(), rect->x1, rect->y1, rect->x2, rect->y2, getRotation());
5972
0
    }
5973
0
}
5974
5975
void AnnotStamp::setIcon(const std::string &new_icon)
5976
0
{
5977
0
    icon = new_icon;
5978
5979
0
    update("Name", Object::name(icon));
5980
0
    invalidateAppearance();
5981
0
}
5982
5983
void AnnotStamp::setCustomImage(std::unique_ptr<AnnotStampImageHelper> &&stampImageHelperA)
5984
0
{
5985
0
    if (!stampImageHelperA) {
5986
0
        return;
5987
0
    }
5988
5989
0
    annotLocker();
5990
0
    if (stampImageHelper) {
5991
0
        stampImageHelper->removeAnnotStampImageObject();
5992
0
    }
5993
5994
0
    stampImageHelper = std::move(stampImageHelperA);
5995
5996
    // Regenerate appearance stream
5997
0
    invalidateAppearance();
5998
0
    updateAppearanceResDict();
5999
0
}
6000
6001
//------------------------------------------------------------------------
6002
// AnnotGeometry
6003
//------------------------------------------------------------------------
6004
0
AnnotGeometry::AnnotGeometry(PDFDoc *docA, const PDFRectangle &rectA, AnnotSubtype subType) : AnnotMarkup(docA, rectA)
6005
0
{
6006
0
    switch (subType) {
6007
0
    case typeSquare:
6008
0
        annotObj.dictSet("Subtype", Object::name("Square"));
6009
0
        break;
6010
0
    case typeCircle:
6011
0
        annotObj.dictSet("Subtype", Object::name("Circle"));
6012
0
        break;
6013
0
    default:
6014
0
        assert(0 && "Invalid subtype for AnnotGeometry\n");
6015
0
    }
6016
6017
0
    initialize(annotObj.getDict());
6018
0
}
6019
6020
0
AnnotGeometry::AnnotGeometry(PDFDoc *docA, Object &&dictObject, const Object &obj) : AnnotMarkup(docA, std::move(dictObject), obj)
6021
0
{
6022
    // the real type will be read in initialize()
6023
0
    type = typeSquare;
6024
0
    initialize(annotObj.getDict());
6025
0
}
6026
6027
0
AnnotGeometry::~AnnotGeometry() = default;
6028
6029
void AnnotGeometry::initialize(Dict *dict)
6030
0
{
6031
0
    Object obj1;
6032
6033
0
    obj1 = dict->lookup("Subtype");
6034
0
    if (obj1.isName()) {
6035
0
        if (obj1.isName("Square")) {
6036
0
            type = typeSquare;
6037
0
        } else if (obj1.isName("Circle")) {
6038
0
            type = typeCircle;
6039
0
        }
6040
0
    }
6041
6042
0
    obj1 = dict->lookup("IC");
6043
0
    if (obj1.isArray()) {
6044
0
        interiorColor = std::make_unique<AnnotColor>(*obj1.getArray());
6045
0
    }
6046
6047
0
    obj1 = dict->lookup("BS");
6048
0
    if (obj1.isDict()) {
6049
0
        border = std::make_unique<AnnotBorderBS>(obj1.getDict());
6050
0
    } else if (!border) {
6051
0
        border = std::make_unique<AnnotBorderBS>();
6052
0
    }
6053
6054
0
    obj1 = dict->lookup("BE");
6055
0
    if (obj1.isDict()) {
6056
0
        borderEffect = std::make_unique<AnnotBorderEffect>(obj1.getDict());
6057
0
    }
6058
6059
0
    obj1 = dict->lookup("RD");
6060
0
    if (obj1.isArray()) {
6061
0
        geometryRect = parseDiffRectangle(obj1.getArray(), rect.get());
6062
0
    }
6063
0
}
6064
6065
void AnnotGeometry::setType(AnnotSubtype new_type)
6066
0
{
6067
0
    const char *typeName = nullptr; /* squelch bogus compiler warning */
6068
6069
0
    switch (new_type) {
6070
0
    case typeSquare:
6071
0
        typeName = "Square";
6072
0
        break;
6073
0
    case typeCircle:
6074
0
        typeName = "Circle";
6075
0
        break;
6076
0
    default:
6077
0
        assert(!"Invalid subtype");
6078
0
    }
6079
6080
0
    type = new_type;
6081
0
    update("Subtype", Object::name(typeName));
6082
0
    invalidateAppearance();
6083
0
}
6084
6085
void AnnotGeometry::setInteriorColor(std::unique_ptr<AnnotColor> &&new_color)
6086
0
{
6087
0
    if (new_color) {
6088
0
        Object obj1 = new_color->writeToObject(doc->getXRef());
6089
0
        update("IC", std::move(obj1));
6090
0
        interiorColor = std::move(new_color);
6091
0
    } else {
6092
0
        interiorColor = nullptr;
6093
0
        update("IC", Object::null());
6094
0
    }
6095
0
    invalidateAppearance();
6096
0
}
6097
6098
void AnnotGeometry::draw(Gfx *gfx, bool printing)
6099
0
{
6100
0
    double ca = 1;
6101
6102
0
    if (!isVisible(printing)) {
6103
0
        return;
6104
0
    }
6105
6106
0
    annotLocker();
6107
0
    if (appearance.isNull()) {
6108
0
        const bool fill = interiorColor && interiorColor->getSpace() != AnnotColor::colorTransparent;
6109
0
        ca = opacity;
6110
6111
0
        AnnotAppearanceBuilder appearBuilder;
6112
0
        appearBuilder.append("q\n");
6113
0
        if (color) {
6114
0
            appearBuilder.setDrawColor(*color, false);
6115
0
        }
6116
6117
0
        double borderWidth = border->getWidth();
6118
0
        appearBuilder.setLineStyleForBorder(*border);
6119
6120
0
        if (interiorColor) {
6121
0
            appearBuilder.setDrawColor(*interiorColor, true);
6122
0
        }
6123
6124
0
        if (type == typeSquare) {
6125
0
            appearBuilder.appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} re\n", borderWidth / 2.0, borderWidth / 2.0, (rect->x2 - rect->x1) - borderWidth, (rect->y2 - rect->y1) - borderWidth);
6126
0
            if (fill) {
6127
0
                if (borderWidth > 0) {
6128
0
                    appearBuilder.append("b\n");
6129
0
                } else {
6130
0
                    appearBuilder.append("f\n");
6131
0
                }
6132
0
            } else if (borderWidth > 0) {
6133
0
                appearBuilder.append("S\n");
6134
0
            }
6135
0
        } else {
6136
0
            const double rx { (rect->x2 - rect->x1) / 2. };
6137
0
            const double ry { (rect->y2 - rect->y1) / 2. };
6138
0
            const double bwHalf { borderWidth / 2.0 };
6139
0
            appearBuilder.drawEllipse(rx, ry, rx - bwHalf, ry - bwHalf, fill, borderWidth > 0);
6140
0
        }
6141
0
        appearBuilder.append("Q\n");
6142
6143
0
        const std::array<double, 4> bbox = { 0, 0, rect->x2 - rect->x1, rect->y2 - rect->y1 };
6144
0
        if (ca == 1) {
6145
0
            appearance = createForm(appearBuilder.buffer(), bbox, false, Object {});
6146
0
        } else {
6147
0
            Object aStream = createForm(appearBuilder.buffer(), bbox, true, Object {});
6148
6149
0
            std::string appearBuf("/GS0 gs\n/Fm0 Do");
6150
0
            std::unique_ptr<Dict> resDict = createResourcesDict("Fm0", std::move(aStream), "GS0", ca, nullptr);
6151
0
            appearance = createForm(appearBuf, bbox, false, std::move(resDict));
6152
0
        }
6153
0
    }
6154
6155
    // draw the appearance stream
6156
0
    Object obj = appearance.fetch(gfx->getXRef());
6157
0
    gfx->drawAnnot(&obj, nullptr, color.get(), rect->x1, rect->y1, rect->x2, rect->y2, getRotation());
6158
0
}
6159
6160
//------------------------------------------------------------------------
6161
// AnnotPolygon
6162
//------------------------------------------------------------------------
6163
0
AnnotPolygon::AnnotPolygon(PDFDoc *docA, const PDFRectangle &rectA, AnnotSubtype subType) : AnnotMarkup(docA, rectA)
6164
0
{
6165
0
    switch (subType) {
6166
0
    case typePolygon:
6167
0
        annotObj.dictSet("Subtype", Object::name("Polygon"));
6168
0
        break;
6169
0
    case typePolyLine:
6170
0
        annotObj.dictSet("Subtype", Object::name("PolyLine"));
6171
0
        break;
6172
0
    default:
6173
0
        assert(0 && "Invalid subtype for AnnotGeometry\n");
6174
0
    }
6175
6176
    // Store dummy path with one null vertex only
6177
0
    auto a = std::make_unique<Array>(doc->getXRef());
6178
0
    a->add(Object(0.));
6179
0
    a->add(Object(0.));
6180
0
    annotObj.dictSet("Vertices", Object(std::move(a)));
6181
6182
0
    initialize(annotObj.getDict());
6183
0
}
6184
6185
0
AnnotPolygon::AnnotPolygon(PDFDoc *docA, Object &&dictObject, const Object &obj) : AnnotMarkup(docA, std::move(dictObject), obj)
6186
0
{
6187
    // the real type will be read in initialize()
6188
0
    type = typePolygon;
6189
0
    initialize(annotObj.getDict());
6190
0
}
6191
6192
0
AnnotPolygon::~AnnotPolygon() = default;
6193
6194
void AnnotPolygon::initialize(Dict *dict)
6195
0
{
6196
0
    Object obj1;
6197
6198
0
    obj1 = dict->lookup("Subtype");
6199
0
    if (obj1.isName()) {
6200
0
        if (obj1.isName("Polygon")) {
6201
0
            type = typePolygon;
6202
0
        } else if (obj1.isName("PolyLine")) {
6203
0
            type = typePolyLine;
6204
0
        }
6205
0
    }
6206
6207
0
    obj1 = dict->lookup("Vertices");
6208
0
    if (obj1.isArray()) {
6209
0
        vertices = std::make_unique<AnnotPath>(*obj1.getArray());
6210
0
    } else {
6211
0
        vertices = std::make_unique<AnnotPath>();
6212
0
        error(errSyntaxError, -1, "Bad Annot Polygon Vertices");
6213
0
        ok = false;
6214
0
    }
6215
6216
0
    obj1 = dict->lookup("LE");
6217
0
    if (obj1.isArrayOfLength(2)) {
6218
0
        Object obj2 = obj1.arrayGet(0);
6219
0
        if (obj2.isName()) {
6220
0
            startStyle = parseAnnotLineEndingStyle(obj2);
6221
0
        } else {
6222
0
            startStyle = annotLineEndingNone;
6223
0
        }
6224
0
        obj2 = obj1.arrayGet(1);
6225
0
        if (obj2.isName()) {
6226
0
            endStyle = parseAnnotLineEndingStyle(obj2);
6227
0
        } else {
6228
0
            endStyle = annotLineEndingNone;
6229
0
        }
6230
0
    } else {
6231
0
        startStyle = endStyle = annotLineEndingNone;
6232
0
    }
6233
6234
0
    obj1 = dict->lookup("IC");
6235
0
    if (obj1.isArray()) {
6236
0
        interiorColor = std::make_unique<AnnotColor>(*obj1.getArray());
6237
0
    }
6238
6239
0
    obj1 = dict->lookup("BS");
6240
0
    if (obj1.isDict()) {
6241
0
        border = std::make_unique<AnnotBorderBS>(obj1.getDict());
6242
0
    } else if (!border) {
6243
0
        border = std::make_unique<AnnotBorderBS>();
6244
0
    }
6245
6246
0
    obj1 = dict->lookup("BE");
6247
0
    if (obj1.isDict()) {
6248
0
        borderEffect = std::make_unique<AnnotBorderEffect>(obj1.getDict());
6249
0
    }
6250
6251
0
    obj1 = dict->lookup("IT");
6252
0
    if (obj1.isName()) {
6253
0
        const std::string &intentName = obj1.getNameString();
6254
6255
0
        if (intentName == "PolygonCloud") {
6256
0
            intent = polygonCloud;
6257
0
        } else if (intentName == "PolyLineDimension") {
6258
0
            intent = polylineDimension;
6259
0
        } else {
6260
0
            intent = polygonDimension;
6261
0
        }
6262
0
    } else {
6263
0
        intent = polygonCloud;
6264
0
    }
6265
0
}
6266
6267
void AnnotPolygon::setType(AnnotSubtype new_type)
6268
0
{
6269
0
    const char *typeName = nullptr; /* squelch bogus compiler warning */
6270
6271
0
    switch (new_type) {
6272
0
    case typePolygon:
6273
0
        typeName = "Polygon";
6274
0
        break;
6275
0
    case typePolyLine:
6276
0
        typeName = "PolyLine";
6277
0
        break;
6278
0
    default:
6279
0
        assert(!"Invalid subtype");
6280
0
    }
6281
6282
0
    type = new_type;
6283
0
    update("Subtype", Object::name(typeName));
6284
0
    invalidateAppearance();
6285
0
}
6286
6287
void AnnotPolygon::setVertices(const AnnotPath &path)
6288
0
{
6289
0
    auto a = std::make_unique<Array>(doc->getXRef());
6290
0
    for (int i = 0; i < path.getCoordsLength(); i++) {
6291
0
        a->add(Object(path.getX(i)));
6292
0
        a->add(Object(path.getY(i)));
6293
0
    }
6294
6295
0
    vertices = std::make_unique<AnnotPath>(*a);
6296
6297
0
    update("Vertices", Object(std::move(a)));
6298
0
    invalidateAppearance();
6299
0
}
6300
6301
void AnnotPolygon::setStartEndStyle(AnnotLineEndingStyle start, AnnotLineEndingStyle end)
6302
0
{
6303
0
    startStyle = start;
6304
0
    endStyle = end;
6305
6306
0
    auto a = std::make_unique<Array>(doc->getXRef());
6307
0
    a->add(Object::name(convertAnnotLineEndingStyle(startStyle)));
6308
0
    a->add(Object::name(convertAnnotLineEndingStyle(endStyle)));
6309
6310
0
    update("LE", Object(std::move(a)));
6311
0
    invalidateAppearance();
6312
0
}
6313
6314
void AnnotPolygon::setInteriorColor(std::unique_ptr<AnnotColor> &&new_color)
6315
0
{
6316
0
    if (new_color) {
6317
0
        Object obj1 = new_color->writeToObject(doc->getXRef());
6318
0
        update("IC", std::move(obj1));
6319
0
        interiorColor = std::move(new_color);
6320
0
    } else {
6321
0
        interiorColor = nullptr;
6322
0
        update("IC", Object::null());
6323
0
    }
6324
0
    invalidateAppearance();
6325
0
}
6326
6327
void AnnotPolygon::setIntent(AnnotPolygonIntent new_intent)
6328
0
{
6329
0
    const char *intentName;
6330
6331
0
    intent = new_intent;
6332
0
    if (new_intent == polygonCloud) {
6333
0
        intentName = "PolygonCloud";
6334
0
    } else if (new_intent == polylineDimension) {
6335
0
        intentName = "PolyLineDimension";
6336
0
    } else { // polygonDimension
6337
0
        intentName = "PolygonDimension";
6338
0
    }
6339
0
    update("IT", Object::name(intentName));
6340
0
}
6341
6342
void AnnotPolygon::generatePolyLineAppearance(AnnotAppearanceBuilder *appearBuilder)
6343
0
{
6344
0
    const bool fill = interiorColor != nullptr;
6345
0
    const double x1 = vertices->getX(0);
6346
0
    const double y1 = vertices->getY(0);
6347
0
    const double x2 = vertices->getX(1);
6348
0
    const double y2 = vertices->getY(1);
6349
0
    const double x3 = vertices->getX(vertices->getCoordsLength() - 2);
6350
0
    const double y3 = vertices->getY(vertices->getCoordsLength() - 2);
6351
0
    const double x4 = vertices->getX(vertices->getCoordsLength() - 1);
6352
0
    const double y4 = vertices->getY(vertices->getCoordsLength() - 1);
6353
6354
0
    const double len_1 = sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
6355
    // length of last segment
6356
0
    const double len_2 = sqrt((x4 - x3) * (x4 - x3) + (y4 - y3) * (y4 - y3));
6357
6358
    // segments become positive x direction, coord1 becomes (0,0).
6359
0
    Matrix matr1, matr2;
6360
0
    const double angle1 = atan2(y2 - y1, x2 - x1);
6361
0
    const double angle2 = atan2(y4 - y3, x4 - x3);
6362
6363
0
    matr1.m[0] = matr1.m[3] = cos(angle1);
6364
0
    matr1.m[1] = sin(angle1);
6365
0
    matr1.m[2] = -matr1.m[1];
6366
0
    matr1.m[4] = x1 - rect->x1;
6367
0
    matr1.m[5] = y1 - rect->y1;
6368
6369
0
    matr2.m[0] = matr2.m[3] = cos(angle2);
6370
0
    matr2.m[1] = sin(angle2);
6371
0
    matr2.m[2] = -matr2.m[1];
6372
0
    matr2.m[4] = x3 - rect->x1;
6373
0
    matr2.m[5] = y3 - rect->y1;
6374
6375
0
    const double lineEndingSize1 { std::min(6. * border->getWidth(), len_1 / 2) };
6376
0
    const double lineEndingSize2 { std::min(6. * border->getWidth(), len_2 / 2) };
6377
6378
0
    if (vertices->getCoordsLength() != 0) {
6379
0
        double tx, ty;
6380
0
        matr1.transform(AnnotAppearanceBuilder::lineEndingXShorten(startStyle, lineEndingSize1), 0, &tx, &ty);
6381
0
        appearBuilder->appendf("{0:.2f} {1:.2f} m\n", tx, ty);
6382
0
        appearBBox->extendTo(tx, ty);
6383
6384
0
        for (int i = 1; i < vertices->getCoordsLength() - 1; ++i) {
6385
0
            appearBuilder->appendf("{0:.2f} {1:.2f} l\n", vertices->getX(i) - rect->x1, vertices->getY(i) - rect->y1);
6386
0
            appearBBox->extendTo(vertices->getX(i) - rect->x1, vertices->getY(i) - rect->y1);
6387
0
        }
6388
6389
0
        if (vertices->getCoordsLength() > 1) {
6390
0
            matr2.transform(len_2 - AnnotAppearanceBuilder::lineEndingXShorten(endStyle, lineEndingSize2), 0, &tx, &ty);
6391
0
            appearBuilder->appendf("{0:.2f} {1:.2f} l S\n", tx, ty);
6392
0
            appearBBox->extendTo(tx, ty);
6393
0
        }
6394
0
    }
6395
6396
0
    if (startStyle != annotLineEndingNone) {
6397
0
        const double extendX { -AnnotAppearanceBuilder::lineEndingXExtendBBox(startStyle, lineEndingSize1) };
6398
0
        double tx, ty;
6399
0
        appearBuilder->drawLineEnding(startStyle, 0, 0, -lineEndingSize1, fill, matr1);
6400
0
        matr1.transform(extendX, lineEndingSize1 / 2., &tx, &ty);
6401
0
        appearBBox->extendTo(tx, ty);
6402
0
        matr1.transform(extendX, -lineEndingSize1 / 2., &tx, &ty);
6403
0
        appearBBox->extendTo(tx, ty);
6404
0
    }
6405
6406
0
    if (endStyle != annotLineEndingNone) {
6407
0
        const double extendX { AnnotAppearanceBuilder::lineEndingXExtendBBox(endStyle, lineEndingSize2) };
6408
0
        double tx, ty;
6409
0
        appearBuilder->drawLineEnding(endStyle, len_2, 0, lineEndingSize2, fill, matr2);
6410
0
        matr2.transform(len_2 + extendX, lineEndingSize2 / 2., &tx, &ty);
6411
0
        appearBBox->extendTo(tx, ty);
6412
0
        matr2.transform(len_2 + extendX, -lineEndingSize2 / 2., &tx, &ty);
6413
0
        appearBBox->extendTo(tx, ty);
6414
0
    }
6415
0
}
6416
6417
void AnnotPolygon::draw(Gfx *gfx, bool printing)
6418
0
{
6419
0
    double ca = 1;
6420
6421
0
    if (!isVisible(printing)) {
6422
0
        return;
6423
0
    }
6424
6425
0
    annotLocker();
6426
0
    if (appearance.isNull()) {
6427
0
        appearBBox = std::make_unique<AnnotAppearanceBBox>(*rect);
6428
0
        ca = opacity;
6429
6430
0
        AnnotAppearanceBuilder appearBuilder;
6431
0
        appearBuilder.append("q\n");
6432
6433
0
        if (color) {
6434
0
            appearBuilder.setDrawColor(*color, false);
6435
0
        }
6436
6437
0
        appearBuilder.setLineStyleForBorder(*border);
6438
0
        appearBBox->setBorderWidth(std::max(1., border->getWidth()));
6439
6440
0
        if (interiorColor) {
6441
0
            appearBuilder.setDrawColor(*interiorColor, true);
6442
0
        }
6443
6444
0
        if (type == typePolyLine) {
6445
0
            generatePolyLineAppearance(&appearBuilder);
6446
0
        } else {
6447
0
            if (vertices->getCoordsLength() != 0) {
6448
0
                appearBuilder.appendf("{0:.2f} {1:.2f} m\n", vertices->getX(0) - rect->x1, vertices->getY(0) - rect->y1);
6449
0
                appearBBox->extendTo(vertices->getX(0) - rect->x1, vertices->getY(0) - rect->y1);
6450
6451
0
                for (int i = 1; i < vertices->getCoordsLength(); ++i) {
6452
0
                    appearBuilder.appendf("{0:.2f} {1:.2f} l\n", vertices->getX(i) - rect->x1, vertices->getY(i) - rect->y1);
6453
0
                    appearBBox->extendTo(vertices->getX(i) - rect->x1, vertices->getY(i) - rect->y1);
6454
0
                }
6455
6456
0
                const double borderWidth = border->getWidth();
6457
0
                if (interiorColor && interiorColor->getSpace() != AnnotColor::colorTransparent) {
6458
0
                    if (borderWidth > 0) {
6459
0
                        appearBuilder.append("b\n");
6460
0
                    } else {
6461
0
                        appearBuilder.append("f\n");
6462
0
                    }
6463
0
                } else if (borderWidth > 0) {
6464
0
                    appearBuilder.append("s\n");
6465
0
                }
6466
0
            }
6467
0
        }
6468
0
        appearBuilder.append("Q\n");
6469
6470
0
        const std::array<double, 4> bbox = appearBBox->getBBoxRect();
6471
0
        if (ca == 1) {
6472
0
            appearance = createForm(appearBuilder.buffer(), bbox, false, Object {});
6473
0
        } else {
6474
0
            Object aStream = createForm(appearBuilder.buffer(), bbox, true, Object {});
6475
6476
0
            std::string appearBuf("/GS0 gs\n/Fm0 Do");
6477
0
            std::unique_ptr<Dict> resDict = createResourcesDict("Fm0", std::move(aStream), "GS0", ca, nullptr);
6478
0
            appearance = createForm(appearBuf, bbox, false, std::move(resDict));
6479
0
        }
6480
0
    }
6481
6482
    // draw the appearance stream
6483
0
    Object obj = appearance.fetch(gfx->getXRef());
6484
0
    if (appearBBox) {
6485
0
        gfx->drawAnnot(&obj, nullptr, color.get(), appearBBox->getPageXMin(), appearBBox->getPageYMin(), appearBBox->getPageXMax(), appearBBox->getPageYMax(), getRotation());
6486
0
    } else {
6487
0
        gfx->drawAnnot(&obj, nullptr, color.get(), rect->x1, rect->y1, rect->x2, rect->y2, getRotation());
6488
0
    }
6489
0
}
6490
6491
//------------------------------------------------------------------------
6492
// AnnotCaret
6493
//------------------------------------------------------------------------
6494
0
AnnotCaret::AnnotCaret(PDFDoc *docA, const PDFRectangle &rectA) : AnnotMarkup(docA, rectA)
6495
0
{
6496
0
    type = typeCaret;
6497
6498
0
    annotObj.dictSet("Subtype", Object::name("Caret"));
6499
0
    initialize(annotObj.getDict());
6500
0
}
6501
6502
0
AnnotCaret::AnnotCaret(PDFDoc *docA, Object &&dictObject, const Object &obj) : AnnotMarkup(docA, std::move(dictObject), obj)
6503
0
{
6504
0
    type = typeCaret;
6505
0
    initialize(annotObj.getDict());
6506
0
}
6507
6508
0
AnnotCaret::~AnnotCaret() = default;
6509
6510
void AnnotCaret::initialize(Dict *dict)
6511
0
{
6512
0
    Object obj1;
6513
6514
0
    symbol = symbolNone;
6515
0
    obj1 = dict->lookup("Sy");
6516
0
    if (obj1.isName()) {
6517
0
        if (obj1.isName("P")) {
6518
0
            symbol = symbolP;
6519
0
        } else if (obj1.isName("None")) {
6520
0
            symbol = symbolNone;
6521
0
        }
6522
0
    }
6523
6524
0
    obj1 = dict->lookup("RD");
6525
0
    if (obj1.isArray()) {
6526
0
        caretRect = parseDiffRectangle(obj1.getArray(), rect.get());
6527
0
    }
6528
0
}
6529
6530
void AnnotCaret::setSymbol(AnnotCaretSymbol new_symbol)
6531
0
{
6532
0
    symbol = new_symbol;
6533
0
    update("Sy", Object::name(new_symbol == symbolP ? "P" : "None"));
6534
0
    invalidateAppearance();
6535
0
}
6536
6537
//------------------------------------------------------------------------
6538
// AnnotInk
6539
//------------------------------------------------------------------------
6540
0
AnnotInk::AnnotInk(PDFDoc *docA, const PDFRectangle &rectA) : AnnotMarkup(docA, rectA)
6541
0
{
6542
0
    type = typeInk;
6543
6544
0
    annotObj.dictSet("Subtype", Object::name("Ink"));
6545
6546
    // Store dummy path with one null vertex only
6547
0
    auto inkListArray = std::make_unique<Array>(doc->getXRef());
6548
0
    auto vList = std::make_unique<Array>(doc->getXRef());
6549
0
    vList->add(Object(0.));
6550
0
    vList->add(Object(0.));
6551
0
    inkListArray->add(Object(std::move(vList)));
6552
0
    annotObj.dictSet("InkList", Object(std::move(inkListArray)));
6553
6554
0
    drawBelow = false;
6555
6556
0
    initialize(annotObj.getDict());
6557
0
}
6558
6559
0
AnnotInk::AnnotInk(PDFDoc *docA, Object &&dictObject, const Object &obj) : AnnotMarkup(docA, std::move(dictObject), obj)
6560
0
{
6561
0
    type = typeInk;
6562
0
    initialize(annotObj.getDict());
6563
0
}
6564
6565
0
AnnotInk::~AnnotInk() = default;
6566
6567
void AnnotInk::initialize(Dict *dict)
6568
0
{
6569
0
    Object obj1;
6570
6571
0
    obj1 = dict->lookup("InkList");
6572
0
    if (obj1.isArray()) {
6573
0
        parseInkList(*obj1.getArray());
6574
0
    } else {
6575
0
        error(errSyntaxError, -1, "Bad Annot Ink List");
6576
6577
0
        obj1 = dict->lookup("AP");
6578
        // Although InkList is required, it should be ignored
6579
        // when there is an AP entry in the Annot, so do not fail
6580
        // when that happens
6581
0
        if (!obj1.isDict()) {
6582
0
            ok = false;
6583
0
        }
6584
0
    }
6585
6586
0
    drawBelow = false;
6587
6588
0
    obj1 = getAppearanceResDict();
6589
0
    if (obj1.isDict()) {
6590
0
        const Object extGState = obj1.dictLookup("ExtGState");
6591
0
        if (extGState.isDict()) {
6592
0
            const Dict *extGStateDict = extGState.getDict();
6593
0
            if (extGStateDict->getLength() > 0) {
6594
0
                obj1 = extGStateDict->getVal(0);
6595
0
                if (obj1.isDict() && obj1.dictLookup("BM").isName("Multiply")) {
6596
0
                    drawBelow = true;
6597
0
                }
6598
0
            }
6599
0
        }
6600
0
    }
6601
6602
0
    obj1 = dict->lookup("BS");
6603
0
    if (obj1.isDict()) {
6604
0
        border = std::make_unique<AnnotBorderBS>(obj1.getDict());
6605
0
    } else if (!border) {
6606
0
        border = std::make_unique<AnnotBorderBS>();
6607
0
    }
6608
0
}
6609
6610
void AnnotInk::writeInkList(const std::vector<std::unique_ptr<AnnotPath>> &paths, Array *dest_array)
6611
0
{
6612
0
    for (const auto &path : paths) {
6613
0
        auto a = std::make_unique<Array>(doc->getXRef());
6614
0
        for (int j = 0; j < path->getCoordsLength(); ++j) {
6615
0
            a->add(Object(path->getX(j)));
6616
0
            a->add(Object(path->getY(j)));
6617
0
        }
6618
0
        dest_array->add(Object(std::move(a)));
6619
0
    }
6620
0
}
6621
6622
void AnnotInk::parseInkList(const Array &array)
6623
0
{
6624
0
    int inkListLength = array.getLength();
6625
0
    inkList.clear();
6626
0
    inkList.reserve(inkListLength);
6627
0
    for (int i = 0; i < inkListLength; i++) {
6628
0
        Object obj2 = array.get(i);
6629
0
        if (obj2.isArray()) {
6630
0
            inkList.push_back(std::make_unique<AnnotPath>(*obj2.getArray()));
6631
0
        } else {
6632
0
            inkList.emplace_back();
6633
0
        }
6634
0
    }
6635
0
}
6636
6637
void AnnotInk::setInkList(const std::vector<std::unique_ptr<AnnotPath>> &paths)
6638
0
{
6639
0
    auto a = std::make_unique<Array>(doc->getXRef());
6640
0
    writeInkList(paths, a.get());
6641
6642
0
    parseInkList(*a);
6643
0
    annotObj.dictSet("InkList", Object(std::move(a)));
6644
0
    invalidateAppearance();
6645
0
    generateInkAppearance();
6646
0
}
6647
6648
void AnnotInk::setDrawBelow(bool drawBelow_)
6649
0
{
6650
0
    drawBelow = drawBelow_;
6651
0
    invalidateAppearance();
6652
0
    generateInkAppearance();
6653
0
}
6654
6655
bool AnnotInk::getDrawBelow() const
6656
0
{
6657
0
    return drawBelow;
6658
0
}
6659
6660
void AnnotInk::generateInkAppearance()
6661
0
{
6662
0
    Object newAppearance;
6663
6664
0
    appearBBox = std::make_unique<AnnotAppearanceBBox>(*rect);
6665
6666
0
    AnnotAppearanceBuilder appearBuilder;
6667
0
    if (opacity != 1 || drawBelow) {
6668
0
        appearBuilder.append("/GS0 gs\n");
6669
0
    }
6670
0
    appearBuilder.append("q\n");
6671
6672
0
    if (color) {
6673
0
        appearBuilder.setDrawColor(*color, false);
6674
0
    }
6675
6676
0
    if (border) {
6677
0
        appearBuilder.setLineStyleForBorder(*border);
6678
        /* It is easier to have the appearance bbox equal the annotation rect
6679
        (especially when the appearance stream is saved to the file), so we
6680
        don't add a border width to the bbox. This is not necessary as the
6681
        border is never drawn and applications are responsible for setting a
6682
        rect big enough in the first place.
6683
        However, this border width was set before the drawBelow property was
6684
        introduced and apps may rely on that behavior. Therefore, we set it in
6685
        the case that appearance streams are not saved (i.e. when drawBelow is
6686
        false) as it does not introduce bugs. */
6687
0
        if (!drawBelow) {
6688
0
            appearBBox->setBorderWidth(std::max(1.0, border->getWidth()));
6689
0
        }
6690
0
    }
6691
6692
0
    for (const auto &path : inkList) {
6693
0
        if (path && path->getCoordsLength() != 0) {
6694
0
            appearBuilder.appendf("{0:.2f} {1:.2f} m\n", path->getX(0) - rect->x1, path->getY(0) - rect->y1);
6695
0
            appearBBox->extendTo(path->getX(0) - rect->x1, path->getY(0) - rect->y1);
6696
6697
0
            for (int j = 1; j < path->getCoordsLength(); ++j) {
6698
0
                appearBuilder.appendf("{0:.2f} {1:.2f} l\n", path->getX(j) - rect->x1, path->getY(j) - rect->y1);
6699
0
                appearBBox->extendTo(path->getX(j) - rect->x1, path->getY(j) - rect->y1);
6700
0
            }
6701
6702
0
            appearBuilder.append("S\n");
6703
0
        }
6704
0
    }
6705
6706
0
    appearBuilder.append("Q\n");
6707
6708
0
    const std::array<double, 4> bbox = appearBBox->getBBoxRect();
6709
6710
0
    if (opacity == 1 && !drawBelow) {
6711
0
        newAppearance = createForm(appearBuilder.buffer(), bbox, false, Object {});
6712
0
    } else {
6713
0
        std::unique_ptr<Dict> resDict = createResourcesDict(nullptr, Object::null(), "GS0", opacity, drawBelow ? "Multiply" : nullptr);
6714
0
        newAppearance = createForm(appearBuilder.buffer(), bbox, false, std::move(resDict));
6715
0
    }
6716
6717
    /* If the annotation is drawn below (highlighting), we must save the
6718
    appearance stream so the multiply blend mode can be saved */
6719
0
    if (drawBelow) {
6720
0
        setNewAppearance(std::move(newAppearance));
6721
0
    } else {
6722
0
        appearance = std::move(newAppearance);
6723
0
    }
6724
0
}
6725
6726
void AnnotInk::draw(Gfx *gfx, bool printing)
6727
0
{
6728
0
    if (!isVisible(printing)) {
6729
0
        return;
6730
0
    }
6731
6732
0
    annotLocker();
6733
0
    if (appearance.isNull()) {
6734
0
        generateInkAppearance();
6735
0
    }
6736
6737
    // draw the appearance stream
6738
0
    Object obj = appearance.fetch(gfx->getXRef());
6739
0
    if (appearBBox) {
6740
0
        gfx->drawAnnot(&obj, nullptr, color.get(), appearBBox->getPageXMin(), appearBBox->getPageYMin(), appearBBox->getPageXMax(), appearBBox->getPageYMax(), getRotation());
6741
0
    } else {
6742
0
        gfx->drawAnnot(&obj, nullptr, color.get(), rect->x1, rect->y1, rect->x2, rect->y2, getRotation());
6743
0
    }
6744
0
}
6745
6746
//------------------------------------------------------------------------
6747
// AnnotFileAttachment
6748
//------------------------------------------------------------------------
6749
0
AnnotFileAttachment::AnnotFileAttachment(PDFDoc *docA, const PDFRectangle &rectA, const GooString &filename) : AnnotMarkup(docA, rectA)
6750
0
{
6751
0
    type = typeFileAttachment;
6752
6753
0
    annotObj.dictSet("Subtype", Object::name("FileAttachment"));
6754
0
    annotObj.dictSet("FS", Object(std::string { filename.toStr() }));
6755
6756
0
    initialize(annotObj.getDict());
6757
0
}
6758
6759
0
AnnotFileAttachment::AnnotFileAttachment(PDFDoc *docA, Object &&dictObject, const Object &obj) : AnnotMarkup(docA, std::move(dictObject), obj)
6760
0
{
6761
0
    type = typeFileAttachment;
6762
0
    initialize(annotObj.getDict());
6763
0
}
6764
6765
0
AnnotFileAttachment::~AnnotFileAttachment() = default;
6766
6767
void AnnotFileAttachment::initialize(Dict *dict)
6768
0
{
6769
0
    Object objFS = dict->lookup("FS");
6770
0
    if (objFS.isDict() || objFS.isString()) {
6771
0
        file = std::move(objFS);
6772
0
    } else {
6773
0
        error(errSyntaxError, -1, "Bad Annot File Attachment");
6774
0
        ok = false;
6775
0
    }
6776
6777
0
    Object objName = dict->lookup("Name");
6778
0
    if (objName.isName()) {
6779
0
        iconName = std::make_unique<GooString>(objName.getNameString());
6780
0
    } else {
6781
0
        iconName = std::make_unique<GooString>("PushPin");
6782
0
    }
6783
0
}
6784
6785
#define ANNOT_FILE_ATTACHMENT_AP_PUSHPIN                                                                                                                                                                                                       \
6786
0
    "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n"                                                                                                                                                                   \
6787
0
    "2.477 21.523 1 19.699 1 c 4.301 1 l 2.477 1 1 2.477 1 4.301 c 1 19.699\n"                                                                                                                                                                 \
6788
0
    "l 1 21.523 2.477 23 4.301 23 c h\n"                                                                                                                                                                                                       \
6789
0
    "4.301 23 m f\n"                                                                                                                                                                                                                           \
6790
0
    "0.533333 0.541176 0.521569 RG 2 w\n"                                                                                                                                                                                                      \
6791
0
    "1 J\n"                                                                                                                                                                                                                                    \
6792
0
    "1 j\n"                                                                                                                                                                                                                                    \
6793
0
    "[] 0.0 d\n"                                                                                                                                                                                                                               \
6794
0
    "4 M 5 4 m 6 5 l S\n"                                                                                                                                                                                                                      \
6795
0
    "2 w\n"                                                                                                                                                                                                                                    \
6796
0
    "11 14 m 9 12 l 6 12 l 13 5 l 13 8 l 15 10 l 18 11 l 20 11 l 12 19 l 12\n"                                                                                                                                                                 \
6797
0
    "17 l 11 14 l h\n"                                                                                                                                                                                                                         \
6798
0
    "11 14 m S\n"                                                                                                                                                                                                                              \
6799
0
    "3 w\n"                                                                                                                                                                                                                                    \
6800
0
    "6 5 m 9 8 l S\n"                                                                                                                                                                                                                          \
6801
0
    "0.729412 0.741176 0.713725 RG 2 w\n"                                                                                                                                                                                                      \
6802
0
    "5 5 m 6 6 l S\n"                                                                                                                                                                                                                          \
6803
0
    "2 w\n"                                                                                                                                                                                                                                    \
6804
0
    "11 15 m 9 13 l 6 13 l 13 6 l 13 9 l 15 11 l 18 12 l 20 12 l 12 20 l 12\n"                                                                                                                                                                 \
6805
0
    "18 l 11 15 l h\n"                                                                                                                                                                                                                         \
6806
0
    "11 15 m S\n"                                                                                                                                                                                                                              \
6807
0
    "3 w\n"                                                                                                                                                                                                                                    \
6808
0
    "6 6 m 9 9 l S\n"
6809
6810
#define ANNOT_FILE_ATTACHMENT_AP_PAPERCLIP                                                                                                                                                                                                     \
6811
0
    "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n"                                                                                                                                                                   \
6812
0
    "2.477 21.523 1 19.699 1 c 4.301 1 l 2.477 1 1 2.477 1 4.301 c 1 19.699\n"                                                                                                                                                                 \
6813
0
    "l 1 21.523 2.477 23 4.301 23 c h\n"                                                                                                                                                                                                       \
6814
0
    "4.301 23 m f\n"                                                                                                                                                                                                                           \
6815
0
    "0.533333 0.541176 0.521569 RG 2 w\n"                                                                                                                                                                                                      \
6816
0
    "1 J\n"                                                                                                                                                                                                                                    \
6817
0
    "1 j\n"                                                                                                                                                                                                                                    \
6818
0
    "[] 0.0 d\n"                                                                                                                                                                                                                               \
6819
0
    "4 M 16.645 12.035 m 12.418 7.707 l 10.902 6.559 6.402 11.203 8.09 12.562 c\n"                                                                                                                                                             \
6820
0
    "14.133 18.578 l 14.949 19.387 16.867 19.184 17.539 18.465 c 20.551\n"                                                                                                                                                                     \
6821
0
    "15.23 l 21.191 14.66 21.336 12.887 20.426 12.102 c 13.18 4.824 l 12.18\n"                                                                                                                                                                 \
6822
0
    "3.82 6.25 2.566 4.324 4.461 c 3 6.395 3.383 11.438 4.711 12.801 c 9.648\n"                                                                                                                                                                \
6823
0
    "17.887 l S\n"                                                                                                                                                                                                                             \
6824
0
    "0.729412 0.741176 0.713725 RG 16.645 13.035 m 12.418 8.707 l\n"                                                                                                                                                                           \
6825
0
    "10.902 7.559 6.402 12.203 8.09 13.562 c\n"                                                                                                                                                                                                \
6826
0
    "14.133 19.578 l 14.949 20.387 16.867 20.184 17.539 19.465 c 20.551\n"                                                                                                                                                                     \
6827
0
    "16.23 l 21.191 15.66 21.336 13.887 20.426 13.102 c 13.18 5.824 l 12.18\n"                                                                                                                                                                 \
6828
0
    "4.82 6.25 3.566 4.324 5.461 c 3 7.395 3.383 12.438 4.711 13.801 c 9.648\n"                                                                                                                                                                \
6829
0
    "18.887 l S\n"
6830
6831
#define ANNOT_FILE_ATTACHMENT_AP_GRAPH                                                                                                                                                                                                         \
6832
0
    "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n"                                                                                                                                                                   \
6833
0
    "2.477 21.523 1 19.699 1 c 4.301 1 l 2.477 1 1 2.477 1 4.301 c 1 19.699\n"                                                                                                                                                                 \
6834
0
    "l 1 21.523 2.477 23 4.301 23 c h\n"                                                                                                                                                                                                       \
6835
0
    "4.301 23 m f\n"                                                                                                                                                                                                                           \
6836
0
    "0.533333 0.541176 0.521569 RG 1 w\n"                                                                                                                                                                                                      \
6837
0
    "1 J\n"                                                                                                                                                                                                                                    \
6838
0
    "0 j\n"                                                                                                                                                                                                                                    \
6839
0
    "[] 0.0 d\n"                                                                                                                                                                                                                               \
6840
0
    "4 M 18.5 15.5 m 18.5 13.086 l 16.086 15.5 l 18.5 15.5 l h\n"                                                                                                                                                                              \
6841
0
    "18.5 15.5 m S\n"                                                                                                                                                                                                                          \
6842
0
    "7 7 m 10 11 l 13 9 l 18 15 l S\n"                                                                                                                                                                                                         \
6843
0
    "0.729412 0.741176 0.713725 RG 7 8 m 10 12 l 13 10 l 18 16 l S\n"                                                                                                                                                                          \
6844
0
    "18.5 16.5 m 18.5 14.086 l 16.086 16.5 l 18.5 16.5 l h\n"                                                                                                                                                                                  \
6845
0
    "18.5 16.5 m S\n"                                                                                                                                                                                                                          \
6846
0
    "0.533333 0.541176 0.521569 RG 2 w\n"                                                                                                                                                                                                      \
6847
0
    "1 j\n"                                                                                                                                                                                                                                    \
6848
0
    "3 19 m 3 3 l 21 3 l S\n"                                                                                                                                                                                                                  \
6849
0
    "0.729412 0.741176 0.713725 RG 3 20 m 3 4 l 21 4 l S\n"
6850
6851
#define ANNOT_FILE_ATTACHMENT_AP_TAG                                                                                                                                                                                                           \
6852
0
    "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n"                                                                                                                                                                   \
6853
0
    "2.477 21.523 1 19.699 1 c 4.301 1 l 2.477 1 1 2.477 1 4.301 c 1 19.699\n"                                                                                                                                                                 \
6854
0
    "l 1 21.523 2.477 23 4.301 23 c h\n"                                                                                                                                                                                                       \
6855
0
    "4.301 23 m f\n"                                                                                                                                                                                                                           \
6856
0
    "0.533333 0.541176 0.521569 RG 0.999781 w\n"                                                                                                                                                                                               \
6857
0
    "1 J\n"                                                                                                                                                                                                                                    \
6858
0
    "1 j\n"                                                                                                                                                                                                                                    \
6859
0
    "[] 0.0 d\n"                                                                                                                                                                                                                               \
6860
0
    "4 M q 1 0 0 -1 0 24 cm\n"                                                                                                                                                                                                                 \
6861
0
    "8.492 8.707 m 8.492 9.535 7.82 10.207 6.992 10.207 c 6.164 10.207 5.492\n"                                                                                                                                                                \
6862
0
    "9.535 5.492 8.707 c 5.492 7.879 6.164 7.207 6.992 7.207 c 7.82 7.207\n"                                                                                                                                                                   \
6863
0
    "8.492 7.879 8.492 8.707 c h\n"                                                                                                                                                                                                            \
6864
0
    "8.492 8.707 m S Q\n"                                                                                                                                                                                                                      \
6865
0
    "2 w\n"                                                                                                                                                                                                                                    \
6866
0
    "20.078 11.414 m 20.891 10.602 20.785 9.293 20.078 8.586 c 14.422 2.93 l\n"                                                                                                                                                                \
6867
0
    "13.715 2.223 12.301 2.223 11.594 2.93 c 3.816 10.707 l 3.109 11.414\n"                                                                                                                                                                    \
6868
0
    "2.402 17.781 3.816 19.195 c 5.23 20.609 11.594 19.902 12.301 19.195 c\n"                                                                                                                                                                  \
6869
0
    "20.078 11.414 l h\n"                                                                                                                                                                                                                      \
6870
0
    "20.078 11.414 m S\n"                                                                                                                                                                                                                      \
6871
0
    "0.729412 0.741176 0.713725 RG 20.078 12.414 m\n"                                                                                                                                                                                          \
6872
0
    "20.891 11.605 20.785 10.293 20.078 9.586 c 14.422 3.93 l\n"                                                                                                                                                                               \
6873
0
    "13.715 3.223 12.301 3.223 11.594 3.93 c 3.816 11.707 l 3.109 12.414\n"                                                                                                                                                                    \
6874
0
    "2.402 18.781 3.816 20.195 c 5.23 21.609 11.594 20.902 12.301 20.195 c\n"                                                                                                                                                                  \
6875
0
    "20.078 12.414 l h\n"                                                                                                                                                                                                                      \
6876
0
    "20.078 12.414 m S\n"                                                                                                                                                                                                                      \
6877
0
    "0.533333 0.541176 0.521569 RG 1 w\n"                                                                                                                                                                                                      \
6878
0
    "0 j\n"                                                                                                                                                                                                                                    \
6879
0
    "11.949 13.184 m 16.191 8.941 l S\n"                                                                                                                                                                                                       \
6880
0
    "0.729412 0.741176 0.713725 RG 11.949 14.184 m 16.191 9.941 l S\n"                                                                                                                                                                         \
6881
0
    "0.533333 0.541176 0.521569 RG 14.07 6.82 m 9.828 11.062 l S\n"                                                                                                                                                                            \
6882
0
    "0.729412 0.741176 0.713725 RG 14.07 7.82 m 9.828 12.062 l S\n"                                                                                                                                                                            \
6883
0
    "0.533333 0.541176 0.521569 RG 6.93 15.141 m 8 20 14.27 20.5 16 20.5 c\n"                                                                                                                                                                  \
6884
0
    "18.094 20.504 19.5 20 19.5 18 c 19.5 16.699 20.91 16.418 22.5 16.5 c S\n"                                                                                                                                                                 \
6885
0
    "0.729412 0.741176 0.713725 RG 0.999781 w\n"                                                                                                                                                                                               \
6886
0
    "1 j\n"                                                                                                                                                                                                                                    \
6887
0
    "q 1 0 0 -1 0 24 cm\n"                                                                                                                                                                                                                     \
6888
0
    "8.492 7.707 m 8.492 8.535 7.82 9.207 6.992 9.207 c 6.164 9.207 5.492\n"                                                                                                                                                                   \
6889
0
    "8.535 5.492 7.707 c 5.492 6.879 6.164 6.207 6.992 6.207 c 7.82 6.207\n"                                                                                                                                                                   \
6890
0
    "8.492 6.879 8.492 7.707 c h\n"                                                                                                                                                                                                            \
6891
0
    "8.492 7.707 m S Q\n"                                                                                                                                                                                                                      \
6892
0
    "1 w\n"                                                                                                                                                                                                                                    \
6893
0
    "0 j\n"                                                                                                                                                                                                                                    \
6894
0
    "6.93 16.141 m 8 21 14.27 21.5 16 21.5 c 18.094 21.504 19.5 21 19.5 19 c\n"                                                                                                                                                                \
6895
0
    "19.5 17.699 20.91 17.418 22.5 17.5 c S\n"
6896
6897
void AnnotFileAttachment::draw(Gfx *gfx, bool printing)
6898
0
{
6899
0
    double ca = 1;
6900
6901
0
    if (!isVisible(printing)) {
6902
0
        return;
6903
0
    }
6904
6905
0
    annotLocker();
6906
0
    if (appearance.isNull()) {
6907
0
        ca = opacity;
6908
6909
0
        AnnotAppearanceBuilder appearBuilder;
6910
6911
0
        appearBuilder.append("q\n");
6912
0
        if (color) {
6913
0
            appearBuilder.setDrawColor(*color, true);
6914
0
        } else {
6915
0
            appearBuilder.append("1 1 1 rg\n");
6916
0
        }
6917
0
        if (!iconName->compare("PushPin")) {
6918
0
            appearBuilder.append(ANNOT_FILE_ATTACHMENT_AP_PUSHPIN);
6919
0
        } else if (!iconName->compare("Paperclip")) {
6920
0
            appearBuilder.append(ANNOT_FILE_ATTACHMENT_AP_PAPERCLIP);
6921
0
        } else if (!iconName->compare("Graph")) {
6922
0
            appearBuilder.append(ANNOT_FILE_ATTACHMENT_AP_GRAPH);
6923
0
        } else if (!iconName->compare("Tag")) {
6924
0
            appearBuilder.append(ANNOT_FILE_ATTACHMENT_AP_TAG);
6925
0
        }
6926
0
        appearBuilder.append("Q\n");
6927
6928
0
        static constexpr std::array<double, 4> bbox = { 0, 0, 24, 24 };
6929
0
        if (ca == 1) {
6930
0
            appearance = createForm(appearBuilder.buffer(), bbox, false, Object {});
6931
0
        } else {
6932
0
            Object aStream = createForm(appearBuilder.buffer(), bbox, true, Object {});
6933
6934
0
            std::string appearBuf("/GS0 gs\n/Fm0 Do");
6935
0
            std::unique_ptr<Dict> resDict = createResourcesDict("Fm0", std::move(aStream), "GS0", ca, nullptr);
6936
0
            appearance = createForm(appearBuf, bbox, false, std::move(resDict));
6937
0
        }
6938
0
    }
6939
6940
    // draw the appearance stream
6941
0
    Object obj = appearance.fetch(gfx->getXRef());
6942
0
    gfx->drawAnnot(&obj, nullptr, color.get(), rect->x1, rect->y1, rect->x2, rect->y2, getRotation());
6943
0
}
6944
6945
//------------------------------------------------------------------------
6946
// AnnotSound
6947
//------------------------------------------------------------------------
6948
0
AnnotSound::AnnotSound(PDFDoc *docA, const PDFRectangle &rectA, const Sound &soundA) : AnnotMarkup(docA, rectA)
6949
0
{
6950
0
    type = typeSound;
6951
6952
0
    annotObj.dictSet("Subtype", Object::name("Sound"));
6953
0
    annotObj.dictSet("Sound", soundA.getObject().copy());
6954
6955
0
    initialize(annotObj.getDict());
6956
0
}
6957
6958
0
AnnotSound::AnnotSound(PDFDoc *docA, Object &&dictObject, const Object &obj) : AnnotMarkup(docA, std::move(dictObject), obj)
6959
0
{
6960
0
    type = typeSound;
6961
0
    initialize(annotObj.getDict());
6962
0
}
6963
6964
0
AnnotSound::~AnnotSound() = default;
6965
6966
void AnnotSound::initialize(Dict *dict)
6967
0
{
6968
0
    Object obj1 = dict->lookup("Sound");
6969
6970
0
    sound = Sound::parseSound(obj1);
6971
0
    if (!sound) {
6972
0
        error(errSyntaxError, -1, "Bad Annot Sound");
6973
0
        ok = false;
6974
0
    }
6975
6976
0
    obj1 = dict->lookup("Name");
6977
0
    if (obj1.isName()) {
6978
0
        iconName = std::make_unique<GooString>(obj1.getNameString());
6979
0
    } else {
6980
0
        iconName = std::make_unique<GooString>("Speaker");
6981
0
    }
6982
0
}
6983
6984
#define ANNOT_SOUND_AP_SPEAKER                                                                                                                                                                                                                 \
6985
0
    "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n"                                                                                                                                                                   \
6986
0
    "2.477 21.523 1 19.699 1 c 4.301 1 l 2.477 1 1 2.477 1 4.301 c 1 19.699\n"                                                                                                                                                                 \
6987
0
    "l 1 21.523 2.477 23 4.301 23 c h\n"                                                                                                                                                                                                       \
6988
0
    "4.301 23 m f\n"                                                                                                                                                                                                                           \
6989
0
    "0.533333 0.541176 0.521569 RG 2 w\n"                                                                                                                                                                                                      \
6990
0
    "0 J\n"                                                                                                                                                                                                                                    \
6991
0
    "1 j\n"                                                                                                                                                                                                                                    \
6992
0
    "[] 0.0 d\n"                                                                                                                                                                                                                               \
6993
0
    "4 M 4 14 m 4.086 8.043 l 7 8 l 11 4 l 11 18 l 7 14 l 4 14 l h\n"                                                                                                                                                                          \
6994
0
    "4 14 m S\n"                                                                                                                                                                                                                               \
6995
0
    "1 w\n"                                                                                                                                                                                                                                    \
6996
0
    "1 J\n"                                                                                                                                                                                                                                    \
6997
0
    "0 j\n"                                                                                                                                                                                                                                    \
6998
0
    "13.699 15.398 m 14.699 13.398 14.699 9.398 13.699 7.398 c S\n"                                                                                                                                                                            \
6999
0
    "18.199 19.398 m 21.199 17.398 21.199 5.398 18.199 3.398 c S\n"                                                                                                                                                                            \
7000
0
    "16 17.398 m 18 16.398 18 7.398 16 5.398 c S\n"                                                                                                                                                                                            \
7001
0
    "0.729412 0.741176 0.713725 RG 2 w\n"                                                                                                                                                                                                      \
7002
0
    "0 J\n"                                                                                                                                                                                                                                    \
7003
0
    "1 j\n"                                                                                                                                                                                                                                    \
7004
0
    "4 15 m 4.086 9.043 l 7 9 l 11 5 l 11 19 l 7 15 l 4 15 l h\n"                                                                                                                                                                              \
7005
0
    "4 15 m S\n"                                                                                                                                                                                                                               \
7006
0
    "1 w\n"                                                                                                                                                                                                                                    \
7007
0
    "1 J\n"                                                                                                                                                                                                                                    \
7008
0
    "0 j\n"                                                                                                                                                                                                                                    \
7009
0
    "13.699 16 m 14.699 14 14.699 10 13.699 8 c S\n"                                                                                                                                                                                           \
7010
0
    "18.199 20 m 21.199 18 21.199 6 18.199 4 c S\n"                                                                                                                                                                                            \
7011
0
    "16 18 m 18 17 18 8 16 6 c S\n"
7012
7013
#define ANNOT_SOUND_AP_MIC                                                                                                                                                                                                                     \
7014
0
    "4.301 23 m 19.699 23 l 21.523 23 23 21.523 23 19.699 c 23 4.301 l 23\n"                                                                                                                                                                   \
7015
0
    "2.477 21.523 1 19.699 1 c 4.301 1 l 2.477 1 1 2.477 1 4.301 c 1 19.699\n"                                                                                                                                                                 \
7016
0
    "l 1 21.523 2.477 23 4.301 23 c h\n"                                                                                                                                                                                                       \
7017
0
    "4.301 23 m f\n"                                                                                                                                                                                                                           \
7018
0
    "0.533333 0.541176 0.521569 RG 2 w\n"                                                                                                                                                                                                      \
7019
0
    "1 J\n"                                                                                                                                                                                                                                    \
7020
0
    "0 j\n"                                                                                                                                                                                                                                    \
7021
0
    "[] 0.0 d\n"                                                                                                                                                                                                                               \
7022
0
    "4 M 12 20 m 12 20 l 13.656 20 15 18.656 15 17 c 15 13 l 15 11.344 13.656 10\n"                                                                                                                                                            \
7023
0
    "12 10 c 12 10 l 10.344 10 9 11.344 9 13 c 9 17 l 9 18.656 10.344 20 12\n"                                                                                                                                                                 \
7024
0
    "20 c h\n"                                                                                                                                                                                                                                 \
7025
0
    "12 20 m S\n"                                                                                                                                                                                                                              \
7026
0
    "1 w\n"                                                                                                                                                                                                                                    \
7027
0
    "17.5 14.5 m 17.5 11.973 l 17.5 8.941 15.047 6.5 12 6.5 c 8.953 6.5 6.5\n"                                                                                                                                                                 \
7028
0
    "8.941 6.5 11.973 c 6.5 14.5 l S\n"                                                                                                                                                                                                        \
7029
0
    "2 w\n"                                                                                                                                                                                                                                    \
7030
0
    "0 J\n"                                                                                                                                                                                                                                    \
7031
0
    "12 6.52 m 12 3 l S\n"                                                                                                                                                                                                                     \
7032
0
    "1 J\n"                                                                                                                                                                                                                                    \
7033
0
    "8 3 m 16 3 l S\n"                                                                                                                                                                                                                         \
7034
0
    "0.729412 0.741176 0.713725 RG 12 21 m 12 21 l 13.656 21 15 19.656 15 18 c\n"                                                                                                                                                              \
7035
0
    "15 14 l 15 12.344 13.656 11 12 11 c 12 11 l 10.344 11 9 12.344 9 14 c\n"                                                                                                                                                                  \
7036
0
    "9 18 l 9 19.656 10.344 21 12 21 c h\n"                                                                                                                                                                                                    \
7037
0
    "12 21 m S\n"                                                                                                                                                                                                                              \
7038
0
    "1 w\n"                                                                                                                                                                                                                                    \
7039
0
    "17.5 15.5 m 17.5 12.973 l 17.5 9.941 15.047 7.5 12 7.5 c 8.953 7.5 6.5\n"                                                                                                                                                                 \
7040
0
    "9.941 6.5 12.973 c 6.5 15.5 l S\n"                                                                                                                                                                                                        \
7041
0
    "2 w\n"                                                                                                                                                                                                                                    \
7042
0
    "0 J\n"                                                                                                                                                                                                                                    \
7043
0
    "12 7.52 m 12 4 l S\n"                                                                                                                                                                                                                     \
7044
0
    "1 J\n"                                                                                                                                                                                                                                    \
7045
0
    "8 4 m 16 4 l S\n"
7046
7047
void AnnotSound::draw(Gfx *gfx, bool printing)
7048
0
{
7049
0
    Object obj;
7050
0
    double ca = 1;
7051
7052
0
    if (!isVisible(printing)) {
7053
0
        return;
7054
0
    }
7055
7056
0
    annotLocker();
7057
0
    if (appearance.isNull()) {
7058
0
        ca = opacity;
7059
7060
0
        AnnotAppearanceBuilder appearBuilder;
7061
7062
0
        appearBuilder.append("q\n");
7063
0
        if (color) {
7064
0
            appearBuilder.setDrawColor(*color, true);
7065
0
        } else {
7066
0
            appearBuilder.append("1 1 1 rg\n");
7067
0
        }
7068
0
        if (!iconName->compare("Speaker")) {
7069
0
            appearBuilder.append(ANNOT_SOUND_AP_SPEAKER);
7070
0
        } else if (!iconName->compare("Mic")) {
7071
0
            appearBuilder.append(ANNOT_SOUND_AP_MIC);
7072
0
        }
7073
0
        appearBuilder.append("Q\n");
7074
7075
0
        static constexpr std::array<double, 4> bbox = { 0, 0, 24, 24 };
7076
0
        if (ca == 1) {
7077
0
            appearance = createForm(appearBuilder.buffer(), bbox, false, Object {});
7078
0
        } else {
7079
0
            Object aStream = createForm(appearBuilder.buffer(), bbox, true, Object {});
7080
7081
0
            std::string appearBuf("/GS0 gs\n/Fm0 Do");
7082
0
            std::unique_ptr<Dict> resDict = createResourcesDict("Fm0", std::move(aStream), "GS0", ca, nullptr);
7083
0
            appearance = createForm(appearBuf, bbox, false, std::move(resDict));
7084
0
        }
7085
0
    }
7086
7087
    // draw the appearance stream
7088
0
    obj = appearance.fetch(gfx->getXRef());
7089
0
    gfx->drawAnnot(&obj, nullptr, color.get(), rect->x1, rect->y1, rect->x2, rect->y2, getRotation());
7090
0
}
7091
7092
//------------------------------------------------------------------------
7093
// Annot3D
7094
//------------------------------------------------------------------------
7095
0
Annot3D::Annot3D(PDFDoc *docA, const PDFRectangle &rectA) : Annot(docA, rectA)
7096
0
{
7097
0
    type = type3D;
7098
7099
0
    annotObj.dictSet("Subtype", Object::name("3D"));
7100
7101
0
    initialize(annotObj.getDict());
7102
0
}
7103
7104
0
Annot3D::Annot3D(PDFDoc *docA, Object &&dictObject, const Object &obj) : Annot(docA, std::move(dictObject), obj)
7105
0
{
7106
0
    type = type3D;
7107
0
    initialize(annotObj.getDict());
7108
0
}
7109
7110
0
Annot3D::~Annot3D() = default;
7111
7112
void Annot3D::initialize(Dict *dict)
7113
0
{
7114
0
    Object obj1 = dict->lookup("3DA");
7115
0
    if (obj1.isDict()) {
7116
0
        activation = std::make_unique<Activation>(obj1.getDict());
7117
0
    }
7118
0
}
7119
7120
Annot3D::Activation::Activation(Dict *dict)
7121
0
{
7122
0
    Object obj1;
7123
7124
0
    obj1 = dict->lookup("A");
7125
0
    if (obj1.isName()) {
7126
0
        const std::string &name = obj1.getNameString();
7127
7128
0
        if (name == "PO") {
7129
0
            aTrigger = aTriggerPageOpened;
7130
0
        } else if (name == "PV") {
7131
0
            aTrigger = aTriggerPageVisible;
7132
0
        } else if (name == "XA") {
7133
0
            aTrigger = aTriggerUserAction;
7134
0
        } else {
7135
0
            aTrigger = aTriggerUnknown;
7136
0
        }
7137
0
    } else {
7138
0
        aTrigger = aTriggerUnknown;
7139
0
    }
7140
7141
0
    obj1 = dict->lookup("AIS");
7142
0
    if (obj1.isName()) {
7143
0
        const std::string &name = obj1.getNameString();
7144
7145
0
        if (name == "I") {
7146
0
            aState = aStateEnabled;
7147
0
        } else if (name == "L") {
7148
0
            aState = aStateDisabled;
7149
0
        } else {
7150
0
            aState = aStateUnknown;
7151
0
        }
7152
0
    } else {
7153
0
        aState = aStateUnknown;
7154
0
    }
7155
7156
0
    obj1 = dict->lookup("D");
7157
0
    if (obj1.isName()) {
7158
0
        const std::string &name = obj1.getNameString();
7159
7160
0
        if (name == "PC") {
7161
0
            dTrigger = dTriggerPageClosed;
7162
0
        } else if (name == "PI") {
7163
0
            dTrigger = dTriggerPageInvisible;
7164
0
        } else if (name == "XD") {
7165
0
            dTrigger = dTriggerUserAction;
7166
0
        } else {
7167
0
            dTrigger = dTriggerUnknown;
7168
0
        }
7169
0
    } else {
7170
0
        dTrigger = dTriggerUnknown;
7171
0
    }
7172
7173
0
    obj1 = dict->lookup("DIS");
7174
0
    if (obj1.isName()) {
7175
0
        const std::string &name = obj1.getNameString();
7176
7177
0
        if (name == "U") {
7178
0
            dState = dStateUninstantiaded;
7179
0
        } else if (name == "I") {
7180
0
            dState = dStateInstantiated;
7181
0
        } else if (name == "L") {
7182
0
            dState = dStateLive;
7183
0
        } else {
7184
0
            dState = dStateUnknown;
7185
0
        }
7186
0
    } else {
7187
0
        dState = dStateUnknown;
7188
0
    }
7189
7190
0
    displayToolbar = dict->lookup("TB").getBoolWithDefaultValue(true);
7191
7192
0
    displayNavigation = dict->lookup("NP").getBoolWithDefaultValue(false);
7193
0
}
7194
7195
//------------------------------------------------------------------------
7196
// AnnotRichMedia
7197
//------------------------------------------------------------------------
7198
0
AnnotRichMedia::AnnotRichMedia(PDFDoc *docA, const PDFRectangle &rectA) : Annot(docA, rectA)
7199
0
{
7200
0
    type = typeRichMedia;
7201
7202
0
    annotObj.dictSet("Subtype", Object::name("RichMedia"));
7203
7204
0
    initialize(annotObj.getDict());
7205
0
}
7206
7207
0
AnnotRichMedia::AnnotRichMedia(PDFDoc *docA, Object &&dictObject, const Object &obj) : Annot(docA, std::move(dictObject), obj)
7208
0
{
7209
0
    type = typeRichMedia;
7210
0
    initialize(annotObj.getDict());
7211
0
}
7212
7213
0
AnnotRichMedia::~AnnotRichMedia() = default;
7214
7215
void AnnotRichMedia::initialize(Dict *dict)
7216
0
{
7217
0
    Object obj1 = dict->lookup("RichMediaContent");
7218
0
    if (obj1.isDict()) {
7219
0
        content = std::make_unique<AnnotRichMedia::Content>(obj1.getDict());
7220
0
    }
7221
7222
0
    obj1 = dict->lookup("RichMediaSettings");
7223
0
    if (obj1.isDict()) {
7224
0
        settings = std::make_unique<AnnotRichMedia::Settings>(obj1.getDict());
7225
0
    }
7226
0
}
7227
7228
const AnnotRichMedia::Content *AnnotRichMedia::getContent() const
7229
0
{
7230
0
    return content.get();
7231
0
}
7232
7233
const AnnotRichMedia::Settings *AnnotRichMedia::getSettings() const
7234
0
{
7235
0
    return settings.get();
7236
0
}
7237
7238
AnnotRichMedia::Settings::Settings(Dict *dict)
7239
0
{
7240
0
    Object obj1 = dict->lookup("Activation");
7241
0
    if (obj1.isDict()) {
7242
0
        activation = std::make_unique<AnnotRichMedia::Activation>(obj1.getDict());
7243
0
    }
7244
7245
0
    obj1 = dict->lookup("Deactivation");
7246
0
    if (obj1.isDict()) {
7247
0
        deactivation = std::make_unique<AnnotRichMedia::Deactivation>(obj1.getDict());
7248
0
    }
7249
0
}
7250
7251
0
AnnotRichMedia::Settings::~Settings() = default;
7252
7253
const AnnotRichMedia::Activation *AnnotRichMedia::Settings::getActivation() const
7254
0
{
7255
0
    return activation.get();
7256
0
}
7257
7258
const AnnotRichMedia::Deactivation *AnnotRichMedia::Settings::getDeactivation() const
7259
0
{
7260
0
    return deactivation.get();
7261
0
}
7262
7263
AnnotRichMedia::Activation::Activation(Dict *dict)
7264
0
{
7265
0
    Object obj1 = dict->lookup("Condition");
7266
0
    if (obj1.isName()) {
7267
0
        const std::string &name = obj1.getNameString();
7268
7269
0
        if (name == "PO") {
7270
0
            condition = conditionPageOpened;
7271
0
        } else if (name == "PV") {
7272
0
            condition = conditionPageVisible;
7273
0
        } else if (name == "XA") {
7274
0
            condition = conditionUserAction;
7275
0
        } else {
7276
0
            condition = conditionUserAction;
7277
0
        }
7278
0
    } else {
7279
0
        condition = conditionUserAction;
7280
0
    }
7281
0
}
7282
7283
AnnotRichMedia::Activation::Condition AnnotRichMedia::Activation::getCondition() const
7284
0
{
7285
0
    return condition;
7286
0
}
7287
7288
AnnotRichMedia::Deactivation::Deactivation(Dict *dict)
7289
0
{
7290
0
    Object obj1 = dict->lookup("Condition");
7291
0
    if (obj1.isName()) {
7292
0
        const std::string &name = obj1.getNameString();
7293
7294
0
        if (name == "PC") {
7295
0
            condition = conditionPageClosed;
7296
0
        } else if (name == "PI") {
7297
0
            condition = conditionPageInvisible;
7298
0
        } else if (name == "XD") {
7299
0
            condition = conditionUserAction;
7300
0
        } else {
7301
0
            condition = conditionUserAction;
7302
0
        }
7303
0
    } else {
7304
0
        condition = conditionUserAction;
7305
0
    }
7306
0
}
7307
7308
AnnotRichMedia::Deactivation::Condition AnnotRichMedia::Deactivation::getCondition() const
7309
0
{
7310
0
    return condition;
7311
0
}
7312
7313
AnnotRichMedia::Content::Content(Dict *dict)
7314
0
{
7315
0
    Object obj1 = dict->lookup("Configurations");
7316
0
    if (obj1.isArray()) {
7317
0
        int numberOfConfigurations = obj1.arrayGetLength();
7318
0
        configurations.reserve(numberOfConfigurations);
7319
7320
0
        for (int i = 0; i < numberOfConfigurations; ++i) {
7321
0
            Object obj2 = obj1.arrayGet(i);
7322
0
            if (obj2.isDict()) {
7323
0
                configurations.push_back(std::make_unique<AnnotRichMedia::Configuration>(obj2.getDict()));
7324
0
            } else {
7325
0
                configurations.emplace_back();
7326
0
            }
7327
0
        }
7328
0
    }
7329
7330
0
    obj1 = dict->lookup("Assets");
7331
0
    if (obj1.isDict()) {
7332
0
        Object obj2 = obj1.getDict()->lookup("Names");
7333
0
        if (obj2.isArray()) {
7334
0
            const int length = obj2.arrayGetLength() / 2;
7335
0
            assets.reserve(length);
7336
7337
0
            for (int i = 0; i < length; ++i) {
7338
0
                Object objKey = obj2.arrayGet(2 * i);
7339
0
                Object objVal = obj2.arrayGet(2 * i + 1);
7340
7341
0
                if (!objKey.isString() || objVal.isNull()) {
7342
0
                    error(errSyntaxError, -1, "Bad Annot Asset");
7343
0
                    continue;
7344
0
                }
7345
7346
0
                auto asset = std::make_unique<AnnotRichMedia::Asset>();
7347
7348
0
                asset->name = objKey.takeString();
7349
0
                asset->fileSpec = std::move(objVal);
7350
0
                assets.push_back(std::move(asset));
7351
0
            }
7352
0
        }
7353
0
    }
7354
0
    assets.shrink_to_fit();
7355
0
}
7356
7357
0
AnnotRichMedia::Content::~Content() = default;
7358
7359
0
AnnotRichMedia::Asset::Asset() = default;
7360
7361
0
AnnotRichMedia::Asset::~Asset() = default;
7362
7363
const GooString *AnnotRichMedia::Asset::getName() const
7364
0
{
7365
0
    return name.get();
7366
0
}
7367
7368
const Object *AnnotRichMedia::Asset::getFileSpec() const
7369
0
{
7370
0
    return &fileSpec;
7371
0
}
7372
7373
AnnotRichMedia::Configuration::Configuration(Dict *dict)
7374
0
{
7375
0
    Object obj1 = dict->lookup("Instances");
7376
0
    if (obj1.isArray()) {
7377
0
        int numberOfInstances = obj1.arrayGetLength();
7378
0
        instances.reserve(numberOfInstances);
7379
7380
0
        for (int i = 0; i < numberOfInstances; ++i) {
7381
0
            Object obj2 = obj1.arrayGet(i);
7382
0
            if (obj2.isDict()) {
7383
0
                instances.push_back(std::make_unique<AnnotRichMedia::Instance>(obj2.getDict()));
7384
0
            } else {
7385
0
                instances.emplace_back();
7386
0
            }
7387
0
        }
7388
0
    }
7389
7390
0
    obj1 = dict->lookup("Name");
7391
0
    if (obj1.isString()) {
7392
0
        name = obj1.takeString();
7393
0
    }
7394
7395
0
    obj1 = dict->lookup("Subtype");
7396
0
    if (obj1.isName()) {
7397
0
        const std::string &subtypeName = obj1.getNameString();
7398
7399
0
        if (subtypeName == "3D") {
7400
0
            type = type3D;
7401
0
        } else if (subtypeName == "Flash") {
7402
0
            type = typeFlash;
7403
0
        } else if (subtypeName == "Sound") {
7404
0
            type = typeSound;
7405
0
        } else if (subtypeName == "Video") {
7406
0
            type = typeVideo;
7407
0
        } else {
7408
            // determine from first non null instance
7409
0
            type = typeFlash; // default in case all instances are null
7410
0
            for (auto &instance : instances) {
7411
0
                if (instance) {
7412
0
                    switch (instance->getType()) {
7413
0
                    case AnnotRichMedia::Instance::type3D:
7414
0
                        type = type3D;
7415
0
                        break;
7416
0
                    case AnnotRichMedia::Instance::typeFlash:
7417
0
                        type = typeFlash;
7418
0
                        break;
7419
0
                    case AnnotRichMedia::Instance::typeSound:
7420
0
                        type = typeSound;
7421
0
                        break;
7422
0
                    case AnnotRichMedia::Instance::typeVideo:
7423
0
                        type = typeVideo;
7424
0
                        break;
7425
0
                    }
7426
                    // break the loop since we found the first non null instance
7427
0
                    break;
7428
0
                }
7429
0
            }
7430
0
        }
7431
0
    }
7432
0
}
7433
7434
0
AnnotRichMedia::Configuration::~Configuration() = default;
7435
7436
const GooString *AnnotRichMedia::Configuration::getName() const
7437
0
{
7438
0
    return name.get();
7439
0
}
7440
7441
AnnotRichMedia::Configuration::Type AnnotRichMedia::Configuration::getType() const
7442
0
{
7443
0
    return type;
7444
0
}
7445
7446
AnnotRichMedia::Instance::Instance(Dict *dict)
7447
0
{
7448
0
    Object obj1 = dict->lookup("Subtype");
7449
0
    const std::string name = obj1.isName() ? obj1.getNameString() : "";
7450
7451
0
    if (name == "3D") {
7452
0
        type = type3D;
7453
0
    } else if (name == "Flash") {
7454
0
        type = typeFlash;
7455
0
    } else if (name == "Sound") {
7456
0
        type = typeSound;
7457
0
    } else if (name == "Video") {
7458
0
        type = typeVideo;
7459
0
    } else {
7460
0
        type = typeFlash;
7461
0
    }
7462
7463
0
    obj1 = dict->lookup("Params");
7464
0
    if (obj1.isDict()) {
7465
0
        params = std::make_unique<AnnotRichMedia::Params>(obj1.getDict());
7466
0
    }
7467
0
}
7468
7469
0
AnnotRichMedia::Instance::~Instance() = default;
7470
7471
AnnotRichMedia::Instance::Type AnnotRichMedia::Instance::getType() const
7472
0
{
7473
0
    return type;
7474
0
}
7475
7476
const AnnotRichMedia::Params *AnnotRichMedia::Instance::getParams() const
7477
0
{
7478
0
    return params.get();
7479
0
}
7480
7481
AnnotRichMedia::Params::Params(Dict *dict)
7482
0
{
7483
0
    Object obj1 = dict->lookup("FlashVars");
7484
0
    if (obj1.isString()) {
7485
0
        flashVars = obj1.takeString();
7486
0
    }
7487
0
}
7488
7489
0
AnnotRichMedia::Params::~Params() = default;
7490
7491
const GooString *AnnotRichMedia::Params::getFlashVars() const
7492
0
{
7493
0
    return flashVars.get();
7494
0
}
7495
7496
//------------------------------------------------------------------------
7497
// Annots
7498
//------------------------------------------------------------------------
7499
7500
Annots::Annots(PDFDoc *docA, int page, Object *annotsObj)
7501
3.78k
{
7502
3.78k
    doc = docA;
7503
7504
3.78k
    if (annotsObj->isArray()) {
7505
0
        Array *annotsArray = annotsObj->getArray();
7506
0
        for (int i = 0; i < annotsArray->getLength(); ++i) {
7507
            // get the Ref to this annot and pass it to Annot constructor
7508
            // this way, it'll be possible for the annot to retrieve the corresponding
7509
            // form widget
7510
0
            Object obj1 = annotsArray->get(i);
7511
0
            if (obj1.isDict()) {
7512
0
                const Object &obj2 = annotsArray->getNF(i);
7513
0
                std::shared_ptr<Annot> annot = createAnnot(std::move(obj1), obj2);
7514
0
                if (annot) {
7515
0
                    if (annot.use_count() > 100000) {
7516
0
                        error(errSyntaxError, -1, "Annotations likely malformed. Too many references. Stopping processing annots on page {0:d}", page);
7517
0
                        break;
7518
0
                    }
7519
0
                    if (annot->isOk()) {
7520
0
                        annot->setPage(page, false); // Don't change /P
7521
0
                        appendAnnot(annot);
7522
0
                    }
7523
0
                }
7524
0
            }
7525
0
        }
7526
0
    }
7527
3.78k
}
7528
7529
void Annots::appendAnnot(std::shared_ptr<Annot> annot)
7530
0
{
7531
0
    if (annot && annot->isOk()) {
7532
0
        annots.push_back(std::move(annot));
7533
0
    }
7534
0
}
7535
7536
bool Annots::removeAnnot(const std::shared_ptr<Annot> &annot)
7537
0
{
7538
0
    auto idx = std::ranges::find(annots, annot);
7539
7540
0
    if (idx == annots.end()) {
7541
0
        return false;
7542
0
    }
7543
0
    annots.erase(idx);
7544
0
    return true;
7545
0
}
7546
7547
std::shared_ptr<Annot> Annots::createAnnot(Object &&dictObject, const Object &obj)
7548
0
{
7549
0
    std::shared_ptr<Annot> annot = nullptr;
7550
0
    Object obj1 = dictObject.dictLookup("Subtype");
7551
0
    if (obj1.isName()) {
7552
0
        const std::string &typeName = obj1.getNameString();
7553
7554
0
        if (typeName == "Text") {
7555
0
            annot = std::make_shared<AnnotText>(doc, std::move(dictObject), obj);
7556
0
        } else if (typeName == "Link") {
7557
0
            annot = std::make_shared<AnnotLink>(doc, std::move(dictObject), obj);
7558
0
        } else if (typeName == "FreeText") {
7559
0
            annot = std::make_shared<AnnotFreeText>(doc, std::move(dictObject), obj);
7560
0
        } else if (typeName == "Line") {
7561
0
            annot = std::make_shared<AnnotLine>(doc, std::move(dictObject), obj);
7562
0
        } else if (typeName == "Square") {
7563
0
            annot = std::make_shared<AnnotGeometry>(doc, std::move(dictObject), obj);
7564
0
        } else if (typeName == "Circle") {
7565
0
            annot = std::make_shared<AnnotGeometry>(doc, std::move(dictObject), obj);
7566
0
        } else if (typeName == "Polygon") {
7567
0
            annot = std::make_shared<AnnotPolygon>(doc, std::move(dictObject), obj);
7568
0
        } else if (typeName == "PolyLine") {
7569
0
            annot = std::make_shared<AnnotPolygon>(doc, std::move(dictObject), obj);
7570
0
        } else if (typeName == "Highlight") {
7571
0
            annot = std::make_shared<AnnotTextMarkup>(doc, std::move(dictObject), obj);
7572
0
        } else if (typeName == "Underline") {
7573
0
            annot = std::make_shared<AnnotTextMarkup>(doc, std::move(dictObject), obj);
7574
0
        } else if (typeName == "Squiggly") {
7575
0
            annot = std::make_shared<AnnotTextMarkup>(doc, std::move(dictObject), obj);
7576
0
        } else if (typeName == "StrikeOut") {
7577
0
            annot = std::make_shared<AnnotTextMarkup>(doc, std::move(dictObject), obj);
7578
0
        } else if (typeName == "Stamp") {
7579
0
            annot = std::make_shared<AnnotStamp>(doc, std::move(dictObject), obj);
7580
0
        } else if (typeName == "Caret") {
7581
0
            annot = std::make_shared<AnnotCaret>(doc, std::move(dictObject), obj);
7582
0
        } else if (typeName == "Ink") {
7583
0
            annot = std::make_shared<AnnotInk>(doc, std::move(dictObject), obj);
7584
0
        } else if (typeName == "FileAttachment") {
7585
0
            annot = std::make_shared<AnnotFileAttachment>(doc, std::move(dictObject), obj);
7586
0
        } else if (typeName == "Sound") {
7587
0
            annot = std::make_shared<AnnotSound>(doc, std::move(dictObject), obj);
7588
0
        } else if (typeName == "Movie") {
7589
0
            annot = std::make_shared<AnnotMovie>(doc, std::move(dictObject), obj);
7590
0
        } else if (typeName == "Widget") {
7591
            // Find the annot in forms
7592
0
            if (obj.isRef()) {
7593
0
                Form *form = doc->getCatalog()->getForm();
7594
0
                if (form) {
7595
0
                    FormWidget *widget = form->findWidgetByRef(obj.getRef());
7596
0
                    if (widget) {
7597
0
                        annot = widget->getWidgetAnnotation();
7598
0
                    }
7599
0
                }
7600
0
            }
7601
0
            if (!annot) {
7602
0
                annot = std::make_shared<AnnotWidget>(doc, std::move(dictObject), obj);
7603
0
            }
7604
0
        } else if (typeName == "Screen") {
7605
0
            annot = std::make_shared<AnnotScreen>(doc, std::move(dictObject), obj);
7606
0
        } else if (typeName == "PrinterMark") {
7607
0
            annot = std::make_shared<Annot>(doc, std::move(dictObject), obj);
7608
0
        } else if (typeName == "TrapNet") {
7609
0
            annot = std::make_shared<Annot>(doc, std::move(dictObject), obj);
7610
0
        } else if (typeName == "Watermark") {
7611
0
            annot = std::make_shared<Annot>(doc, std::move(dictObject), obj);
7612
0
        } else if (typeName == "3D") {
7613
0
            annot = std::make_shared<Annot3D>(doc, std::move(dictObject), obj);
7614
0
        } else if (typeName == "RichMedia") {
7615
0
            annot = std::make_shared<AnnotRichMedia>(doc, std::move(dictObject), obj);
7616
0
        } else if (typeName == "Popup") {
7617
            /* Popup annots are already handled by markup annots
7618
             * Here we only care about popup annots without a
7619
             * markup annotation associated
7620
             */
7621
0
            Object obj2 = dictObject.dictLookup("Parent");
7622
0
            if (obj2.isNull()) {
7623
0
                annot = std::make_shared<AnnotPopup>(doc, std::move(dictObject), obj);
7624
0
            } else {
7625
0
                annot = nullptr;
7626
0
            }
7627
0
        } else {
7628
0
            annot = std::make_shared<Annot>(doc, std::move(dictObject), obj);
7629
0
        }
7630
0
    }
7631
7632
0
    return annot;
7633
0
}
7634
7635
std::shared_ptr<Annot> Annots::findAnnot(Ref ref)
7636
0
{
7637
0
    for (const auto &annot : annots) {
7638
0
        if (annot->match(ref)) {
7639
0
            return annot;
7640
0
        }
7641
0
    }
7642
0
    return nullptr;
7643
0
}
7644
7645
3.78k
Annots::~Annots() = default;
7646
7647
//------------------------------------------------------------------------
7648
// AnnotAppearanceBuilder
7649
//------------------------------------------------------------------------
7650
7651
AnnotAppearanceBuilder::AnnotAppearanceBuilder()
7652
0
{
7653
0
    addedDingbatsResource = false;
7654
0
}
7655
7656
0
AnnotAppearanceBuilder::~AnnotAppearanceBuilder() = default;
7657
7658
void AnnotAppearanceBuilder::append(const char *text)
7659
0
{
7660
0
    if (text) {
7661
0
        appearBuf.append(text);
7662
0
    }
7663
0
}
7664
7665
void AnnotAppearanceBuilder::appendf(const char *fmt, ...) GOOSTRING_FORMAT
7666
0
{
7667
0
    va_list argList;
7668
7669
0
    va_start(argList, fmt);
7670
0
    GooString::appendfv(appearBuf, fmt, argList);
7671
0
    va_end(argList);
7672
0
}
7673
7674
const std::string &AnnotAppearanceBuilder::buffer() const
7675
0
{
7676
0
    return appearBuf;
7677
0
}