Subversion Repositories Scribus

Rev

Rev 24009 | Rev 24130 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
4430 cbradney 1
/*
2
For general Scribus (>=1.3.2) copyright and licensing information please refer
3
to the COPYING file provided with the program. Following this notice may exist
4
a copyright and/or license notice that predates the release of Scribus 1.3.2
5
for which a new license (GPL+exception) is in place.
6
*/
3133 fschmid 7
/***************************************************************************
14043 jghali 8
                                                  pdflib_core.cpp  -  description
9
                                                         -------------------
10
        begin                : Sat Jan 19 2002
11
        copyright            : (C) 2002 by Franz Schmid
12
        email                : Franz.Schmid@altmuehlnet.de
3133 fschmid 13
 ***************************************************************************/
14
 
15
/***************************************************************************
16
 *                                                                         *
17
 *   This program is free software; you can redistribute it and/or modify  *
18
 *   it under the terms of the GNU General Public License as published by  *
19
 *   the Free Software Foundation; either version 2 of the License, or     *
20
 *   (at your option) any later version.                                   *
21
 *                                                                         *
22
 ***************************************************************************/
23
 
10054 craig 24
#include "pdflib_core.h"
3133 fschmid 25
 
26
#include "scconfig.h"
27
 
17539 jghali 28
#if defined(_MSC_VER) && !defined(_USE_MATH_DEFINES)
14170 jghali 29
#define _USE_MATH_DEFINES
30
#endif
12080 avox 31
#include <cmath>
32
#include <cstdlib>
4229 craig 33
#include <string>
12080 avox 34
#ifdef HAVE_UNISTD_H
35
#include <unistd.h>
36
#endif
37
 
38
#include "rc4.h"
39
 
40
#include <QByteArray>
19914 craig 41
#include <QCryptographicHash>
10223 cbradney 42
#include <QDateTime>
10004 fschmid 43
#include <QDataStream>
12080 avox 44
#include <QDebug>
10223 cbradney 45
#include <QDir>
12080 avox 46
#include <QFileInfo>
47
#include <QImage>
9803 fschmid 48
#include <QList>
11453 fschmid 49
#include <QPainterPath>
12080 avox 50
#include <QRect>
51
#include <QRegExp>
19671 jghali 52
#include <QScopedPointer>
12080 avox 53
#include <QStack>
54
#include <QString>
13839 fschmid 55
#include <QTemporaryFile>
10614 cbradney 56
#include <QTextCodec>
14043 jghali 57
#include <QtXml>
58
#include <QUuid>
3133 fschmid 59
 
10601 mrdocs 60
#include "cmsettings.h"
4028 cbradney 61
#include "commonstrings.h"
3133 fschmid 62
#include "pageitem.h"
10601 mrdocs 63
#include "pageitem_textframe.h"
16258 fschmid 64
#include "pageitem_group.h"
17312 fschmid 65
#include "pageitem_table.h"
10212 cbradney 66
#include "pdfoptions.h"
67
#include "prefscontext.h"
68
#include "prefsmanager.h"
69
#include "sccolor.h"
70
#include "sccolorengine.h"
71
#include "scfonts.h"
21107 craig 72
#include "text/textlayoutpainter.h"
20103 avox 73
#include "fonts/cff.h"
74
#include "fonts/sfnt.h"
16736 jghali 75
#include "scpage.h"
5917 jghali 76
#include "scpaths.h"
10212 cbradney 77
#include "scpattern.h"
19115 avox 78
 
5243 cbradney 79
#include "scribuscore.h"
3699 cbradney 80
#include "scribusdoc.h"
11599 jghali 81
#include "scstreamfilter_flate.h"
82
#include "scstreamfilter_rc4.h"
17312 fschmid 83
#include "tableutils.h"
3133 fschmid 84
#include "util.h"
11605 jghali 85
#include "util_file.h"
10212 cbradney 86
#include "util_formats.h"
10203 cbradney 87
#include "util_math.h"
12517 fschmid 88
#include "util_ghostscript.h"
21107 craig 89
#include "text/boxes.h"
16736 jghali 90
 
13839 fschmid 91
#ifdef HAVE_OSG
14930 fschmid 92
        #include "third_party/prc/exportPRC.h"
13839 fschmid 93
#endif
3133 fschmid 94
 
16736 jghali 95
#include "ui/bookmwin.h"
96
#include "ui/bookmarkpalette.h"
97
#include "ui/multiprogressdialog.h"
98
 
3133 fschmid 99
using namespace std;
17312 fschmid 100
using namespace TableUtils;
3133 fschmid 101
 
12096 jghali 102
#if defined(_WIN32)
103
#undef GetObject
104
#endif
3133 fschmid 105
 
21107 craig 106
static inline QByteArray FToStr(double c)
107
{
108
        double v = c;
109
        if (fabs(c) < 0.0000001)
110
                v = 0.0;
111
        return QByteArray::number(v, 'f', 5);
112
}
6562 fschmid 113
 
23562 jghali 114
static inline QByteArray TransformToStr(const QTransform& tr)
115
{
116
        return FToStr(tr.m11()) + " " + FToStr(tr.m12()) + " " + FToStr(tr.m21()) + " " + FToStr(tr.m22()) + " " + FToStr(tr.dx()) + " " + FToStr(tr.dy());
117
}
118
 
21107 craig 119
class PdfPainter: public TextLayoutPainter
120
{
22987 jghali 121
        QByteArray m_backBuffer;
21107 craig 122
        QByteArray m_glyphBuffer;
123
        QByteArray m_pathBuffer;
21942 craig 124
//      PageItem* m_item;
21107 craig 125
        QMap<QString, PdfFont>  m_UsedFontsP;
126
        PDFLibCore *m_pdf;
127
        uint m_PNr;
128
        const ScPage* m_page;
129
 
22098 jghali 130
        QByteArray m_prevFontName;
131
        int        m_prevFontSize;
132
 
22600 craig 133
        QByteArray transformToStr(const QTransform& tr)
21107 craig 134
        {
135
                return FToStr(tr.m11()) + " " + FToStr(-tr.m12()) + " " + FToStr(-tr.m21()) + " " + FToStr(tr.m22()) + " " + FToStr(tr.dx()) + " " + FToStr(-tr.dy());
136
        }
137
 
138
public:
139
        PdfPainter(PageItem *ite, PDFLibCore *pdf, uint num, const ScPage* pag) :
22987 jghali 140
                m_backBuffer(),
21107 craig 141
                m_glyphBuffer(),
142
                m_pathBuffer(),
21942 craig 143
//              m_item(ite),
21107 craig 144
                m_pdf(pdf),
145
                m_PNr(num),
22098 jghali 146
                m_page(pag),
147
                m_prevFontSize(-1)
21107 craig 148
        {}
149
 
150
        ~PdfPainter() {}
151
 
23073 jghali 152
        void drawGlyph(const GlyphCluster& gc) override
21107 craig 153
        {
21563 jghali 154
                if (gc.isControlGlyphs() || gc.isEmpty())
21107 craig 155
                        return;
21563 jghali 156
 
157
                double current_x = 0.0;
22507 jghali 158
                for (const GlyphLayout& gl : gc.glyphs())
159
                {
22780 jghali 160
                        if (gl.glyph >= ScFace::CONTROL_GLYPHS)
161
                        {
23072 jghali 162
                                current_x += gl.xadvance * gl.scaleH;
22780 jghali 163
                                continue;
164
                        }
165
 
21563 jghali 166
                        PdfFont pdfFont = m_pdf->UsedFontsP[font().replacementName()];
167
                        QByteArray StrokeColor;
168
                        QByteArray FillColor;
21107 craig 169
 
21563 jghali 170
                        if (strokeColor().color != CommonStrings::None)
171
                                StrokeColor = m_pdf->putColor(strokeColor().color, strokeColor().shade, false);
172
                        if (fillColor().color != CommonStrings::None)
173
                                FillColor = m_pdf->putColor(fillColor().color, fillColor().shade, true);
21107 craig 174
 
21563 jghali 175
                        QTransform transform = matrix();
176
                        if (pdfFont.method == Use_XForm)
177
                        {
178
                                if (!FillColor.isEmpty())
179
                                        m_pathBuffer += FillColor;
21107 craig 180
 
21563 jghali 181
                                m_pathBuffer += "q\n";
182
                                m_pathBuffer += transformToStr(transform) + " cm\n";
23072 jghali 183
                                m_pathBuffer += FToStr(fontSize()) + " 0 0 " + FToStr(fontSize()) + " " + FToStr(x() + gl.xoffset + current_x) + " " + FToStr(-y() + fontSize() - gl.yoffset) + " cm\n";
21107 craig 184
 
21563 jghali 185
                                if (gl.scaleV != 1.0)
23072 jghali 186
                                        m_pathBuffer += "1 0 0 1 0 " + FToStr(gl.scaleV - 1.0) + " cm\n";
21563 jghali 187
                                m_pathBuffer += FToStr(qMax(gl.scaleH, 0.1)) + " 0 0 " + FToStr(qMax(gl.scaleV, 0.1)) + " 0 0 cm\n";
21107 craig 188
 
21563 jghali 189
                                if (!FillColor.isEmpty())
21894 jghali 190
                                        m_pathBuffer += pdfFont.name + "_gl" + Pdf::toPdf(gl.glyph) + " Do\n";
21563 jghali 191
                                m_pathBuffer += "Q\n";
21107 craig 192
                        }
193
                        else
21563 jghali 194
                        {
195
                                uint gid = gl.glyph;
196
                                uint fontNr = 65535;
21107 craig 197
 
198
                                switch (pdfFont.encoding)
199
                                {
21563 jghali 200
                                case  Encode_256:
201
                                        gid = pdfFont.glyphmap[gid];
202
                                        fontNr = gid / 256;
203
                                        gid = gid % 256;
204
                                        break;
21107 craig 205
                                case Encode_224:
21563 jghali 206
                                        fontNr = gid / 224;
207
                                        gid = gid % 224 + 32;
21107 craig 208
                                        break;
21563 jghali 209
                                case Encode_Subset:
210
                                        gid = pdfFont.glyphmap[gid];
211
                                        break;
21107 craig 212
                                case Encode_IdentityH:
213
                                        break;
214
                                }
215
 
22098 jghali 216
                                QByteArray pdfFontName = pdfFont.name;
217
                                if (fontNr != 65535)
218
                                {
219
                                        pdfFontName += "S";
220
                                        pdfFontName += Pdf::toPdf(fontNr);
221
                                }
21107 craig 222
 
22099 jghali 223
                                if ((pdfFontName != m_prevFontName) || (fontSize() != m_prevFontSize))
22098 jghali 224
                                {
225
                                        m_glyphBuffer += pdfFontName + " " + FToStr(fontSize()) + " Tf\n";
226
                                        m_prevFontName = pdfFontName;
227
                                        m_prevFontSize = fontSize();
228
                                }
229
 
21563 jghali 230
                                if (!StrokeColor.isEmpty())
231
                                        m_glyphBuffer += StrokeColor;
21107 craig 232
 
21563 jghali 233
                                if (!FillColor.isEmpty())
234
                                        m_glyphBuffer += FillColor;
21107 craig 235
 
21563 jghali 236
                                m_glyphBuffer += "0 Tr\n";
21107 craig 237
 
21563 jghali 238
                                transform.translate(x() + gl.xoffset + current_x , y() + gl.yoffset);
239
                                transform.scale(qMax(gl.scaleH, 0.1), qMax(gl.scaleV, 0.1));
240
                                m_glyphBuffer += transformToStr(transform) + " Tm\n";
21107 craig 241
 
21563 jghali 242
                                if (pdfFont.method != Use_Type3 || !FillColor.isEmpty())
21107 craig 243
                                {
21563 jghali 244
                                        switch (pdfFont.encoding)
21107 craig 245
                                        {
21563 jghali 246
                                        case Encode_224:
247
                                        case Encode_256:
248
                                                m_glyphBuffer += Pdf::toHexString8(gid) + " Tj\n";
249
                                                break;
250
                                        case Encode_IdentityH:
251
                                        default:
252
                                                m_glyphBuffer += Pdf::toHexString16(gid) + " Tj\n";
253
                                                break;
21107 craig 254
                                        }
255
                                }
256
                        }
23072 jghali 257
                        current_x += gl.xadvance * gl.scaleH;
21107 craig 258
                }
21563 jghali 259
        }
21107 craig 260
 
23073 jghali 261
        void drawGlyphOutline(const GlyphCluster& gc, bool fill) override
21563 jghali 262
        {
263
                if (gc.isControlGlyphs() || gc.isEmpty())
264
                        return;
21107 craig 265
 
21563 jghali 266
                double current_x = 0.0;
22507 jghali 267
                for (const GlyphLayout& gl : gc.glyphs())
268
                {
22780 jghali 269
                        if (gl.glyph >= ScFace::CONTROL_GLYPHS)
270
                        {
23072 jghali 271
                                current_x += gl.xadvance * gl.scaleH;
22780 jghali 272
                                continue;
273
                        }
274
 
21563 jghali 275
                        PdfFont pdfFont = m_pdf->UsedFontsP[font().replacementName()];
276
                        QByteArray StrokeColor;
277
                        QByteArray FillColor;
278
                        QTransform transform = matrix();
21107 craig 279
 
21563 jghali 280
                        if (strokeColor().color != CommonStrings::None)
281
                                StrokeColor = m_pdf->putColor(strokeColor().color, strokeColor().shade, false);
282
                        if (fill && fillColor().color != CommonStrings::None)
283
                                FillColor = m_pdf->putColor(fillColor().color, fillColor().shade, true);
21107 craig 284
 
21563 jghali 285
                        if (pdfFont.method == Use_XForm)
286
                        {
23072 jghali 287
                                m_pathBuffer += "q\n";
21563 jghali 288
                                if (!StrokeColor.isEmpty())
289
                                {
23072 jghali 290
                                        m_pathBuffer += FToStr(strokeWidth()) + " w\n[] 0 d\n0 J\n0 j\n";
21563 jghali 291
                                        m_pathBuffer += StrokeColor;
292
                                }
23072 jghali 293
                                m_pathBuffer += transformToStr(transform) + " cm\n";
21107 craig 294
 
21563 jghali 295
                                if (!FillColor.isEmpty())
23072 jghali 296
                                {
297
                                        m_pathBuffer += "q\n";
21563 jghali 298
                                        m_pathBuffer += FillColor;
23072 jghali 299
                                        m_pathBuffer += FToStr(fontSize()) + " 0 0 " + FToStr(fontSize()) + " " + FToStr(x() + gl.xoffset + current_x) + " " + FToStr(-y() + fontSize() - gl.yoffset) + " cm\n";
300
                                        if (gc.scaleV() != 1.0)
301
                                                m_pathBuffer += "1 0 0 1 0 " + FToStr(gl.scaleV - 1.0) + " cm\n";
302
                                        m_pathBuffer += FToStr(qMax(gc.scaleH(), 0.1)) + " 0 0 " + FToStr(qMax(gc.scaleV(), 0.1)) + " 0 0 cm\n";
303
                                        m_pathBuffer += pdfFont.name + "_gl" + Pdf::toPdf(gl.glyph) + " Do\n";
304
                                        m_pathBuffer += "Q\n";
305
                                }
21563 jghali 306
 
23072 jghali 307
                                m_pathBuffer += "1.0 0 0 1.0 " + FToStr(x()) + " " + FToStr(fontSize() - y()) + " cm\n";
308
                                m_pathBuffer += "1.0 0 0 1.0 " + FToStr(gl.xoffset + current_x) + " " + FToStr(gc.scaleV() * fontSize() - fontSize() - gl.yoffset) + " cm\n";
21107 craig 309
 
310
                                FPointArray outline = font().glyphOutline(gl.glyph);
311
                                QTransform mat;
23072 jghali 312
                                mat.scale((fontSize() * gc.scaleH()) / 10.0, (fontSize() * gc.scaleV()) / 10.0);
21107 craig 313
                                outline.map(mat);
314
                                bool nPath = true;
315
                                FPoint np;
316
                                if (outline.size() > 3)
317
                                {
318
                                        for (int poi = 0; poi < outline.size() - 3; poi += 4)
319
                                        {
320
                                                if (outline.isMarker(poi))
321
                                                {
322
                                                        m_pathBuffer += "h\n";
323
                                                        nPath = true;
324
                                                        continue;
325
                                                }
326
 
327
                                                if (nPath)
328
                                                {
329
                                                        np = outline.point(poi);
330
                                                        m_pathBuffer += FToStr(np.x()) + " " + FToStr(-np.y()) + " m\n";
331
                                                        nPath = false;
332
                                                }
333
 
334
                                                np = outline.point(poi + 1);
335
                                                m_pathBuffer += FToStr(np.x()) + " " + FToStr(-np.y()) + " ";
336
                                                np = outline.point(poi + 3);
337
                                                m_pathBuffer += FToStr(np.x()) + " " + FToStr(-np.y()) + " ";
338
                                                np = outline.point(poi + 2);
339
                                                m_pathBuffer += FToStr(np.x()) + " " + FToStr(-np.y()) + " c\n";
340
                                        }
341
                                }
342
                                m_pathBuffer += "h s\n";
343
                                m_pathBuffer += "Q\n";
344
                        }
345
                        else
346
                        {
21563 jghali 347
                                if (!StrokeColor.isEmpty())
348
                                        m_pathBuffer += StrokeColor;
21107 craig 349
 
21563 jghali 350
                                uint gid = gl.glyph;
351
                                uint fontNr = 65535;
21107 craig 352
                                switch (pdfFont.encoding)
353
                                {
21563 jghali 354
                                case  Encode_256:
355
                                        gid = pdfFont.glyphmap[gid];
356
                                        fontNr = gid / 256;
357
                                        gid = gid % 256;
358
                                        break;
21107 craig 359
                                case Encode_224:
21563 jghali 360
                                        fontNr = gid / 224;
361
                                        gid = gid % 224 + 32;
21107 craig 362
                                        break;
21563 jghali 363
                                case Encode_Subset:
364
                                        gid = pdfFont.glyphmap[gid];
365
                                        break;
21107 craig 366
                                case Encode_IdentityH:
367
                                        break;
368
                                }
21563 jghali 369
 
22098 jghali 370
                                QByteArray pdfFontName = pdfFont.name;
371
                                if (fontNr != 65535)
372
                                {
373
                                        pdfFontName += "S";
374
                                        pdfFontName += Pdf::toPdf(fontNr);
375
                                }
21563 jghali 376
 
22099 jghali 377
                                if ((pdfFontName != m_prevFontName) || (fontSize() != m_prevFontSize))
22098 jghali 378
                                {
379
                                        m_glyphBuffer += pdfFontName + " " + FToStr(fontSize()) + " Tf\n";
380
                                        m_prevFontName = pdfFontName;
381
                                        m_prevFontSize = fontSize();
382
                                }
383
 
21563 jghali 384
                                if (!StrokeColor.isEmpty())
385
                                        m_glyphBuffer += StrokeColor;
386
 
387
                                if (!FillColor.isEmpty())
388
                                        m_glyphBuffer += FillColor;
389
 
390
                                if (pdfFont.method == Use_Type3 && !StrokeColor.isEmpty())
391
                                {
392
                                        m_pathBuffer += "q\n";
393
                                        m_pathBuffer += FToStr(strokeWidth()) + " w\n[] 0 d\n0 J\n0 j\n";
394
 
395
                                        m_pathBuffer += transformToStr(transform) + " cm\n";
23072 jghali 396
                                        m_pathBuffer += "1.0 0 0 1.0 " + FToStr(x()) + " " + FToStr(fontSize() - y()) + " cm\n";
397
                                        m_pathBuffer += "1.0 0 0 1.0 " + FToStr(gl.xoffset + current_x) + " " + FToStr(gc.scaleV() * fontSize() - fontSize() - gl.yoffset) + " cm\n";
21563 jghali 398
 
399
                                        /* paint outline */
400
                                        FPointArray outline = font().glyphOutline(gl.glyph);
401
                                        QTransform mat;
23072 jghali 402
                                        mat.scale((fontSize() * gc.scaleH()) / 10.0, (fontSize() * gc.scaleV()) / 10.0);
21563 jghali 403
                                        outline.map(mat);
404
                                        bool nPath = true;
405
                                        FPoint np;
406
                                        if (outline.size() > 3)
407
                                        {
408
                                                for (int poi = 0; poi < outline.size() - 3; poi += 4)
409
                                                {
410
                                                        if (outline.isMarker(poi))
411
                                                        {
412
                                                                m_pathBuffer += "h\n";
413
                                                                nPath = true;
414
                                                                continue;
415
                                                        }
416
 
417
                                                        if (nPath)
418
                                                        {
419
                                                                np = outline.point(poi);
420
                                                                m_pathBuffer += FToStr(np.x()) + " " + FToStr(-np.y()) + " m\n";
421
                                                                nPath = false;
422
                                                        }
423
 
424
                                                        np = outline.point(poi + 1);
425
                                                        m_pathBuffer += FToStr(np.x()) + " " + FToStr(-np.y()) + " ";
426
                                                        np = outline.point(poi + 3);
427
                                                        m_pathBuffer += FToStr(np.x()) + " " + FToStr(-np.y()) + " ";
428
                                                        np = outline.point(poi + 2);
429
                                                        m_pathBuffer += FToStr(np.x()) + " " + FToStr(-np.y()) + " c\n";
430
                                                }
431
                                        }
432
                                        m_pathBuffer += "h s\n";
433
                                        m_pathBuffer += "Q\n";
434
                                }
435
                                else
436
                                {
437
                                        m_glyphBuffer += FToStr(strokeWidth()) + " w ";
438
                                        if (fill)
439
                                                m_glyphBuffer += "2 Tr\n";
440
                                        else
441
                                                m_glyphBuffer += "1 Tr\n";
442
                                }
443
 
444
                                transform.translate(x() + gl.xoffset + current_x, y() + gl.yoffset);
445
                                transform.scale(qMax(gc.scaleH(), 0.1), qMax(gc.scaleV(), 0.1));
446
                                m_glyphBuffer += transformToStr(transform) + " Tm\n";
447
 
448
                                if (pdfFont.method != Use_Type3 || !FillColor.isEmpty())
449
                                {
450
                                        switch (pdfFont.encoding)
451
                                        {
452
                                        case Encode_224:
453
                                        case Encode_256:
454
                                                m_glyphBuffer += Pdf::toHexString8(gid) + " Tj\n";
455
                                                break;
456
                                        case Encode_IdentityH:
457
                                        default:
458
                                                m_glyphBuffer += Pdf::toHexString16(gid) + " Tj\n";
459
                                                break;
460
                                        }
461
                                }
21107 craig 462
                        }
23072 jghali 463
                        current_x += gl.xadvance * gl.scaleH;
21107 craig 464
                }
465
        }
466
 
23073 jghali 467
        void drawLine(QPointF start, QPointF end) override
21107 craig 468
        {
469
                QTransform transform = matrix();
470
                transform.translate(x(), y());
471
                m_pathBuffer += "q\n";
472
                m_pathBuffer += transformToStr(transform) + " cm\n";
473
                m_pathBuffer += m_pdf->putColor(strokeColor().color, strokeColor().shade, false);
23561 jghali 474
                m_pathBuffer += FToStr(strokeWidth()) + " w\n";
21107 craig 475
                m_pathBuffer += FToStr(start.x()) + " " + FToStr(-start.y()) + " m\n";
476
                m_pathBuffer += FToStr(end.x()) + " " + FToStr(-end.y()) + " l\n";
477
                m_pathBuffer += "S\n";
478
                m_pathBuffer += "Q\n";
479
        }
480
 
481
        QByteArray getBuffer()
482
        {
22987 jghali 483
                return m_backBuffer + "BT\n" + m_glyphBuffer + "ET\n" + m_pathBuffer;
21107 craig 484
        }
485
 
23073 jghali 486
        void drawRect(QRectF rect) override
21107 craig 487
        {
488
                QTransform transform = matrix();
489
//              transform.translate(x(), y());
490
                double rectX = x() + rect.x();
491
                double rectY = -y() - rect.y();
22987 jghali 492
                m_backBuffer += "q\n";
493
                m_backBuffer += transformToStr(transform) + " cm\n";
494
                m_backBuffer += "n\n";
495
                m_backBuffer += m_pdf->putColor(fillColor().color, fillColor().shade, true);
496
                m_backBuffer += m_pdf->putColor(strokeColor().color, strokeColor().shade, false);
497
                m_backBuffer += FToStr(rectX) + " " + FToStr(rectY) + " m\n";
498
                m_backBuffer += FToStr(rectX + rect.width()) + " " + FToStr(rectY) + " l\n";
499
                m_backBuffer += FToStr(rectX + rect.width()) + " " + FToStr(rectY - rect.height()) + " l\n";
500
                m_backBuffer += FToStr(rectX) + " " + FToStr(rectY - rect.height()) + " l\n";
501
                m_backBuffer += "h\nf\n";
502
                m_backBuffer += "Q\n";
21107 craig 503
        }
504
 
23073 jghali 505
        void drawObject(PageItem* embedded) override
21107 craig 506
        {
23561 jghali 507
                m_glyphBuffer += "ET\n" + m_pathBuffer;
22719 jghali 508
                m_pathBuffer.clear();
21107 craig 509
 
510
                m_pathBuffer += "q\n";
21301 fschmid 511
                m_pathBuffer += FToStr(scaleH()) + " 0 0 " + FToStr(scaleV()) + " " + FToStr(x() + embedded->gXpos) + " " + FToStr(-(y() + embedded->gYpos)) + " cm\n";
21107 craig 512
 
513
                QByteArray output;
514
                if (!m_pdf->PDF_ProcessItem(output, embedded, m_page, m_PNr, true))
515
                        return;
516
                m_pathBuffer += output;
517
                m_pathBuffer += "Q\n";
23561 jghali 518
                m_glyphBuffer += m_pathBuffer + "\n";
22719 jghali 519
                m_pathBuffer.clear();
21107 craig 520
 
22098 jghali 521
                m_prevFontName.clear();
522
                m_prevFontSize = -1;
21107 craig 523
                m_glyphBuffer += "BT\n";
524
        }
525
};
526
 
10054 craig 527
PDFLibCore::PDFLibCore(ScribusDoc & docu)
23825 jghali 528
        : PDFLibCore(docu, docu.pdfOptions())
529
{
530
 
531
}
532
 
533
PDFLibCore::PDFLibCore(ScribusDoc & docu, const PDFOptions& options)
4264 craig 534
        : QObject(&docu),
4223 craig 535
        doc(docu),
22600 craig 536
        ActPageP(nullptr),
23825 jghali 537
        Options(options),
22600 craig 538
        Bvie(nullptr),
539
        ucs2Codec(nullptr),
4223 craig 540
        ResNam("RE"),
541
        ResCount(0),
542
        NDnam("LI"),
543
        NDnum(0),
544
        spotNam("Spot"),
545
        spotCount(0),
22600 craig 546
        progressDialog(nullptr),
4223 craig 547
        abortExport(false),
20089 jghali 548
        usingGUI(ScCore->usingGUI()),
549
        bleedDisplacementX(0),
550
        bleedDisplacementY(0)
3133 fschmid 551
{
552
        Catalog.Outlines = 2;
553
        Catalog.PageTree = 3;
554
        Catalog.Dest = 4;
555
        Outlines.First = 0;
556
        Outlines.Last = 0;
557
        Outlines.Count = 0;
20103 avox 558
        pageData.ObjNum = 0;
559
        pageData.Thumb = 0;
23825 jghali 560
 
4028 cbradney 561
        if (usingGUI)
562
        {
10508 cbradney 563
                progressDialog = new MultiProgressDialog( tr("Saving PDF"), CommonStrings::tr_Cancel, doc.scMW());
4224 craig 564
                Q_CHECK_PTR(progressDialog);
565
                QStringList barNames, barTexts;
566
                barNames << "EMP" << "EP" << "ECPI";
5370 cbradney 567
                barTexts << tr("Exporting Master Page:") << tr("Exporting Page:") << tr("Exporting Items on Current Page:");
9803 fschmid 568
                QList<bool> barsNumeric;
5370 cbradney 569
                barsNumeric << true << true << false;
570
                progressDialog->addExtraProgressBars(barNames, barTexts, barsNumeric);
8559 subik 571
                connect(progressDialog, SIGNAL(canceled()), this, SLOT(cancelRequested()));
4028 cbradney 572
        }
3133 fschmid 573
}
574
 
10054 craig 575
PDFLibCore::~PDFLibCore()
4224 craig 576
{
577
        delete progressDialog;
578
}
579
 
4229 craig 580
 
19115 avox 581
bool PDFLibCore::PDF_IsPDFX()
582
{
23548 jghali 583
        if (Options.Version == PDFVersion::PDF_X1a)
19115 avox 584
                return true;
23548 jghali 585
        if (Options.Version == PDFVersion::PDF_X3)
19115 avox 586
                return true;
23548 jghali 587
        if (Options.Version == PDFVersion::PDF_X4)
19115 avox 588
                return true;
589
        return false;
590
}
591
 
23548 jghali 592
bool PDFLibCore::PDF_IsPDFX(const PDFVersion& ver)
19115 avox 593
{
23548 jghali 594
        if (ver == PDFVersion::PDF_X1a)
19115 avox 595
                return true;
23548 jghali 596
        if (ver == PDFVersion::PDF_X3)
19115 avox 597
                return true;
23548 jghali 598
        if (ver == PDFVersion::PDF_X4)
19115 avox 599
                return true;
600
        return false;
601
}
602
 
23825 jghali 603
bool PDFLibCore::doExport(const QString& fn, const std::vector<int> & pageNs, const QMap<int, QImage> & thumbs)
3133 fschmid 604
{
22459 jghali 605
        QImage thumb;
11060 jghali 606
        bool ret = false, error = false;
607
        int  pc_exportpages=0;
608
        int  pc_exportmasterpages=0;
4224 craig 609
        if (usingGUI)
610
                progressDialog->show();
23560 jghali 611
 
19114 avox 612
        ucs2Codec = QTextCodec::codecForName("ISO-10646-UCS-2");
17758 jghali 613
        if (!ucs2Codec)
19114 avox 614
                ucs2Codec = QTextCodec::codecForName("UTF-16");
615
        if (!ucs2Codec)
13764 jghali 616
        {
13791 pierre 617
                PDF_Error( tr("Qt build miss both \"UTF-16\" and \"ISO-10646-UCS-2\" text codecs, pdf export is not possible") );
13764 jghali 618
                return false;
619
        }
23560 jghali 620
 
621
        QMap<QString, QMap<uint, FPointArray> > usedFonts;
622
        doc.getUsedFonts(usedFonts);
623
 
23060 craig 624
        if (PDF_Begin_Doc(fn, PrefsManager::instance().appPrefs.fontPrefs.AvailFonts, usedFonts, doc.scMW()->bookmarkPalette->BView))
3133 fschmid 625
        {
626
                QMap<int, int> pageNsMpa;
627
                for (uint a = 0; a < pageNs.size(); ++a)
628
                {
23563 jghali 629
                        pageNsMpa.insert(doc.MasterNames[doc.DocPages.at(pageNs[a] - 1)->masterPageName()], 0);
3133 fschmid 630
                }
4028 cbradney 631
                if (usingGUI)
3133 fschmid 632
                {
23563 jghali 633
                        progressDialog->setOverallTotalSteps(pageNsMpa.count() + pageNs.size());
4028 cbradney 634
                        progressDialog->setTotalSteps("EMP", pageNsMpa.count());
635
                        progressDialog->setTotalSteps("EP", pageNs.size());
636
                        progressDialog->setOverallProgress(0);
637
                        progressDialog->setProgress("EMP", 0);
638
                        progressDialog->setProgress("EP", 0);
639
                }
10018 fschmid 640
                for (int ap = 0; ap < doc.MasterPages.count() && !abortExport; ++ap)
4028 cbradney 641
                {
4264 craig 642
                        if (doc.MasterItems.count() != 0)
3133 fschmid 643
                        {
644
                                if (pageNsMpa.contains(ap))
645
                                {
5243 cbradney 646
                                        qApp->processEvents();
11060 jghali 647
                                        if (!PDF_TemplatePage(doc.MasterPages.at(ap)))
648
                                                error = abortExport = true;
4028 cbradney 649
                                        ++pc_exportmasterpages;
3133 fschmid 650
                                }
651
                        }
4546 subik 652
                        if (usingGUI)
653
                        {
4028 cbradney 654
                                progressDialog->setProgress("EMP", pc_exportmasterpages);
655
                                progressDialog->setOverallProgress(pc_exportmasterpages+pc_exportpages);
656
                        }
3133 fschmid 657
                }
4028 cbradney 658
                for (uint a = 0; a < pageNs.size() && !abortExport; ++a)
3133 fschmid 659
                {
23829 jghali 660
                        if (Options.Thumbnails)
22459 jghali 661
                                thumb = thumbs[pageNs[a]];
5243 cbradney 662
                        qApp->processEvents();
4028 cbradney 663
                        if (abortExport) break;
11060 jghali 664
 
22459 jghali 665
                        PDF_Begin_Page(doc.DocPages.at(pageNs[a]-1), thumb);
5243 cbradney 666
                        qApp->processEvents();
4028 cbradney 667
                        if (abortExport) break;
11060 jghali 668
 
23829 jghali 669
                        if (!PDF_ProcessPage(doc.DocPages.at(pageNs[a]-1), pageNs[a]-1, Options.doClip))
11060 jghali 670
                                error = abortExport = true;
5243 cbradney 671
                        qApp->processEvents();
4028 cbradney 672
                        if (abortExport) break;
11060 jghali 673
 
22434 jghali 674
                        PDF_End_Page();
4028 cbradney 675
                        pc_exportpages++;
676
                        if (usingGUI)
677
                        {
678
                                progressDialog->setProgress("EP", pc_exportpages);
679
                                progressDialog->setOverallProgress(pc_exportmasterpages+pc_exportpages);
680
                        }
3133 fschmid 681
                }
20681 craig 682
                ret = true;//Even when aborting we return true. Don't want that "couldnt write msg"
4028 cbradney 683
                if (!abortExport)
684
                {
23829 jghali 685
                        if (PDF_IsPDFX(Options.Version))
686
                                ret = PDF_End_Doc(ScCore->PrinterProfiles[Options.PrintProf]);
4028 cbradney 687
                        else
12387 jghali 688
                                ret = PDF_End_Doc();
4028 cbradney 689
                }
3133 fschmid 690
                else
4028 cbradney 691
                        closeAndCleanup();
3133 fschmid 692
        }
4029 cbradney 693
        if (usingGUI)
694
                progressDialog->close();
11060 jghali 695
        return (ret && !error);
3133 fschmid 696
}
697
 
22600 craig 698
const QString& PDFLibCore::errorMessage() const
11060 jghali 699
{
700
        return ErrorMessage;
701
}
702
 
22600 craig 703
bool PDFLibCore::exportAborted() const
11441 jghali 704
{
705
        return abortExport;
706
}
707
 
20103 avox 708
//#define StartObj(n) writer.startObj((n))
709
#define PutDoc(s) writer.write(s)
710
//#define newObject() writer.newObject()
3133 fschmid 711
 
712
 
20103 avox 713
//void PDFLibCore::StartObj(PdfId nr)
714
//{
715
//      for (int i=XRef.size(); i < nr; ++i)
716
//              XRef.append(0);
717
//      XRef[nr-1] = bytesWritten();
23563 jghali 718
//      PutDoc(Pdf::toPdf(nr) + " 0 obj\n");
20103 avox 719
//}
720
 
721
//// Encode a string for inclusion in a
722
//// PDF (literal) .
723
//static QByteArray PDFEncode(const QString & in)
724
//{
725
//      QByteArray tmp("");
726
//      for (int d = 0; d < in.length(); ++d)
727
//      {
728
//              QChar cc(in.at(d));
729
//              if ((cc == '(') || (cc == ')') || (cc == '\\'))
730
//                      tmp += '\\';
731
//              else if ((cc == '\r') || (cc == '\n'))
732
//              {
733
//                      tmp += (cc == '\r') ? "\\r" : "\\n";
734
//                      continue;
735
//              }
736
//              tmp += cc;
737
//      }
738
//      return tmp;
739
//}
740
 
741
static QByteArray blendMode(int code)
12080 avox 742
{
743
        switch (code)
744
        {
745
                case 0:
746
                        return("Normal");
747
                        break;
748
                case 1:
749
                        return("Darken");
750
                        break;
751
                case 2:
752
                        return("Lighten");
753
                        break;
754
                case 3:
755
                        return("Multiply");
756
                        break;
757
                case 4:
758
                        return("Screen");
759
                        break;
760
                case 5:
761
                        return("Overlay");
762
                        break;
763
                case 6:
764
                        return("HardLight");
765
                        break;
766
                case 7:
767
                        return("SoftLight");
768
                        break;
769
                case 8:
770
                        return("Difference");
771
                        break;
772
                case 9:
773
                        return("Exclusion");
774
                        break;
775
                case 10:
776
                        return("ColorDodge");
777
                        break;
778
                case 11:
779
                        return("ColorBurn");
780
                        break;
781
                case 12:
782
                        return("Hue");
783
                        break;
784
                case 13:
785
                        return("Saturation");
786
                        break;
787
                case 14:
788
                        return("Color");
789
                        break;
790
                case 15:
791
                        return("Luminosity");
792
                        break;
793
                default:
794
                        return "";
795
        }
14043 jghali 796
}
20103 avox 797
//
798
//QByteArray PDFLibCore::EncodeUTF16(const QString &in)
799
//{
800
//      QString tmp = in;
801
//      QByteArray cres = ucs2Codec->fromUnicode( tmp );
802
//#ifndef WORDS_BIGENDIAN
803
//      // on little endian systems we need to swap bytes:
804
//      uchar sw;
23563 jghali 805
//      for (int d = 0; d < cres.size() - 1; d += 2)
20103 avox 806
//      {
807
//              sw = cres[d];
808
//              cres[d] = cres[d+1];
809
//              cres[d+1] = sw;
810
//      }
811
//#endif
812
//      return cres;
813
//}
12080 avox 814
 
20103 avox 815
QByteArray PDFLibCore::EncStream(const QByteArray & in, PdfId ObjNum)
19114 avox 816
{
817
        if (in.length() < 1)
20103 avox 818
                return QByteArray();
22638 craig 819
        if (!Options.Encrypt)
19114 avox 820
                return in;
22638 craig 821
        return writer.encryptBytes(in, ObjNum);
20103 avox 822
//      rc4_context_t rc4;
823
//      QByteArray tmp(in);
824
//      QByteArray us(tmp.length(), ' ');
825
//      QByteArray ou(tmp.length(), ' ');
826
//      for (int a = 0; a < tmp.length(); ++a)
20112 jghali 827
//              us[a] = (tmp.at(a));
828
//      QByteArray step1 = writer.ComputeRC4Key(ObjNum);
20103 avox 829
//      rc4_init(&rc4, reinterpret_cast<uchar*>(step1.data()), qMin(KeyLen+5, 16));
830
//      rc4_encrypt(&rc4, reinterpret_cast<uchar*>(us.data()), reinterpret_cast<uchar*>(ou.data()), tmp.length());
831
//      QString uk = "";
832
//      for (int cl = 0; cl < tmp.length(); ++cl)
833
//              uk += QChar(ou[cl]);
834
//      return ou;
19114 avox 835
}
836
 
20103 avox 837
QByteArray PDFLibCore::EncString(const QByteArray & in, PdfId ObjNum)
19114 avox 838
{
839
        if (in.length() < 1)
840
                return "<>";
841
        if (!Options.Encrypt)
842
        {
20103 avox 843
                //tmp = "(" + PDFEncode(in) + ")";
20112 jghali 844
                return Pdf::toLiteralString(in);
19114 avox 845
        }
20112 jghali 846
 
20103 avox 847
//      rc4_context_t rc4;
848
//      QByteArray us(in.length(), ' ');
849
//      QByteArray ou(in.length(), ' ');
850
//      for (int a = 0; a < in.length(); ++a)
851
//              us[a] = static_cast<uchar>(QChar(in.at(a)).cell());
852
//      QByteArray step1 = writer.ComputeRC4Key(ObjNum);
853
//      rc4_init(&rc4, reinterpret_cast<uchar*>(step1.data()), qMin(KeyLen+5, 16));
854
//      rc4_encrypt(&rc4, reinterpret_cast<uchar*>(us.data()), reinterpret_cast<uchar*>(ou.data()), in.length());
855
//      QString uk = "";
856
//      for (int cl = 0; cl < in.length(); ++cl)
857
//              uk += QChar(ou[cl]);
20112 jghali 858
 
859
        return Pdf::toHexString(writer.encryptBytes(in, ObjNum));
19114 avox 860
}
861
 
20103 avox 862
QByteArray PDFLibCore::EncStringUTF16(const QString & in, PdfId ObjNum)
19114 avox 863
{
864
        if (in.length() < 1)
865
                return "<>";
866
        if (!Options.Encrypt)
867
        {
20112 jghali 868
                QByteArray us = Pdf::toUTF16(in);
20103 avox 869
//              QString uk = "";
870
//              for (int cl = 0; cl < us.size(); ++cl)
871
//                      uk += QChar(us[cl]);
872
                return Pdf::toHexString(us);
19114 avox 873
        }
20103 avox 874
//      rc4_context_t rc4;
20112 jghali 875
        QByteArray us = Pdf::toUTF16(in);
20103 avox 876
//      QByteArray ou(us.size(), ' ');
877
//      QByteArray step1 = writer.ComputeRC4Key(ObjNum);
878
//      rc4_init(&rc4, reinterpret_cast<uchar*>(step1.data()), qMin(KeyLen+5, 16));
879
//      rc4_encrypt(&rc4, reinterpret_cast<uchar*>(us.data()), reinterpret_cast<uchar*>(ou.data()), ou.size());
880
//      QString uk = "";
881
//      for (int cl = 0; cl < ou.size(); ++cl)
882
//              uk += QChar(ou[cl]);
883
        QByteArray tmp = Pdf::toHexString(writer.encryptBytes(us, ObjNum));
19114 avox 884
        return tmp;
885
}
886
 
20103 avox 887
bool PDFLibCore::EncodeArrayToStream(const QByteArray& in, PdfId ObjNum)
19114 avox 888
{
889
        if (in.size() < 1)
890
                return true;
891
        bool succeed = false;
892
        if (Options.Encrypt)
893
        {
20103 avox 894
                ScStreamFilter* rc4Encode = writer.openStreamFilter(true, ObjNum);
895
                if (rc4Encode->openFilter())
19114 avox 896
                {
20103 avox 897
                        succeed  = rc4Encode->writeData(in.data(), in.size());
898
                        succeed &= rc4Encode->closeFilter();
19114 avox 899
                }
20112 jghali 900
                delete rc4Encode;
19114 avox 901
        }
902
        else
20103 avox 903
                writer.write(in);
904
        return (writer.getOutStream().status() == QDataStream::Ok);
19114 avox 905
}
906
 
20103 avox 907
int PDFLibCore::WriteImageToStream(ScImage& image, PdfId ObjNum, ColorSpaceEnum format, bool precal)
11599 jghali 908
{
17758 jghali 909
        bool fromCmyk, succeed = false;
11599 jghali 910
        int  bytesWritten = 0;
20103 avox 911
 
20112 jghali 912
        ScStreamFilter* rc4Encode = writer.openStreamFilter(Options.Encrypt, ObjNum);
913
        if (rc4Encode->openFilter())
914
        {
915
                switch (format)
916
                {
917
                        case ColorSpaceMonochrome :
918
                                fromCmyk = !Options.UseRGB && !Options.isGrayscale && !(doc.HasCMS && Options.UseProfiles2);
919
                                succeed = image.writeMonochromeDataToFilter(rc4Encode, fromCmyk); break;
920
                        case ColorSpaceGray :
921
                                succeed = image.writeGrayDataToFilter(rc4Encode, precal); break;
922
                        case ColorSpaceCMYK :
923
                                succeed = image.writeCMYKDataToFilter(rc4Encode); break;
924
                        default :
925
                                succeed = image.writeRGBDataToFilter(rc4Encode); break;
926
                }
927
                succeed &= rc4Encode->closeFilter();
928
                bytesWritten = rc4Encode->writtenToStream();
929
                delete rc4Encode;
930
        }
11599 jghali 931
        return (succeed ? bytesWritten : 0);
932
}
933
 
20103 avox 934
int PDFLibCore::WriteJPEGImageToStream(ScImage& image, const QString& fn, PdfId ObjNum, int quality, ColorSpaceEnum format,
17758 jghali 935
                                                                                 bool sameFile, bool precal)
11599 jghali 936
{
937
        bool succeed = true;
938
        int  bytesWritten = 0;
939
        QFileInfo fInfo(fn);
940
        QString   ext = fInfo.suffix().toLower();
941
        QString   jpgFileName, tmpFile;
942
        if (extensionIndicatesJPEG(ext) && sameFile)
943
                jpgFileName = fn;
11445 jghali 944
        else
11599 jghali 945
        {
21526 craig 946
                tmpFile  = QDir::toNativeSeparators(ScPaths::tempFileDir() + "sc.jpg");
17758 jghali 947
                if (format == ColorSpaceGray && (!precal))
11599 jghali 948
                        image.convertToGray();
17758 jghali 949
                if (image.convert2JPG(tmpFile, quality, format == ColorSpaceCMYK, format == ColorSpaceGray))
11599 jghali 950
                        jpgFileName = tmpFile;
951
        }
952
        if (jpgFileName.isEmpty())
11792 fschmid 953
                return 0;
11599 jghali 954
        if (Options.Encrypt)
955
        {
956
                succeed = false;
20112 jghali 957
                ScStreamFilter* rc4Encode = writer.openStreamFilter(true, ObjNum);
958
                if (rc4Encode->openFilter())
11599 jghali 959
                {
20103 avox 960
                        succeed  = copyFileToFilter(jpgFileName, *rc4Encode);
961
                        succeed &= rc4Encode->closeFilter();
962
                        bytesWritten = rc4Encode->writtenToStream();
11599 jghali 963
                }
20112 jghali 964
                delete rc4Encode;
11599 jghali 965
        }
966
        else
967
        {
20103 avox 968
                succeed &= copyFileToStream(jpgFileName, writer.getOutStream());
11599 jghali 969
                QFileInfo jpgInfo(jpgFileName);
970
                bytesWritten = jpgInfo.size();
971
        }
972
        if (!tmpFile.isEmpty() && QFile::exists(tmpFile))
973
                QFile::remove(tmpFile);
974
        return (succeed ? bytesWritten : 0);
11445 jghali 975
}
976
 
20103 avox 977
int PDFLibCore::WriteFlateImageToStream(ScImage& image, PdfId ObjNum, ColorSpaceEnum format, bool precal)
11599 jghali 978
{
20112 jghali 979
        bool fromCmyk, succeed = false;
980
        int  bytesWritten = 0;
981
        ScStreamFilter* rc4Encode = writer.openStreamFilter(Options.Encrypt, ObjNum);
982
        ScFlateEncodeFilter flateEncode(rc4Encode);
983
        if (flateEncode.openFilter())
984
        {
985
                switch (format)
986
                {
987
                        case ColorSpaceMonochrome :
988
                                fromCmyk = !Options.UseRGB && !Options.isGrayscale && !(doc.HasCMS && Options.UseProfiles2);
989
                                succeed = image.writeMonochromeDataToFilter(&flateEncode, fromCmyk); break;
990
                        case ColorSpaceGray :
991
                                succeed = image.writeGrayDataToFilter(&flateEncode, precal); break;
992
                        case ColorSpaceCMYK :
993
                                succeed = image.writeCMYKDataToFilter(&flateEncode); break;
994
                        default :
995
                                succeed = image.writeRGBDataToFilter(&flateEncode); break;
996
                }
997
                succeed &= flateEncode.closeFilter();
998
                bytesWritten = flateEncode.writtenToStream();
999
        }
1000
        delete rc4Encode;
1001
        return (succeed ? bytesWritten : 0);
11599 jghali 1002
}
1003
 
23924 jghali 1004
bool PDFLibCore::PDF_Begin_Doc(const QString& fn, SCFonts &AllFonts, const QMap<QString, QMap<uint, FPointArray> >& DocFonts, BookmarkView* vi)
19114 avox 1005
{
20112 jghali 1006
        if (!writer.open(fn))
1007
                return false;
1008
 
6407 fschmid 1009
        inPattern = 0;
3133 fschmid 1010
        Bvie = vi;
1011
        BookMinUse = false;
1012
        UsedFontsP.clear();
8454 fschmid 1013
        UsedFontsF.clear();
20112 jghali 1014
 
1015
        writer.writeHeader(Options.Version);
20103 avox 1016
 
20112 jghali 1017
        PDF_Begin_Catalog();
1018
        PDF_Begin_MetadataAndEncrypt();
1019
        PDF_Begin_WriteUsedFonts(AllFonts, PDF_Begin_FindUsedFonts(AllFonts, DocFonts));
1020
        PDF_Begin_Colors();
1021
        PDF_Begin_Layers();
1022
 
1023
        return true;
20103 avox 1024
}
1025
 
1026
void PDFLibCore::PDF_Begin_Catalog()
1027
{
20112 jghali 1028
        writer.startObj(writer.CatalogObj);
22386 jghali 1029
        PutDoc("<<\n/Type /Catalog\n");
1030
        PutDoc("/Pages " + Pdf::toObjRef(writer.PagesObj) + "\n");
1031
        if (!PDF_IsPDFX())
1032
        {
1033
                if (doc.useAcroFormFields())
1034
                {
1035
                        writer.AcroFormObj = writer.newObject();
1036
                        PutDoc("/AcroForm " + Pdf::toObjRef(writer.AcroFormObj) + "\n");
1037
                }
1038
                if (doc.useAnnotations())
1039
                {
1040
                        writer.DestsObj = writer.newObject();
1041
                        PutDoc("/Dests " + Pdf::toObjRef(writer.DestsObj) + "\n");
1042
                }
1043
                if (doc.JavaScripts.count() > 0)
1044
                {
1045
                        writer.NamesObj = writer.newObject();
1046
                        PutDoc("/Names " + Pdf::toObjRef(writer.NamesObj) + "\n");
1047
                }
1048
        }
1049
        if (Options.Bookmarks)
1050
        {
1051
                writer.OutlinesObj = writer.newObject();
1052
                PutDoc("/Outlines " + Pdf::toObjRef(writer.OutlinesObj) + "\n");
1053
        }
1054
        if (Options.Articles)
1055
        {
1056
                writer.ThreadsObj = writer.newObject();
1057
                PutDoc("/Threads " + Pdf::toObjRef(writer.ThreadsObj) + "\n");
1058
        }
23548 jghali 1059
        if (Options.exportsLayers())
20112 jghali 1060
        {
1061
                writer.OCPropertiesObj = writer.newObject();
20103 avox 1062
                PutDoc("/OCProperties " + Pdf::toObjRef(writer.OCPropertiesObj) + "\n");
20112 jghali 1063
        }
1064
        if (PDF_IsPDFX())
19114 avox 1065
        {
20112 jghali 1066
                writer.OutputIntentObj = writer.newObject();
23561 jghali 1067
                PutDoc("/OutputIntents [ " + Pdf::toObjRef(writer.OutputIntentObj) + " ]\n");
20112 jghali 1068
        }
23548 jghali 1069
        if ((Options.Version == PDFVersion::PDF_X4))
20112 jghali 1070
        {
1071
                writer.MetaDataObj = writer.newObject();
23561 jghali 1072
                PutDoc("/Metadata " + Pdf::toObjRef(writer.MetaDataObj) + "\n");
19114 avox 1073
        }
1074
        PutDoc("/PageLayout ");
1075
        switch (Options.PageLayout)
1076
        {
1077
                case PDFOptions::SinglePage:
1078
                        PutDoc("/SinglePage\n");
1079
                        break;
1080
                case PDFOptions::OneColumn:
1081
                        PutDoc("/OneColumn\n");
1082
                        break;
1083
                case PDFOptions::TwoColumnLeft:
1084
                        PutDoc("/TwoColumnLeft\n");
1085
                        break;
1086
                case PDFOptions::TwoColumnRight:
1087
                        PutDoc("/TwoColumnRight\n");
1088
                        break;
1089
        }
1090
        if (Options.displayBookmarks)
1091
                PutDoc("/PageMode /UseOutlines\n");
1092
        else if (Options.displayFullscreen)
1093
                PutDoc("/PageMode /FullScreen\n");
1094
        else if (Options.displayThumbs)
1095
                PutDoc("/PageMode /UseThumbs\n");
23548 jghali 1096
        else if ((Options.Version == PDFVersion::PDF_15 || Options.Version == PDFVersion::PDF_16) && (Options.displayLayers))
19114 avox 1097
                        PutDoc("/PageMode /UseOC\n");
1098
        if (!Options.openAction.isEmpty())
1099
        {
22389 jghali 1100
                writer.OpenActionObj = writer.newObject();
1101
                PutDoc("/OpenAction << /S /JavaScript /JS " + Pdf::toPdf(writer.OpenActionObj) + " 0 R >>\n");
19114 avox 1102
        }
21942 craig 1103
        QDateTime dt = QDateTime::currentDateTimeUtc();
20112 jghali 1104
        Datum = Pdf::toDateString(dt);
20103 avox 1105
//    "D:";
1106
//      tmp.sprintf("%4d", d.year());
1107
//      tmp.replace(QRegExp(" "), "0");
1108
//      Datum += tmp;
1109
//      tmp.sprintf("%2d", d.month());
1110
//      tmp.replace(QRegExp(" "), "0");
1111
//      Datum += tmp;
1112
//      tmp.sprintf("%2d", d.day());
1113
//      tmp.replace(QRegExp(" "), "0");
1114
//      Datum += tmp;
1115
//      tmp = dt.time().toString();
1116
//      tmp.replace(QRegExp(":"), "");
1117
//      Datum += tmp;
1118
//      Datum += "Z";
12429 fschmid 1119
 
19114 avox 1120
        // only include XMP to PDF/X-4 at the moment, could easily be extended to include it to any PDF
23548 jghali 1121
        if (Options.Version == PDFVersion::PDF_X4)
19114 avox 1122
                generateXMP(dt.toString("yyyy-MM-ddThh:mm:ssZ"));
1123
 
1124
/* The following code makes the resulting PDF "Reader enabled" in Acrobat Reader 8
1125
   but sadly it doesn't work with newer version, because its based on a bug in AR 8
1126
        PutDoc("/Perms\n");
1127
        PutDoc("<<\n");
1128
        PutDoc("/UR3\n");
1129
        PutDoc("<<\n");
23561 jghali 1130
        PutDoc("/M (" + Datum + ")\n");
1131
        PutDoc("/Name (Scribus " + QString(VERSION) + ")\n");
19114 avox 1132
        PutDoc("/Reference [\n");
1133
        PutDoc("<<\n");
1134
        PutDoc("/TransformParams\n");
1135
        PutDoc("<<\n");
1136
        PutDoc("/Type /TransformParams\n");
1137
        PutDoc("/V /2.2\n");
1138
        PutDoc("/Document [/FullSave]\n");
1139
        PutDoc("/Annots [/Create/Delete/Modify/Copy/Import/Export]\n");
1140
        PutDoc("/Form [/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate]\n");
1141
        PutDoc("/Signature [/Modify]\n");
1142
        PutDoc(">>\n");
1143
        PutDoc("/TransformMethod /UR3\n");
1144
        PutDoc("/Type /SigRef\n");
1145
        PutDoc(">>\n");
1146
        PutDoc("]\n");
1147
        PutDoc("/Type /Sig\n");
1148
        PutDoc(">>\n");
1149
        PutDoc(">>\n");
1150
*/
1151
 
1152
        PutDoc("/ViewerPreferences\n<<\n/PageDirection ");
1153
        PutDoc( Options.Binding == 0 ? "/L2R\n" : "/R2L\n");
1154
        if (Options.hideToolBar)
1155
                PutDoc("/HideToolbar true\n");
1156
        if (Options.hideMenuBar)
1157
                PutDoc("/HideMenubar true\n");
1158
        if (Options.fitWindow)
1159
                PutDoc("/FitWindow true\n");
20112 jghali 1160
        PutDoc(" >>\n>>");
1161
        writer.endObj(writer.CatalogObj);
20103 avox 1162
}
1163
 
1164
void PDFLibCore::PDF_Begin_MetadataAndEncrypt()
1165
{
19114 avox 1166
        QString IDg(Datum);
1167
        IDg += Options.fileName;
23561 jghali 1168
        IDg += "Scribus " + QString(VERSION);
1169
        IDg += "Scribus PDF Library " + QString(VERSION);
19114 avox 1170
        IDg += doc.documentInfo().title();
1171
        IDg += doc.documentInfo().author();
1172
        IDg += "/False";
20112 jghali 1173
        writer.setFileId(Pdf::toPdfDocEncoding(IDg));
19114 avox 1174
        if (Options.Encrypt)
1175
        {
23548 jghali 1176
                writer.setEncryption(Options.Version.supports128BitsEncryption(), Pdf::toPdfDocEncoding(Options.PassOwner), Pdf::toPdfDocEncoding(Options.PassUser), Options.Permissions);
19114 avox 1177
        }
20103 avox 1178
        writer.startObj(writer.InfoObj);
20112 jghali 1179
        PutDoc("<<\n/Creator " + EncString(QByteArray("Scribus ") + VERSION, writer.InfoObj) + "\n");
20103 avox 1180
        PutDoc("/Producer " + EncString(QByteArray("Scribus PDF Library ") + VERSION, writer.InfoObj) + "\n");
19114 avox 1181
        QString docTitle = doc.documentInfo().title();
19115 avox 1182
        if ((PDF_IsPDFX()) && (docTitle.isEmpty()))
22832 craig 1183
                PutDoc("/Title " + EncStringUTF16(doc.documentFileName(), writer.InfoObj) + "\n");
19114 avox 1184
        else
20103 avox 1185
                PutDoc("/Title " + EncStringUTF16(doc.documentInfo().title(), writer.InfoObj) + "\n");
1186
        PutDoc("/Author " + EncStringUTF16(doc.documentInfo().author(), writer.InfoObj) + "\n");
1187
        PutDoc("/Subject " + EncStringUTF16(doc.documentInfo().subject(), writer.InfoObj) + "\n");
1188
        PutDoc("/Keywords " + EncStringUTF16(doc.documentInfo().keywords(), writer.InfoObj) + "\n");
1189
        PutDoc("/CreationDate " + EncString(Datum, writer.InfoObj) + "\n");
1190
        PutDoc("/ModDate " + EncString(Datum, writer.InfoObj) + "\n");
23548 jghali 1191
        if (Options.Version == PDFVersion::PDF_X1a)
19114 avox 1192
        {
1193
                PutDoc("/GTS_PDFXVersion (PDF/X-1:2001)\n");
1194
                PutDoc("/GTS_PDFXConformance (PDF/X-1a:2001)\n");
1195
        }
23548 jghali 1196
        if (Options.Version == PDFVersion::PDF_X3)
19115 avox 1197
                PutDoc("/GTS_PDFXVersion (PDF/X-3:2002)\n");
23548 jghali 1198
        if (Options.Version == PDFVersion::PDF_X4)
19114 avox 1199
                PutDoc("/GTS_PDFXVersion (PDF/X-4)\n");
20112 jghali 1200
        PutDoc("/Trapped /False\n>>");
1201
        writer.endObj(writer.InfoObj);
20103 avox 1202
}
1203
 
1204
QMap<QString, QMap<uint, FPointArray> >
1205
PDFLibCore::PDF_Begin_FindUsedFonts(SCFonts &AllFonts, const QMap<QString, QMap<uint, FPointArray> >& DocFonts)
1206
{
5387 avox 1207
        QMap<QString, QMap<uint, FPointArray> > ReallyUsed;
3133 fschmid 1208
        ReallyUsed.clear();
1209
        PageItem* pgit;
20103 avox 1210
        QMap<int, QByteArray> ind2PDFabr;
1211
        const QByteArray tmpf[] = {"/Courier", "/Courier-Bold", "/Courier-Oblique", "/Courier-BoldOblique",
4017 fschmid 1212
                                                                                                "/Helvetica", "/Helvetica-Bold", "/Helvetica-Oblique", "/Helvetica-BoldOblique",
4546 subik 1213
                                                                                                "/Times-Roman", "/Times-Bold", "/Times-Italic", "/Times-BoldItalic",
4017 fschmid 1214
                                                                                                "/ZapfDingbats", "/Symbol"};
1215
        size_t ar = sizeof(tmpf) / sizeof(*tmpf);
1216
        for (uint ax = 0; ax < ar; ++ax)
1217
                ind2PDFabr[ax] = tmpf[ax];
21709 jghali 1218
 
1219
        QList<PageItem*> allItems = doc.FrameItems.values();
1220
        while (allItems.count() > 0)
3133 fschmid 1221
        {
21709 jghali 1222
                pgit = allItems.takeFirst();
1223
                if (pgit->isGroup() || pgit->isTable())
3133 fschmid 1224
                {
22224 jghali 1225
                        allItems = pgit->getChildren() + allItems;
21709 jghali 1226
                        continue;
1227
                }
1228
                if ((pgit->itemType() == PageItem::TextFrame) || (pgit->itemType() == PageItem::PathText))
1229
                {
1230
                        if (pgit->isAnnotation())
17312 fschmid 1231
                        {
21709 jghali 1232
                                int annotType  = pgit->annotation().Type();
1233
                                bool mustEmbed = ((annotType >= Annotation::Button) && (annotType <= Annotation::Listbox) && (annotType != Annotation::Checkbox));
1234
                                if ((pgit->annotation().Type() == Annotation::Checkbox) || (pgit->annotation().Type() == Annotation::RadioButton))
1235
                                        StdFonts.insert("/ZapfDingbats", "");
1236
                                if (pgit->itemText.length() > 0 || mustEmbed)
17312 fschmid 1237
                                {
23548 jghali 1238
                                        if (Options.Version < PDFVersion::PDF_14)
21709 jghali 1239
                                                StdFonts.insert(ind2PDFabr[pgit->annotation().Font()], "");
21744 jghali 1240
                                        QString replacementName = pgit->itemText.defaultStyle().charStyle().font().replacementName();
1241
                                        ReallyUsed.insert(replacementName, DocFonts[replacementName]);
17312 fschmid 1242
                                }
1243
                        }
21709 jghali 1244
                        uint start = pgit->isTextFrame() ? (uint) pgit->firstInFrame() : 0;
1245
                        uint stop  = pgit->isTextFrame() ? (uint) pgit->lastInFrame() + 1 : (uint) pgit->itemText.length();
1246
                        for (uint e = start; e < stop; ++e)
8092 fschmid 1247
                        {
21709 jghali 1248
                                QString replacementName = pgit->itemText.charStyle(e).font().replacementName();
1249
                                ReallyUsed.insert(replacementName, DocFonts[replacementName]);
8092 fschmid 1250
                        }
3133 fschmid 1251
                }
1252
        }
21709 jghali 1253
 
1254
        allItems = doc.MasterItems;
1255
        while (allItems.count() > 0)
3133 fschmid 1256
        {
21709 jghali 1257
                pgit = allItems.takeFirst();
1258
                if (pgit->isGroup() || pgit->isTable())
3133 fschmid 1259
                {
22224 jghali 1260
                        allItems = pgit->getChildren() + allItems;
21709 jghali 1261
                        continue;
1262
                }
1263
                if ((pgit->itemType() == PageItem::TextFrame) || (pgit->itemType() == PageItem::PathText))
1264
                {
1265
                        if (pgit->isAnnotation())
17312 fschmid 1266
                        {
21709 jghali 1267
                                int annotType  = pgit->annotation().Type();
1268
                                bool mustEmbed = ((annotType >= Annotation::Button) && (annotType <= Annotation::Listbox) && (annotType != Annotation::Checkbox));
1269
                                if ((pgit->annotation().Type() == Annotation::Checkbox) || (pgit->annotation().Type() == Annotation::RadioButton))
1270
                                        StdFonts.insert("/ZapfDingbats", "");
1271
                                if (pgit->itemText.length() > 0 || mustEmbed)
17312 fschmid 1272
                                {
23548 jghali 1273
                                        if (Options.Version < PDFVersion::PDF_14)
21709 jghali 1274
                                                StdFonts.insert(ind2PDFabr[pgit->annotation().Font()], "");
1275
                                        QString replacementName = pgit->itemText.defaultStyle().charStyle().font().replacementName();
1276
                                        ReallyUsed.insert(replacementName, DocFonts[replacementName]);
17312 fschmid 1277
                                }
1278
                        }
21709 jghali 1279
                        uint start = pgit->isTextFrame() ? (uint) pgit->firstInFrame() : 0;
1280
                        uint stop  = pgit->isTextFrame() ? (uint) pgit->lastInFrame() + 1 : (uint) pgit->itemText.length();
1281
                        for (uint e = start; e < stop; ++e)
8092 fschmid 1282
                        {
21709 jghali 1283
                                QString replacementName = pgit->itemText.charStyle(e).font().replacementName();
1284
                                ReallyUsed.insert(replacementName, DocFonts[replacementName]);
1285
                        }
1286
                }
1287
        }
1288
 
1289
        allItems = *(doc.Items);
1290
        while (allItems.count() > 0)
1291
        {
1292
                pgit = allItems.takeFirst();
1293
                if (pgit->isGroup() || pgit->isTable())
1294
                {
22224 jghali 1295
                        allItems = pgit->getChildren() + allItems;
21709 jghali 1296
                        continue;
1297
                }
1298
                if ((pgit->itemType() == PageItem::TextFrame) || (pgit->itemType() == PageItem::PathText))
1299
                {
1300
                        if (pgit->isAnnotation())
1301
                        {
1302
                                int annotType  = pgit->annotation().Type();
1303
                                bool mustEmbed = ((annotType >= Annotation::Button) && (annotType <= Annotation::Listbox) && (annotType != Annotation::Checkbox));
1304
                                if ((pgit->annotation().Type() == Annotation::Checkbox) || (pgit->annotation().Type() == Annotation::RadioButton))
1305
                                        StdFonts.insert("/ZapfDingbats", "");
1306
                                if (pgit->itemText.length() > 0 || mustEmbed)
12763 fschmid 1307
                                {
23548 jghali 1308
                                        if (Options.Version < PDFVersion::PDF_14)
21709 jghali 1309
                                                StdFonts.insert(ind2PDFabr[pgit->annotation().Font()], "");
1310
                                        QString replacementName = pgit->itemText.defaultStyle().charStyle().font().replacementName();
21707 jghali 1311
                                        ReallyUsed.insert(replacementName, DocFonts[replacementName]);
16953 fschmid 1312
                                }
8092 fschmid 1313
                        }
21709 jghali 1314
                        uint start = pgit->isTextFrame() ? (uint) pgit->firstInFrame() : 0;
1315
                        uint stop  = pgit->isTextFrame() ? (uint) pgit->lastInFrame() + 1 : (uint) pgit->itemText.length();
1316
                        for (uint e = start; e < stop; ++e)
1317
                        {
1318
                                QString replacementName = pgit->itemText.charStyle(e).font().replacementName();
1319
                                ReallyUsed.insert(replacementName, DocFonts[replacementName]);
1320
                        }
3133 fschmid 1321
                }
1322
        }
21709 jghali 1323
/*      if (Options.docInfoMarks)
3133 fschmid 1324
        {
21709 jghali 1325
                StdFonts.insert("/Helvetica", "");
1326
        } */
1327
        QStringList patterns = doc.getPatternDependencyList(doc.getUsedPatterns());
1328
        for (int c = 0; c < patterns.count(); ++c)
1329
        {
1330
                ScPattern pa = doc.docPatterns[patterns[c]];
1331
                allItems = pa.items;
1332
                while (allItems.count() > 0)
3133 fschmid 1333
                {
21709 jghali 1334
                        pgit = allItems.takeFirst();
1335
                        if (pgit->isGroup() || pgit->isTable())
17312 fschmid 1336
                        {
22224 jghali 1337
                                allItems = pgit->getChildren() + allItems;
21709 jghali 1338
                                continue;
17312 fschmid 1339
                        }
16953 fschmid 1340
                        if ((pgit->itemType() == PageItem::TextFrame) || (pgit->itemType() == PageItem::PathText))
8092 fschmid 1341
                        {
16953 fschmid 1342
                                if (pgit->isAnnotation())
12763 fschmid 1343
                                {
17952 fschmid 1344
                                        if ((pgit->annotation().Type() == Annotation::Checkbox) || (pgit->annotation().Type() == Annotation::RadioButton))
16953 fschmid 1345
                                                StdFonts.insert("/ZapfDingbats", "");
21709 jghali 1346
                                        if (pgit->itemText.length() > 0)
16953 fschmid 1347
                                        {
23548 jghali 1348
                                                if (Options.Version < PDFVersion::PDF_14)
16953 fschmid 1349
                                                        StdFonts.insert(ind2PDFabr[pgit->annotation().Font()], "");
21707 jghali 1350
                                                QString replacementName = pgit->itemText.defaultStyle().charStyle().font().replacementName();
1351
                                                ReallyUsed.insert(replacementName, DocFonts[replacementName]);
16953 fschmid 1352
                                        }
12763 fschmid 1353
                                }
17751 jghali 1354
                                uint start = pgit->isTextFrame() ? (uint) pgit->firstInFrame() : 0;
1355
                                uint stop  = pgit->isTextFrame() ? (uint) pgit->lastInFrame() + 1 : (uint) pgit->itemText.length();
1356
                                for (uint e = start; e < stop; ++e)
16953 fschmid 1357
                                {
21707 jghali 1358
                                        QString replacementName = pgit->itemText.charStyle(e).font().replacementName();
1359
                                        ReallyUsed.insert(replacementName, DocFonts[replacementName]);
16953 fschmid 1360
                                }
8092 fschmid 1361
                        }
3133 fschmid 1362
                }
1363
        }
20112 jghali 1364
        return ReallyUsed;
20103 avox 1365
}
17628 jghali 1366
 
20103 avox 1367
static QByteArray sanitizeFontName(QString fn)
1368
{
24129 jghali 1369
        return Pdf::toPdfDocEncoding(fn.replace( QRegExp("[\\s\\/\\{\\[\\]\\}\\<\\>\\(\\)\\%]"), "_"));
20103 avox 1370
}
17628 jghali 1371
 
22507 jghali 1372
static QList<Pdf::Resource> asColorSpace(const QList<PdfICCD>& iccCSlist)
20103 avox 1373
{
20112 jghali 1374
        QList<Pdf::Resource> result;
22507 jghali 1375
        for (const Pdf::Resource& r : iccCSlist)
20112 jghali 1376
                result.append(r);
1377
        return result;
20103 avox 1378
}
1379
 
22507 jghali 1380
static QList<Pdf::Resource> asColorSpace(const QList<PdfSpotC>& spotMapValues)
20103 avox 1381
{
20112 jghali 1382
        QList<Pdf::Resource> result;
22507 jghali 1383
        for (const Pdf::Resource& r : spotMapValues)
20112 jghali 1384
                result.append(r);
1385
        return result;
20103 avox 1386
}
1387
 
1388
void PDFLibCore::PDF_WriteStandardFonts()
1389
{
20112 jghali 1390
        int a = 0;
1391
        QMap<QString, QString>::Iterator itStd;
1392
        for (itStd = StdFonts.begin(); itStd != StdFonts.end(); ++itStd)
1393
        {
1394
                PdfId fontObject = writer.newObject();
1395
                writer.startObj(fontObject);
1396
                PutDoc("<<\n/Type /Font\n/Subtype /Type1\n");
23561 jghali 1397
                PutDoc("/Name /FoStd" + Pdf::toPdf(a) + "\n");
1398
                PutDoc("/BaseFont " + Pdf::toPdfDocEncoding(itStd.key()) + "\n");
20112 jghali 1399
                if (itStd.key() != "/ZapfDingbats")
1400
                {
1401
                        PutDoc("/Encoding << \n");
1402
                        PutDoc("/Differences [ \n");
1403
                        PutDoc("24 /breve /caron /circumflex /dotaccent /hungarumlaut /ogonek /ring /tilde\n");
1404
                        PutDoc("39 /quotesingle 96 /grave 128 /bullet /dagger /daggerdbl /ellipsis /emdash /endash /florin /fraction /guilsinglleft /guilsinglright\n");
1405
                        PutDoc("/minus /perthousand /quotedblbase /quotedblleft /quotedblright /quoteleft /quoteright /quotesinglbase /trademark /fi /fl /Lslash /OE /Scaron\n");
21744 jghali 1406
                        PutDoc("/Ydieresis /Zcaron /dotlessi /lslash /oe /scaron /zcaron 160 /Euro 164 /currency 166 /brokenbar 168 /dieresis /copyright /ordfeminine 172 /logicalnot\n");
20112 jghali 1407
                        PutDoc("/.notdef /registered /macron /degree /plusminus /twosuperior /threesuperior /acute /mu 183 /periodcentered /cedilla /onesuperior /ordmasculine\n");
1408
                        PutDoc("188 /onequarter /onehalf /threequarters 192 /Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla /Egrave /Eacute /Ecircumflex\n");
1409
                        PutDoc("/Edieresis /Igrave /Iacute /Icircumflex /Idieresis /Eth /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply /Oslash\n");
1410
                        PutDoc("/Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn /germandbls /agrave /aacute /acircumflex /atilde /adieresis /aring /ae /ccedilla\n");
1411
                        PutDoc("/egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex /idieresis /eth /ntilde /ograve /oacute /ocircumflex /otilde /odieresis\n");
1412
                        PutDoc("/divide /oslash /ugrave /uacute /ucircumflex /udieresis /yacute /thorn /ydieresis\n");
1413
                        PutDoc("] >>\n");
1414
                }
1415
                PutDoc(">>");
1416
                writer.endObj(fontObject);
23561 jghali 1417
                pageData.FObjects["FoStd" + Pdf::toPdf(a)] = fontObject;
1418
                itStd.value() = "FoStd" + Pdf::toPdf(a);
20112 jghali 1419
                a++;
1420
        }
20103 avox 1421
}
1422
 
1423
 
1424
PdfFont PDFLibCore::PDF_WriteType3Font(const QByteArray& name, ScFace& face, const QMap<uint,FPointArray>& RealGlyphs)
1425
{
20112 jghali 1426
        PdfFont result;
1427
        result.name = Pdf::toName(name);
23759 jghali 1428
        result.usage = Used_in_Content;
20112 jghali 1429
        result.method = Use_Type3;
1430
        result.encoding = Encode_256;
20103 avox 1431
 
22008 jghali 1432
        // TrueType font rasterizers use non-zero winding file rule,
1433
        // vs even-odd for current Postscript font rasterizers
1434
        // Note: OTF CFF2 will also use non-zero winding rule, so
1435
        // change code below when adding CCF2 support to Scribus
1436
        // ref: https://www.microsoft.com/typography/OTSpec/cff2.htm
1437
        bool useNonZeroRule = (face.type() == ScFace::TTF);
1438
 
20112 jghali 1439
        uint SubFonts = 0;
1440
        int glyphCount = 0;
1441
        double minx =  std::numeric_limits<double>::max();
1442
        double miny =  std::numeric_limits<double>::max();
1443
        double maxx = -std::numeric_limits<double>::max();
1444
        double maxy = -std::numeric_limits<double>::max();
22142 jghali 1445
 
20112 jghali 1446
        QList<uint> glyphWidths;
1447
        QList<QByteArray> charProcs;
22142 jghali 1448
        QStringList toUnicodeMaps;
1449
        QList<int> toUnicodeMapsCount;
22719 jghali 1450
        QString toUnicodeMap;
22142 jghali 1451
        int toUnicodeMapCounter = 0;
20112 jghali 1452
        QByteArray encoding = "<< /Type /Encoding\n/Differences [ 0\n";
1453
        QByteArray fon;
1454
        QMap<uint, uint> glyphMapping;
22142 jghali 1455
 
20112 jghali 1456
        ScFace::FaceEncoding gl;
1457
        face.glyphNames(gl);
22142 jghali 1458
 
20112 jghali 1459
        QMap<uint,FPointArray>::ConstIterator ig;
1460
        for (ig = RealGlyphs.cbegin(); ig != RealGlyphs.cend(); ++ig)
1461
        {
1462
                FPoint np, np1, np2;
1463
                bool nPath = true;
1464
                fon.resize(0);
1465
                if (ig.value().size() > 3)
1466
                {
1467
                        FPointArray gly = ig.value();
1468
                        QTransform mat;
1469
                        mat.scale(100.0, -100.0);
1470
                        gly.map(mat);
1471
                        gly.translate(0, 1000);
23563 jghali 1472
                        for (int poi = 0; poi < gly.size() - 3; poi += 4)
20112 jghali 1473
                        {
1474
                                if (gly.isMarker(poi))
1475
                                {
1476
                                        fon += "h\n";
1477
                                        nPath = true;
1478
                                        continue;
1479
                                }
1480
                                if (nPath)
1481
                                {
1482
                                        np = gly.point(poi);
23561 jghali 1483
                                        fon += FToStr(np.x()) + " " + FToStr(np.y()) + " m\n";
20112 jghali 1484
                                        nPath = false;
1485
                                }
1486
                                np = gly.point(poi+1);
1487
                                np1 = gly.point(poi+3);
1488
                                np2 = gly.point(poi+2);
1489
                                fon += FToStr(np.x()) + " " + FToStr(np.y()) + " " + FToStr(np1.x()) + " " + FToStr(np1.y()) + " " + FToStr(np2.x()) + " " + FToStr(np2.y()) + " c\n";
1490
                        }
22008 jghali 1491
                        fon += useNonZeroRule? "h f\n" : "h f*\n";
20112 jghali 1492
                        np = getMinClipF(&gly);
1493
                        np1 = getMaxClipF(&gly);
1494
                }
1495
                else
1496
                {
20684 jghali 1497
                        // 13654: make evince emit warning about "Syntax Error: No current point in closepath"
1498
                        // PDF specification is not quite clear about that, but a single "h" operator might not
1499
                        // be a valid way to make an empty path
1500
                        // fon = "h";
20112 jghali 1501
                        np = FPoint(0, 0);
1502
                        np1 = FPoint(0, 0);
1503
                }
23563 jghali 1504
                fon.prepend(Pdf::toPdf(qRound(np1.x())) + " 0 " + Pdf::toPdf(qRound(np.x())) + " " + Pdf::toPdf(qRound(np.y())) + " " + Pdf::toPdf(qRound(np1.x())) + " " + Pdf::toPdf(qRound(np1.y())) + " d1\n");
20112 jghali 1505
                minx = qMin(minx, np.x());
1506
                miny = qMin(miny, np.y());
1507
                maxx = qMax(maxx, np1.x());
1508
                maxy = qMax(maxy, np1.y());
1509
                glyphWidths.append(qRound(np1.x()));
22142 jghali 1510
 
20112 jghali 1511
                PdfId charProcObject = writer.newObject();
1512
                writer.startObj(charProcObject);
1513
                if (Options.Compress)
1514
                        fon = CompressArray(fon);
22717 jghali 1515
                PutDoc("<< /Length " + Pdf::toPdf(fon.length() + 1));
20112 jghali 1516
                if (Options.Compress)
1517
                        PutDoc("\n/Filter /FlateDecode");
22717 jghali 1518
                PutDoc("\n>>\nstream\n" + EncStream(fon, charProcObject) + "\nendstream");
20112 jghali 1519
                writer.endObj(charProcObject);
22142 jghali 1520
 
22717 jghali 1521
                // #15449 : in some cases we cannot retrieve glyph names for all glyphs we need
1522
                // using ScFace's glyphNames(), so generate custom glyph names using glyph index.
1523
                // With Type 3 fonts we have more flexibility than with other fonts.
1524
                QString glGlyphName, glToUnicode;
1525
                auto  glyphIt = gl.constFind(ig.key());
1526
                if ((glyphIt != gl.constEnd()) && (glyphIt->charcode > 0))
1527
                        glToUnicode = QString::asprintf("%04X", glyphIt->charcode);
1528
                else
1529
                        glToUnicode = QString("0000");
1530
                glGlyphName = "gly" + QString::asprintf("%04X", ig.key());
1531
 
1532
                charProcs.append(Pdf::toName(glGlyphName) + " " + Pdf::toPdf(charProcObject) + " 0 R\n");
1533
                encoding += Pdf::toName(glGlyphName) + " ";
1534
                glyphMapping.insert(ig.key(), glyphCount + SubFonts * 256);
1535
 
1536
                QString tmp = QString::asprintf("%02X", glyphCount);
1537
                toUnicodeMap += "<" + Pdf::toAscii(tmp) + "> <" + Pdf::toAscii(glToUnicode) + ">\n";
22142 jghali 1538
                toUnicodeMapCounter++;
1539
                if (toUnicodeMapCounter == 100)
1540
                {
1541
                        toUnicodeMaps.append(toUnicodeMap);
1542
                        toUnicodeMapsCount.append(toUnicodeMapCounter);
22717 jghali 1543
                        toUnicodeMap.clear();
22142 jghali 1544
                        toUnicodeMapCounter = 0;
1545
                }
1546
 
20112 jghali 1547
                glyphCount++;
1548
                int glyphsLeft = RealGlyphs.count() - SubFonts * 256;
1549
                if ((glyphCount > 255) || (glyphCount == glyphsLeft))
1550
                {
1551
                        PdfId fontWidths = writer.newObject();
1552
                        writer.startObj(fontWidths);
1553
                        PutDoc("[ ");
1554
                        for (int ww = 0; ww < glyphWidths.count(); ++ww)
1555
                        {
23561 jghali 1556
                                PutDoc(Pdf::toPdf(glyphWidths[ww]) + " ");
20112 jghali 1557
                        }
1558
                        PutDoc("]");
1559
                        writer.endObj(fontWidths);
1560
                        PdfId fontCharProcs = writer.newObject();
1561
                        writer.startObj(fontCharProcs);
1562
                        PutDoc("<<\n");
1563
                        for (int ww = 0; ww < charProcs.count(); ++ww)
1564
                        {
1565
                                PutDoc(charProcs[ww]);
1566
                        }
1567
                        PutDoc(">>");
1568
                        writer.endObj(fontCharProcs);
1569
                        PdfId fontEncoding = writer.newObject();
1570
                        writer.startObj(fontEncoding);
1571
                        PutDoc(encoding);
1572
                        PutDoc("]\n");
1573
                        PutDoc(">>");
1574
                        writer.endObj(fontEncoding);
22142 jghali 1575
 
1576
                        if (toUnicodeMapCounter != 0)
1577
                        {
1578
                                toUnicodeMaps.append(toUnicodeMap);
1579
                                toUnicodeMapsCount.append(toUnicodeMapCounter);
1580
                        }
1581
 
22719 jghali 1582
                        QByteArray toUnicodeMapStream;
22142 jghali 1583
                        toUnicodeMapStream += "/CIDInit /ProcSet findresource begin\n";
1584
                        toUnicodeMapStream += "12 dict begin\n";
1585
                        toUnicodeMapStream += "begincmap\n";
1586
                        toUnicodeMapStream += "/CIDSystemInfo <<\n";
1587
                        toUnicodeMapStream += "/Registry (Adobe)\n";
1588
                        toUnicodeMapStream += "/Ordering (UCS)\n";
1589
                        toUnicodeMapStream += "/Supplement 0\n";
1590
                        toUnicodeMapStream += ">> def\n";
1591
                        toUnicodeMapStream += "/CMapName /Adobe-Identity-UCS def\n";
1592
                        toUnicodeMapStream += "/CMapType 2 def\n";
1593
                        toUnicodeMapStream += "1 begincodespacerange\n";
1594
                        toUnicodeMapStream += "<0000> <FFFF>\n";
1595
                        toUnicodeMapStream += "endcodespacerange\n";
1596
                        for (int uniC = 0; uniC < toUnicodeMaps.count(); uniC++)
1597
                        {
1598
                                toUnicodeMapStream += Pdf::toPdf(toUnicodeMapsCount[uniC]);
1599
                                toUnicodeMapStream += " beginbfchar\n";
1600
                                toUnicodeMapStream += toUnicodeMaps[uniC];
1601
                                toUnicodeMapStream += "endbfchar\n";
1602
                        }
1603
                        toUnicodeMapStream += "endcmap\n";
1604
                        toUnicodeMapStream += "CMapName currentdict /CMap defineresource pop\n";
1605
                        toUnicodeMapStream += "end\n";
1606
                        toUnicodeMapStream += "end\n";
1607
                        PdfId fontToUnicode = WritePDFStream(toUnicodeMapStream);
1608
 
20112 jghali 1609
                        PdfId font3Object = writer.newObject();
1610
                        writer.startObj(font3Object);
1611
                        PutDoc("<<\n/Type /Font\n/Subtype /Type3\n");
23561 jghali 1612
                        PutDoc("/Name /" + name + "S" + Pdf::toPdf(SubFonts) + "\n");
20112 jghali 1613
                        PutDoc("/FirstChar 0\n");
23561 jghali 1614
                        PutDoc("/LastChar " + Pdf::toPdf(glyphCount-1) + "\n");
1615
                        PutDoc("/Widths " + Pdf::toPdf(fontWidths) + " 0 R\n");
1616
                        PutDoc("/CharProcs " + Pdf::toPdf(fontCharProcs) + " 0 R\n");
23563 jghali 1617
                        PutDoc("/FontBBox [" + Pdf::toPdf(qRound(minx)) + " " + Pdf::toPdf(qRound(miny)) + " " + Pdf::toPdf(qRound(maxx)) + " " + Pdf::toPdf(qRound(maxy)) + "]\n");
20112 jghali 1618
                        PutDoc("/FontMatrix [0.001 0 0 0.001 0 0]\n");
23561 jghali 1619
                        PutDoc("/Encoding " + Pdf::toPdf(fontEncoding) + " 0 R\n");
22142 jghali 1620
                        PutDoc("/ToUnicode " + Pdf::toPdf(fontToUnicode) + " 0 R\n");
20112 jghali 1621
                        PutDoc(">>");
1622
                        writer.endObj(font3Object);
23561 jghali 1623
                        pageData.FObjects[name + "S" + Pdf::toPdf(SubFonts)] = font3Object;
20112 jghali 1624
                        charProcs.clear();
1625
                        glyphWidths.clear();
22142 jghali 1626
                        toUnicodeMaps.clear();
1627
                        toUnicodeMapsCount.clear();
1628
                        toUnicodeMap.clear();
1629
                        toUnicodeMapCounter = 0;
20112 jghali 1630
                        glyphCount = 0;
1631
                        ++SubFonts;
1632
                        minx =  std::numeric_limits<double>::max();
1633
                        miny =  std::numeric_limits<double>::max();
1634
                        maxx = -std::numeric_limits<double>::max();
1635
                        maxy = -std::numeric_limits<double>::max();
1636
                        encoding = "<< /Type /Encoding\n/Differences [ 0\n";
1637
                }
1638
        }
1639
        result.glyphmap = glyphMapping;
1640
        return result;
20103 avox 1641
}
1642
 
1643
 
1644
PdfFont PDFLibCore::PDF_WriteGlyphsAsXForms(const QByteArray& fontName, ScFace& face, const QMap<uint,FPointArray>& RealGlyphs)
1645
{
20112 jghali 1646
        PdfFont result;
1647
        result.name = Pdf::toName(fontName);
23729 jghali 1648
        result.usage = Used_in_Content;
20112 jghali 1649
        result.method = Use_XForm;
1650
        result.encoding = Encode_224;
20103 avox 1651
 
22008 jghali 1652
        // TrueType font rasterizers use non-zero winding file rule,
1653
        // vs even-odd for current Postscript font rasterizers
1654
        // Note: OTF CFF2 will also use non-zero winding rule, so
1655
        // change code below when adding CCF2 support to Scribus
1656
        // ref: https://www.microsoft.com/typography/OTSpec/cff2.htm
1657
        bool useNonZeroRule = (face.type() == ScFace::TTF);
1658
 
20112 jghali 1659
        QByteArray fon;
1660
        QMap<uint,FPointArray>::ConstIterator ig;
1661
        for (ig = RealGlyphs.cbegin(); ig != RealGlyphs.cend(); ++ig)
1662
        {
1663
                FPoint np, np1, np2;
1664
                bool nPath = true;
1665
                fon.resize(0);
1666
                if (ig.value().size() > 3)
1667
                {
1668
                        FPointArray gly = ig.value();
1669
                        QTransform mat;
1670
                        mat.scale(0.1, 0.1);
1671
                        gly.map(mat);
23563 jghali 1672
                        for (int poi = 0; poi < gly.size() - 3; poi += 4)
20112 jghali 1673
                        {
1674
                                if (gly.isMarker(poi))
1675
                                {
1676
                                        fon += "h\n";
1677
                                        nPath = true;
1678
                                        continue;
1679
                                }
1680
                                if (nPath)
1681
                                {
1682
                                        np = gly.point(poi);
23561 jghali 1683
                                        fon += FToStr(np.x()) + " " + FToStr(-np.y()) + " m\n";
20112 jghali 1684
                                        nPath = false;
1685
                                }
1686
                                np = gly.point(poi+1);
1687
                                np1 = gly.point(poi+3);
1688
                                np2 = gly.point(poi+2);
1689
                                fon += FToStr(np.x()) + " " + FToStr(-np.y()) + " " +
1690
                                FToStr(np1.x()) + " " + FToStr(-np1.y()) + " " +
1691
                                FToStr(np2.x()) + " " + FToStr(-np2.y()) + " c\n";
1692
                        }
22008 jghali 1693
                        fon += useNonZeroRule? "h f\n" : "h f*\n";
20112 jghali 1694
                        np = getMinClipF(&gly);
1695
                        np1 = getMaxClipF(&gly);
1696
                }
1697
                else
1698
                {
20684 jghali 1699
                        // 13654: make evince emit warning about "Syntax Error: No current point in closepath"
1700
                        // PDF specification is not quite clear about that, but a single "h" operator might not
1701
                        // be a valid way to make an empty path
1702
                        fon = " " /*"h"*/;
20112 jghali 1703
                        np = FPoint(0, 0);
1704
                        np1 = FPoint(0, 0);
1705
                }
1706
                PdfId fontGlyphXForm = writer.newObject();
1707
                writer.startObj(fontGlyphXForm);
1708
                PutDoc("<<\n/Type /XObject\n/Subtype /Form\n/FormType 1\n");
23563 jghali 1709
                PutDoc("/BBox [ " + FToStr(np.x()) + " " + FToStr(-np.y()) + " " + FToStr(np1.x()) + " " + FToStr(-np1.y()) + " ]\n");
20112 jghali 1710
                PutDoc("/Resources << /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]\n");
1711
                PutDoc(">>\n");
1712
                if (Options.Compress)
1713
                        fon = CompressArray(fon);
23563 jghali 1714
                PutDoc("/Length " + Pdf::toPdf(fon.length() + 1));
20112 jghali 1715
                if (Options.Compress)
1716
                        PutDoc("\n/Filter /FlateDecode");
23561 jghali 1717
                PutDoc(" >>\nstream\n" + EncStream(fon, fontGlyphXForm) + "\nendstream");
20112 jghali 1718
                writer.endObj(fontGlyphXForm);
21894 jghali 1719
                pageData.XObjects[fontName + "_gl" + Pdf::toPdf(ig.key())] = fontGlyphXForm;
20112 jghali 1720
        }
1721
        return result;
20103 avox 1722
}
1723
 
1724
PdfId PDFLibCore::PDF_EmbedFontObject(const QByteArray& font, const QByteArray& subtype)
1725
{
20112 jghali 1726
        PdfId embeddedFontObject = writer.newObject();
1727
        writer.startObj(embeddedFontObject);
1728
        int len = font.length();
1729
        QByteArray ttf = (Options.Compress? CompressArray(font) : font);
1730
        //qDebug() << QString("sfnt data: size=%1 compressed=%2").arg(len).arg(bb.length());
1731
        PutDoc("<<\n/Length " + Pdf::toPdf(ttf.length() + 1) + "\n");
1732
        PutDoc("/Length1 " + Pdf::toPdf(len) + "\n");
1733
        if (subtype.size() > 0)
1734
                PutDoc("/Subtype " + subtype);
1735
        if (Options.Compress)
1736
                PutDoc("/Filter /FlateDecode\n");
1737
        PutDoc(">>\nstream\n");
1738
        EncodeArrayToStream(ttf, embeddedFontObject);
1739
        PutDoc("\nendstream");
1740
        writer.endObj(embeddedFontObject);
1741
        return embeddedFontObject;
20103 avox 1742
}
1743
 
22600 craig 1744
QByteArray PDFLibCore::PDF_GenerateSubsetTag(const QByteArray& fontName, const QList<uint>& usedGlyphs)
21775 jghali 1745
{
1746
        uint hash, mod;
1747
        QVector<uint> glyphVec = usedGlyphs.toVector();
1748
 
1749
        hash = qHashBits(fontName.constData(), fontName.size(), 0);
1750
        hash = qHashBits(glyphVec.constData(), glyphVec.size() * sizeof(uint), hash);
1751
 
1752
        QByteArray subsetTag(6, (char) 0);
1753
        for (int i = 0; i < 6; ++i)
1754
        {
1755
                mod = hash % 26;
1756
                subsetTag[i] = 'A' + mod;
1757
                hash = (hash - mod) / 26;
1758
        }
1759
 
1760
        return subsetTag;
1761
}
1762
 
20103 avox 1763
PdfId PDFLibCore::PDF_WriteFontDescriptor(const QByteArray& fontName, ScFace& face, ScFace::FontFormat fformat, PdfId embeddedFontObject)
1764
{
20112 jghali 1765
        PdfId fontDescriptor = writer.newObject();
1766
        writer.startObj(fontDescriptor);
1767
        // TODO: think about QByteArray ScFace::getFontDescriptor() -- AV
1768
        PutDoc("<<\n/Type /FontDescriptor\n");
1769
        PutDoc("/FontName "  + Pdf::toName(fontName) + "\n");
1770
        PutDoc("/FontBBox [ " + Pdf::toAscii(face.pdfFontBBoxAsString()) + " ]\n");
1771
        PutDoc("/Flags ");
1772
        //FIXME: isItalic() should be queried from ScFace, not from Qt -- AV
1773
        //QFontInfo fo = QFontInfo(it.data());
1774
        int pfl = 0;
1775
        if (face.isFixedPitch())
1776
                pfl = pfl ^ 1;
1777
        //if (fo.italic())
1778
        if (face.italicAngleAsString() != "0")
1779
                pfl = pfl ^ 64;
1780
        //                      pfl = pfl ^ 4;
1781
        pfl = pfl ^ 32;
23561 jghali 1782
        PutDoc(Pdf::toPdf(pfl) + "\n");
1783
        PutDoc("/Ascent "      + Pdf::toAscii(face.pdfAscentAsString()) + "\n");
1784
        PutDoc("/Descent "     + Pdf::toAscii(face.pdfDescentAsString()) + "\n");
1785
        PutDoc("/CapHeight "   + Pdf::toAscii(face.pdfCapHeightAsString()) + "\n");
1786
        PutDoc("/ItalicAngle " + Pdf::toAscii(face.italicAngleAsString()) + "\n");
20112 jghali 1787
        PutDoc("/StemV 1\n");
1788
        if (embeddedFontObject != 0)
1789
        {
1790
                if (fformat == ScFace::SFNT || fformat == ScFace::TTCF)
1791
                {
1792
                        if (face.type() == ScFace::OTF)
1793
                        {
23548 jghali 1794
                                PutDoc("/FontFile3 " + Pdf::toPdf(embeddedFontObject) + " 0 R\n");
20112 jghali 1795
                        }
1796
                        else
1797
                        {
23548 jghali 1798
                                PutDoc("/FontFile2 " + Pdf::toPdf(embeddedFontObject) + " 0 R\n");
20112 jghali 1799
                        }
1800
                }
1801
                else if (fformat == ScFace::PFB)
23561 jghali 1802
                        PutDoc("/FontFile " + Pdf::toPdf(embeddedFontObject) + " 0 R\n");
20112 jghali 1803
                else if (fformat == ScFace::PFA)
23561 jghali 1804
                        PutDoc("/FontFile " + Pdf::toPdf(embeddedFontObject) + " 0 R\n");
20112 jghali 1805
        }
1806
        PutDoc(">>");
1807
        writer.endObj(fontDescriptor);
1808
        return fontDescriptor;
20103 avox 1809
}
1810
 
24129 jghali 1811
PdfFont PDFLibCore::PDF_EncodeCidFont(const QByteArray& fontName, ScFace& face, const QByteArray& baseFont, PdfId fontDes, const ScFace::FaceEncoding& gl, const QMap<uint,uint>& glyphmap)
20103 avox 1812
{
20112 jghali 1813
        PdfFont result;
1814
        result.name = Pdf::toName(fontName);
23727 jghali 1815
        result.usage = Used_in_Content;
20112 jghali 1816
        result.method = glyphmap.isEmpty()? Use_Embedded : Use_Subset;
1817
        result.encoding = glyphmap.isEmpty()? Encode_IdentityH : Encode_Subset;
1818
        result.glyphmap = glyphmap;
1819
 
1820
        PdfId fontWidths2 = writer.newObject();
1821
        writer.startObj(fontWidths2);
1822
        QList<QByteArray> toUnicodeMaps;
1823
        QList<int> toUnicodeMapsCount;
22719 jghali 1824
        QByteArray toUnicodeMap;
20112 jghali 1825
        int toUnicodeMapCounter = 0;
1826
 
1827
        PutDoc("[ ");
1828
        QList<uint> keys = gl.uniqueKeys();
1829
        bool seenNotDef = false;
23483 jghali 1830
        for (auto git = keys.begin(); git != keys.end(); ++git)
20112 jghali 1831
        {
1832
                uint gid = result.encoding == Encode_Subset? glyphmap[*git] : *git;
1833
                if (gid > 0 || !seenNotDef)
1834
                {
1835
                        seenNotDef |= (gid == 0);
24129 jghali 1836
                        PutDoc(Pdf::toPdf(gid) + " [" + Pdf::toPdf(static_cast<int>(face.glyphWidth(*git) * 1000)) + "] ");
23483 jghali 1837
                        QString tmp = QString::asprintf("%04X", gid);
1838
                        QString tmp2 = gl.value(*git).toUnicode;
23563 jghali 1839
                        toUnicodeMap += "<" + Pdf::toAscii(tmp) + "> <" + Pdf::toAscii(tmp2) + ">\n";
20112 jghali 1840
                        toUnicodeMapCounter++;
1841
                        if (toUnicodeMapCounter == 100)
1842
                        {
1843
                                toUnicodeMaps.append(toUnicodeMap);
1844
                                toUnicodeMapsCount.append(toUnicodeMapCounter);
22719 jghali 1845
                                toUnicodeMap.clear();
20112 jghali 1846
                                toUnicodeMapCounter = 0;
1847
                        }
1848
                }
1849
        }
1850
        PutDoc("]");
1851
        writer.endObj(fontWidths2);
1852
        if (toUnicodeMapCounter != 0)
1853
        {
1854
                toUnicodeMaps.append(toUnicodeMap);
1855
                toUnicodeMapsCount.append(toUnicodeMapCounter);
1856
        }
22719 jghali 1857
        QByteArray toUnicodeMapStream;
20112 jghali 1858
        toUnicodeMapStream += "/CIDInit /ProcSet findresource begin\n";
1859
        toUnicodeMapStream += "12 dict begin\n";
1860
        toUnicodeMapStream += "begincmap\n";
1861
        toUnicodeMapStream += "/CIDSystemInfo <<\n";
1862
        toUnicodeMapStream += "/Registry (Adobe)\n";
1863
        toUnicodeMapStream += "/Ordering (UCS)\n";
1864
        toUnicodeMapStream += "/Supplement 0\n";
1865
        toUnicodeMapStream += ">> def\n";
1866
        toUnicodeMapStream += "/CMapName /Adobe-Identity-UCS def\n";
1867
        toUnicodeMapStream += "/CMapType 2 def\n";
1868
        toUnicodeMapStream += "1 begincodespacerange\n";
1869
        toUnicodeMapStream += "<0000> <FFFF>\n";
1870
        toUnicodeMapStream += "endcodespacerange\n";
1871
        for (int uniC = 0; uniC < toUnicodeMaps.count(); uniC++)
1872
        {
1873
                toUnicodeMapStream += Pdf::toPdf(toUnicodeMapsCount[uniC]);
1874
                toUnicodeMapStream += " beginbfchar\n";
1875
                toUnicodeMapStream += toUnicodeMaps[uniC];
1876
                toUnicodeMapStream += "endbfchar\n";
1877
        }
1878
        toUnicodeMapStream += "endcmap\n";
1879
        toUnicodeMapStream += "CMapName currentdict /CMap defineresource pop\n";
1880
        toUnicodeMapStream += "end\n";
1881
        toUnicodeMapStream += "end\n";
1882
        PdfId fontToUnicode2 = WritePDFStream(toUnicodeMapStream);
1883
        PdfId fontObject2 = writer.newObject();
1884
        writer.startObj(fontObject2);
1885
        PutDoc("<<\n/Type /Font\n/Subtype /Type0\n");
1886
        PutDoc("/Name " + Pdf::toName(fontName) + "\n");
23563 jghali 1887
        PutDoc("/BaseFont " + Pdf::toName(baseFont) + "\n");
20112 jghali 1888
        PutDoc("/Encoding /Identity-H\n");
23561 jghali 1889
        PutDoc("/ToUnicode " + Pdf::toPdf(fontToUnicode2) + " 0 R\n");
20112 jghali 1890
        PutDoc("/DescendantFonts [");
1891
        PutDoc("<</Type /Font");
1892
        if (face.type() == ScFace::OTF)
1893
                PutDoc("/Subtype /CIDFontType0");
1894
        else
1895
                PutDoc("/Subtype /CIDFontType2");
21759 jghali 1896
        PutDoc("/BaseFont " + Pdf::toName(baseFont));
23563 jghali 1897
        PutDoc("/FontDescriptor " + Pdf::toPdf(fontDes) + " 0 R");
20112 jghali 1898
        PutDoc("/CIDSystemInfo <</Ordering(Identity)/Registry(Adobe)/Supplement 0>>");
1899
        PutDoc("/DW 1000");
23561 jghali 1900
        PutDoc("/W " + Pdf::toPdf(fontWidths2) + " 0 R");
20112 jghali 1901
        PutDoc("/CIDToGIDMap /Identity");
1902
        PutDoc(">>"); // close CIDFont dictionary
1903
        PutDoc("]\n"); // close DescendantFonts array
1904
        PutDoc(">>");
1905
        writer.endObj(fontObject2);
1906
        pageData.FObjects[fontName] = fontObject2;
1907
        return result;
20103 avox 1908
}
1909
 
1910
 
1911
PdfFont PDFLibCore::PDF_EncodeSimpleFont(const QByteArray& fontName, ScFace& face, const QByteArray& baseFont, const QByteArray& subtype, bool isEmbedded, PdfId fontDes, const ScFace::FaceEncoding& gl)
1912
{
20112 jghali 1913
        PdfFont result;
1914
        result.name = Pdf::toName(fontName);
23728 jghali 1915
        result.usage = Used_in_Content;
20112 jghali 1916
        result.method = isEmbedded? Use_Embedded : Use_System;
1917
        result.encoding = Encode_224;
1918
 
1919
        int nglyphs = 0;
22717 jghali 1920
        for (auto gli = gl.cbegin(); gli != gl.cend(); ++gli)
20112 jghali 1921
        {
1922
                if (gli.key() > static_cast<uint>(nglyphs))
1923
                        nglyphs = gli.key();
1924
        }
1925
        ++nglyphs;
1926
        //                      qDebug() << QString("pdflib: nglyphs %1 max %2").arg(nglyphs).arg(face.maxGlyph());
1927
 
1928
        uint Fcc = nglyphs / 224;
1929
        if ((nglyphs % 224) != 0)
1930
                Fcc += 1;
1931
        for (uint Fc = 0; Fc < Fcc; ++Fc)
1932
        {
1933
                PdfId fontWidths2 = writer.newObject();
1934
                writer.startObj(fontWidths2);
1935
                int chCount = 32;
1936
                PutDoc("[ 0 0 0 0 0  0 0 0 0 0  0 0 0 0 0  0 0 0 0 0  0 0 0 0 0  0 0 0 0 0  0 0 ");
1937
                for (int ww = 32; ww < 256; ++ww)
1938
                {
1939
                        uint glyph = 224 * Fc + ww - 32;
1940
                        if (gl.contains(glyph))
23561 jghali 1941
                                PutDoc(Pdf::toPdf(static_cast<int>(face.glyphWidth(glyph)* 1000)) + " ");
20112 jghali 1942
                        else
1943
                                PutDoc("0 ");
1944
                        chCount++;
1945
                        if (signed(glyph) == nglyphs-1)
1946
                                break;
1947
                }
1948
                PutDoc("]");
1949
                writer.endObj(fontWidths2);
22717 jghali 1950
 
20112 jghali 1951
                QStringList toUnicodeMaps;
22719 jghali 1952
                QList<int>  toUnicodeMapsCount;
1953
                QString toUnicodeMap;
20112 jghali 1954
                int toUnicodeMapCounter = 0;
22717 jghali 1955
                int crc = 0;
1956
                bool startOfSeq = true;
1957
 
1958
                PdfId fontEncoding2 = writer.newObject();
1959
                writer.startObj(fontEncoding2);
20112 jghali 1960
                PutDoc("<< /Type /Encoding\n");
1961
                PutDoc("/Differences [ \n");
1962
                for (int ww2 = 32; ww2 < 256; ++ww2)
1963
                {
1964
                        uint glyph = 224 * Fc + ww2 - 32;
1965
                        ScFace::FaceEncoding::ConstIterator glIt = gl.find(glyph);
22143 jghali 1966
                        if (glIt != gl.cend() && !glIt.value().glyphName.isEmpty())
20112 jghali 1967
                        {
22143 jghali 1968
                                const ScFace::GlyphEncoding& glEncoding = glIt.value();
20112 jghali 1969
                                if (startOfSeq)
1970
                                {
23561 jghali 1971
                                        PutDoc(Pdf::toPdf(ww2) + " ");
20112 jghali 1972
                                        startOfSeq = false;
1973
                                }
23561 jghali 1974
                                PutDoc(Pdf::toName(glEncoding.glyphName) + " ");
23483 jghali 1975
                                QString tmp = QString::asprintf("%02X", ww2);
1976
                                QString tmp2 = glEncoding.toUnicode;
20112 jghali 1977
                                toUnicodeMap += "<" + Pdf::toAscii(tmp) + "> <" + Pdf::toAscii(tmp2) + ">\n";
1978
                                //QString("<%1> <%2>\n").arg(tmp).arg((tmp2));
1979
                                toUnicodeMapCounter++;
1980
                                if (toUnicodeMapCounter == 100)
1981
                                {
1982
                                        toUnicodeMaps.append(toUnicodeMap);
1983
                                        toUnicodeMapsCount.append(toUnicodeMapCounter);
22717 jghali 1984
                                        toUnicodeMap.clear();
20112 jghali 1985
                                        toUnicodeMapCounter = 0;
1986
                                }
1987
                                crc++;
1988
                        }
1989
                        else
1990
                        {
1991
                                startOfSeq = true;
1992
                        }
1993
                        if (signed(glyph) == nglyphs-1)
1994
                                break;
1995
                        if (crc > 8)
1996
                        {
1997
                                PutDoc("\n");
1998
                                crc = 0;
1999
                        }
2000
                }
2001
                if (toUnicodeMapCounter != 0)
2002
                {
2003
                        toUnicodeMaps.append(toUnicodeMap);
2004
                        toUnicodeMapsCount.append(toUnicodeMapCounter);
2005
                }
2006
                PutDoc("]\n");
2007
                PutDoc(">>");
2008
                writer.endObj(fontEncoding2);
22719 jghali 2009
                QByteArray toUnicodeMapStream;
20112 jghali 2010
                toUnicodeMapStream += "/CIDInit /ProcSet findresource begin\n";
2011
                toUnicodeMapStream += "12 dict begin\n";
2012
                toUnicodeMapStream += "begincmap\n";
2013
                toUnicodeMapStream += "/CIDSystemInfo <<\n";
2014
                toUnicodeMapStream += "/Registry (Adobe)\n";
2015
                toUnicodeMapStream += "/Ordering (UCS)\n";
2016
                toUnicodeMapStream += "/Supplement 0\n";
2017
                toUnicodeMapStream += ">> def\n";
2018
                toUnicodeMapStream += "/CMapName /Adobe-Identity-UCS def\n";
2019
                toUnicodeMapStream += "/CMapType 2 def\n";
2020
                toUnicodeMapStream += "1 begincodespacerange\n";
2021
                toUnicodeMapStream += "<0000> <FFFF>\n";
2022
                toUnicodeMapStream += "endcodespacerange\n";
2023
                for (int uniC = 0; uniC < toUnicodeMaps.count(); uniC++)
2024
                {
2025
                        toUnicodeMapStream += Pdf::toPdf(toUnicodeMapsCount[uniC]);
2026
                        toUnicodeMapStream += " beginbfchar\n";
2027
                        toUnicodeMapStream += toUnicodeMaps[uniC];
2028
                        toUnicodeMapStream += "endbfchar\n";
2029
                }
2030
                toUnicodeMapStream += "endcmap\n";
2031
                toUnicodeMapStream += "CMapName currentdict /CMap defineresource pop\n";
2032
                toUnicodeMapStream += "end\n";
2033
                toUnicodeMapStream += "end\n";
2034
                PdfId fontToUnicode2 = WritePDFStream(toUnicodeMapStream);
2035
                PdfId fontObject2 = writer.newObject();
2036
                writer.startObj(fontObject2);
2037
                PutDoc("<<\n/Type /Font\n/Subtype ");
2038
                PutDoc(subtype + "\n");
23561 jghali 2039
                PutDoc("/Name " + Pdf::toName(fontName) + "S" + Pdf::toPdf(Fc) + "\n");
23563 jghali 2040
                PutDoc("/BaseFont " + Pdf::toName(baseFont) + "\n");
20112 jghali 2041
                PutDoc("/FirstChar 0\n");
23561 jghali 2042
                PutDoc("/LastChar " + Pdf::toPdf(chCount-1) + "\n");
2043
                PutDoc("/Widths " + Pdf::toPdf(fontWidths2) + " 0 R\n");
2044
                PutDoc("/Encoding " + Pdf::toPdf(fontEncoding2) + " 0 R\n");
2045
                PutDoc("/ToUnicode " + Pdf::toPdf(fontToUnicode2) + " 0 R\n");
2046
                PutDoc("/FontDescriptor " + Pdf::toPdf(fontDes) + " 0 R\n");
20112 jghali 2047
                PutDoc(">>");
2048
                writer.endObj(fontObject2);
23561 jghali 2049
                pageData.FObjects[fontName + "S" + Pdf::toPdf(Fc)] = fontObject2;
22720 jghali 2050
        } // for (Fc)
21744 jghali 2051
 
2052
        return result;
2053
}
2054
 
2055
PdfFont PDFLibCore::PDF_EncodeFormFont(const QByteArray& fontName, ScFace& face, const QByteArray