Subversion Repositories Scribus

Rev

Rev 24429 | 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
 
24429 jghali 621
        QMap<QString, QMap<uint, QString> > usedFonts;
23560 jghali 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
 
24429 jghali 1004
bool PDFLibCore::PDF_Begin_Doc(const QString& fn, SCFonts &AllFonts, const QMap<QString, QMap<uint, QString> >& 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))
24306 jghali 1097
                PutDoc("/PageMode /UseOC\n");
19114 avox 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
 
24429 jghali 1204
QMap<QString, QMap<uint, QString> >
1205
PDFLibCore::PDF_Begin_FindUsedFonts(SCFonts &AllFonts, const QMap<QString, QMap<uint, QString> >& DocFonts)
20103 avox 1206
{
24429 jghali 1207
        QMap<QString, QMap<uint, QString> > usedFonts;
3133 fschmid 1208
        PageItem* pgit;
20103 avox 1209
        QMap<int, QByteArray> ind2PDFabr;
1210
        const QByteArray tmpf[] = {"/Courier", "/Courier-Bold", "/Courier-Oblique", "/Courier-BoldOblique",
4017 fschmid 1211
                                                                                                "/Helvetica", "/Helvetica-Bold", "/Helvetica-Oblique", "/Helvetica-BoldOblique",
4546 subik 1212
                                                                                                "/Times-Roman", "/Times-Bold", "/Times-Italic", "/Times-BoldItalic",
4017 fschmid 1213
                                                                                                "/ZapfDingbats", "/Symbol"};
1214
        size_t ar = sizeof(tmpf) / sizeof(*tmpf);
1215
        for (uint ax = 0; ax < ar; ++ax)
1216
                ind2PDFabr[ax] = tmpf[ax];
21709 jghali 1217
 
1218
        QList<PageItem*> allItems = doc.FrameItems.values();
1219
        while (allItems.count() > 0)
3133 fschmid 1220
        {
21709 jghali 1221
                pgit = allItems.takeFirst();
1222
                if (pgit->isGroup() || pgit->isTable())
3133 fschmid 1223
                {
22224 jghali 1224
                        allItems = pgit->getChildren() + allItems;
21709 jghali 1225
                        continue;
1226
                }
1227
                if ((pgit->itemType() == PageItem::TextFrame) || (pgit->itemType() == PageItem::PathText))
1228
                {
1229
                        if (pgit->isAnnotation())
17312 fschmid 1230
                        {
21709 jghali 1231
                                int annotType  = pgit->annotation().Type();
1232
                                bool mustEmbed = ((annotType >= Annotation::Button) && (annotType <= Annotation::Listbox) && (annotType != Annotation::Checkbox));
1233
                                if ((pgit->annotation().Type() == Annotation::Checkbox) || (pgit->annotation().Type() == Annotation::RadioButton))
1234
                                        StdFonts.insert("/ZapfDingbats", "");
1235
                                if (pgit->itemText.length() > 0 || mustEmbed)
17312 fschmid 1236
                                {
23548 jghali 1237
                                        if (Options.Version < PDFVersion::PDF_14)
21709 jghali 1238
                                                StdFonts.insert(ind2PDFabr[pgit->annotation().Font()], "");
21744 jghali 1239
                                        QString replacementName = pgit->itemText.defaultStyle().charStyle().font().replacementName();
24429 jghali 1240
                                        usedFonts.insert(replacementName, DocFonts[replacementName]);
17312 fschmid 1241
                                }
1242
                        }
21709 jghali 1243
                        uint start = pgit->isTextFrame() ? (uint) pgit->firstInFrame() : 0;
1244
                        uint stop  = pgit->isTextFrame() ? (uint) pgit->lastInFrame() + 1 : (uint) pgit->itemText.length();
1245
                        for (uint e = start; e < stop; ++e)
8092 fschmid 1246
                        {
21709 jghali 1247
                                QString replacementName = pgit->itemText.charStyle(e).font().replacementName();
24429 jghali 1248
                                usedFonts.insert(replacementName, DocFonts[replacementName]);
8092 fschmid 1249
                        }
3133 fschmid 1250
                }
1251
        }
21709 jghali 1252
 
1253
        allItems = doc.MasterItems;
1254
        while (allItems.count() > 0)
3133 fschmid 1255
        {
21709 jghali 1256
                pgit = allItems.takeFirst();
1257
                if (pgit->isGroup() || pgit->isTable())
3133 fschmid 1258
                {
22224 jghali 1259
                        allItems = pgit->getChildren() + allItems;
21709 jghali 1260
                        continue;
1261
                }
1262
                if ((pgit->itemType() == PageItem::TextFrame) || (pgit->itemType() == PageItem::PathText))
1263
                {
1264
                        if (pgit->isAnnotation())
17312 fschmid 1265
                        {
21709 jghali 1266
                                int annotType  = pgit->annotation().Type();
1267
                                bool mustEmbed = ((annotType >= Annotation::Button) && (annotType <= Annotation::Listbox) && (annotType != Annotation::Checkbox));
1268
                                if ((pgit->annotation().Type() == Annotation::Checkbox) || (pgit->annotation().Type() == Annotation::RadioButton))
1269
                                        StdFonts.insert("/ZapfDingbats", "");
1270
                                if (pgit->itemText.length() > 0 || mustEmbed)
17312 fschmid 1271
                                {
23548 jghali 1272
                                        if (Options.Version < PDFVersion::PDF_14)
21709 jghali 1273
                                                StdFonts.insert(ind2PDFabr[pgit->annotation().Font()], "");
1274
                                        QString replacementName = pgit->itemText.defaultStyle().charStyle().font().replacementName();
24429 jghali 1275
                                        usedFonts.insert(replacementName, DocFonts[replacementName]);
17312 fschmid 1276
                                }
1277
                        }
21709 jghali 1278
                        uint start = pgit->isTextFrame() ? (uint) pgit->firstInFrame() : 0;
1279
                        uint stop  = pgit->isTextFrame() ? (uint) pgit->lastInFrame() + 1 : (uint) pgit->itemText.length();
1280
                        for (uint e = start; e < stop; ++e)
8092 fschmid 1281
                        {
21709 jghali 1282
                                QString replacementName = pgit->itemText.charStyle(e).font().replacementName();
24429 jghali 1283
                                usedFonts.insert(replacementName, DocFonts[replacementName]);
21709 jghali 1284
                        }
1285
                }
1286
        }
1287
 
1288
        allItems = *(doc.Items);
1289
        while (allItems.count() > 0)
1290
        {
1291
                pgit = allItems.takeFirst();
1292
                if (pgit->isGroup() || pgit->isTable())
1293
                {
22224 jghali 1294
                        allItems = pgit->getChildren() + allItems;
21709 jghali 1295
                        continue;
1296
                }
1297
                if ((pgit->itemType() == PageItem::TextFrame) || (pgit->itemType() == PageItem::PathText))
1298
                {
1299
                        if (pgit->isAnnotation())
1300
                        {
1301
                                int annotType  = pgit->annotation().Type();
1302
                                bool mustEmbed = ((annotType >= Annotation::Button) && (annotType <= Annotation::Listbox) && (annotType != Annotation::Checkbox));
1303
                                if ((pgit->annotation().Type() == Annotation::Checkbox) || (pgit->annotation().Type() == Annotation::RadioButton))
1304
                                        StdFonts.insert("/ZapfDingbats", "");
1305
                                if (pgit->itemText.length() > 0 || mustEmbed)
12763 fschmid 1306
                                {
23548 jghali 1307
                                        if (Options.Version < PDFVersion::PDF_14)
21709 jghali 1308
                                                StdFonts.insert(ind2PDFabr[pgit->annotation().Font()], "");
1309
                                        QString replacementName = pgit->itemText.defaultStyle().charStyle().font().replacementName();
24429 jghali 1310
                                        usedFonts.insert(replacementName, DocFonts[replacementName]);
16953 fschmid 1311
                                }
8092 fschmid 1312
                        }
21709 jghali 1313
                        uint start = pgit->isTextFrame() ? (uint) pgit->firstInFrame() : 0;
1314
                        uint stop  = pgit->isTextFrame() ? (uint) pgit->lastInFrame() + 1 : (uint) pgit->itemText.length();
1315
                        for (uint e = start; e < stop; ++e)
1316
                        {
1317
                                QString replacementName = pgit->itemText.charStyle(e).font().replacementName();
24429 jghali 1318
                                usedFonts.insert(replacementName, DocFonts[replacementName]);
21709 jghali 1319
                        }
3133 fschmid 1320
                }
1321
        }
21709 jghali 1322
/*      if (Options.docInfoMarks)
3133 fschmid 1323
        {
21709 jghali 1324
                StdFonts.insert("/Helvetica", "");
1325
        } */
1326
        QStringList patterns = doc.getPatternDependencyList(doc.getUsedPatterns());
1327
        for (int c = 0; c < patterns.count(); ++c)
1328
        {
1329
                ScPattern pa = doc.docPatterns[patterns[c]];
1330
                allItems = pa.items;
1331
                while (allItems.count() > 0)
3133 fschmid 1332
                {
21709 jghali 1333
                        pgit = allItems.takeFirst();
1334
                        if (pgit->isGroup() || pgit->isTable())
17312 fschmid 1335
                        {
22224 jghali 1336
                                allItems = pgit->getChildren() + allItems;
21709 jghali 1337
                                continue;
17312 fschmid 1338
                        }
16953 fschmid 1339
                        if ((pgit->itemType() == PageItem::TextFrame) || (pgit->itemType() == PageItem::PathText))
8092 fschmid 1340
                        {
16953 fschmid 1341
                                if (pgit->isAnnotation())
12763 fschmid 1342
                                {
17952 fschmid 1343
                                        if ((pgit->annotation().Type() == Annotation::Checkbox) || (pgit->annotation().Type() == Annotation::RadioButton))
16953 fschmid 1344
                                                StdFonts.insert("/ZapfDingbats", "");
21709 jghali 1345
                                        if (pgit->itemText.length() > 0)
16953 fschmid 1346
                                        {
23548 jghali 1347
                                                if (Options.Version < PDFVersion::PDF_14)
16953 fschmid 1348
                                                        StdFonts.insert(ind2PDFabr[pgit->annotation().Font()], "");
21707 jghali 1349
                                                QString replacementName = pgit->itemText.defaultStyle().charStyle().font().replacementName();
24429 jghali 1350
                                                usedFonts.insert(replacementName, DocFonts[replacementName]);
16953 fschmid 1351
                                        }
12763 fschmid 1352
                                }
17751 jghali 1353
                                uint start = pgit->isTextFrame() ? (uint) pgit->firstInFrame() : 0;
1354
                                uint stop  = pgit->isTextFrame() ? (uint) pgit->lastInFrame() + 1 : (uint) pgit->itemText.length();
1355
                                for (uint e = start; e < stop; ++e)
16953 fschmid 1356
                                {
21707 jghali 1357
                                        QString replacementName = pgit->itemText.charStyle(e).font().replacementName();
24429 jghali 1358
                                        usedFonts.insert(replacementName, DocFonts[replacementName]);
16953 fschmid 1359
                                }
8092 fschmid 1360
                        }
3133 fschmid 1361
                }
1362
        }
24429 jghali 1363
        return usedFonts;
20103 avox 1364
}
17628 jghali 1365
 
20103 avox 1366
static QByteArray sanitizeFontName(QString fn)
1367
{
24129 jghali 1368
        return Pdf::toPdfDocEncoding(fn.replace( QRegExp("[\\s\\/\\{\\[\\]\\}\\<\\>\\(\\)\\%]"), "_"));
20103 avox 1369
}
17628 jghali 1370
 
22507 jghali 1371
static QList<Pdf::Resource> asColorSpace(const QList<PdfICCD>& iccCSlist)
20103 avox 1372
{
20112 jghali 1373
        QList<Pdf::Resource> result;
22507 jghali 1374
        for (const Pdf::Resource& r : iccCSlist)
20112 jghali 1375
                result.append(r);
1376
        return result;
20103 avox 1377
}
1378
 
22507 jghali 1379
static QList<Pdf::Resource> asColorSpace(const QList<PdfSpotC>& spotMapValues)
20103 avox 1380
{
20112 jghali 1381
        QList<Pdf::Resource> result;
22507 jghali 1382
        for (const Pdf::Resource& r : spotMapValues)
20112 jghali 1383
                result.append(r);
1384
        return result;
20103 avox 1385
}
1386
 
1387
void PDFLibCore::PDF_WriteStandardFonts()
1388
{
20112 jghali 1389
        int a = 0;
1390
        QMap<QString, QString>::Iterator itStd;
1391
        for (itStd = StdFonts.begin(); itStd != StdFonts.end(); ++itStd)
1392
        {
1393
                PdfId fontObject = writer.newObject();
1394
                writer.startObj(fontObject);
1395
                PutDoc("<<\n/Type /Font\n/Subtype /Type1\n");
23561 jghali 1396
                PutDoc("/Name /FoStd" + Pdf::toPdf(a) + "\n");
1397
                PutDoc("/BaseFont " + Pdf::toPdfDocEncoding(itStd.key()) + "\n");
20112 jghali 1398
                if (itStd.key() != "/ZapfDingbats")
1399
                {
1400
                        PutDoc("/Encoding << \n");
1401
                        PutDoc("/Differences [ \n");
1402
                        PutDoc("24 /breve /caron /circumflex /dotaccent /hungarumlaut /ogonek /ring /tilde\n");
1403
                        PutDoc("39 /quotesingle 96 /grave 128 /bullet /dagger /daggerdbl /ellipsis /emdash /endash /florin /fraction /guilsinglleft /guilsinglright\n");
1404
                        PutDoc("/minus /perthousand /quotedblbase /quotedblleft /quotedblright /quoteleft /quoteright /quotesinglbase /trademark /fi /fl /Lslash /OE /Scaron\n");
21744 jghali 1405
                        PutDoc("/Ydieresis /Zcaron /dotlessi /lslash /oe /scaron /zcaron 160 /Euro 164 /currency 166 /brokenbar 168 /dieresis /copyright /ordfeminine 172 /logicalnot\n");
20112 jghali 1406
                        PutDoc("/.notdef /registered /macron /degree /plusminus /twosuperior /threesuperior /acute /mu 183 /periodcentered /cedilla /onesuperior /ordmasculine\n");
1407
                        PutDoc("188 /onequarter /onehalf /threequarters 192 /Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla /Egrave /Eacute /Ecircumflex\n");
1408
                        PutDoc("/Edieresis /Igrave /Iacute /Icircumflex /Idieresis /Eth /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply /Oslash\n");
1409
                        PutDoc("/Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn /germandbls /agrave /aacute /acircumflex /atilde /adieresis /aring /ae /ccedilla\n");
1410
                        PutDoc("/egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex /idieresis /eth /ntilde /ograve /oacute /ocircumflex /otilde /odieresis\n");
1411
                        PutDoc("/divide /oslash /ugrave /uacute /ucircumflex /udieresis /yacute /thorn /ydieresis\n");
1412
                        PutDoc("] >>\n");
1413
                }
1414
                PutDoc(">>");
1415
                writer.endObj(fontObject);
23561 jghali 1416
                pageData.FObjects["FoStd" + Pdf::toPdf(a)] = fontObject;
1417
                itStd.value() = "FoStd" + Pdf::toPdf(a);
20112 jghali 1418
                a++;
1419
        }
20103 avox 1420
}
1421
 
1422
 
24429 jghali 1423
PdfFont PDFLibCore::PDF_WriteType3Font(const QByteArray& name, ScFace& face, const QMap<uint, QString>& usedGlyphs)
20103 avox 1424
{
20112 jghali 1425
        PdfFont result;
1426
        result.name = Pdf::toName(name);
23759 jghali 1427
        result.usage = Used_in_Content;
20112 jghali 1428
        result.method = Use_Type3;
1429
        result.encoding = Encode_256;
20103 avox 1430
 
22008 jghali 1431
        // TrueType font rasterizers use non-zero winding file rule,
1432
        // vs even-odd for current Postscript font rasterizers
1433
        // Note: OTF CFF2 will also use non-zero winding rule, so
1434
        // change code below when adding CCF2 support to Scribus
1435
        // ref: https://www.microsoft.com/typography/OTSpec/cff2.htm
1436
        bool useNonZeroRule = (face.type() == ScFace::TTF);
1437
 
20112 jghali 1438
        uint SubFonts = 0;
1439
        int glyphCount = 0;
1440
        double minx =  std::numeric_limits<double>::max();
1441
        double miny =  std::numeric_limits<double>::max();
1442
        double maxx = -std::numeric_limits<double>::max();
1443
        double maxy = -std::numeric_limits<double>::max();
22142 jghali 1444
 
20112 jghali 1445
        QList<uint> glyphWidths;
1446
        QList<QByteArray> charProcs;
22142 jghali 1447
        QStringList toUnicodeMaps;
1448
        QList<int> toUnicodeMapsCount;
22719 jghali 1449
        QString toUnicodeMap;
22142 jghali 1450
        int toUnicodeMapCounter = 0;
20112 jghali 1451
        QByteArray encoding = "<< /Type /Encoding\n/Differences [ 0\n";
1452
        QByteArray fon;
1453
        QMap<uint, uint> glyphMapping;
22142 jghali 1454
 
20112 jghali 1455
        ScFace::FaceEncoding gl;
1456
        face.glyphNames(gl);
22142 jghali 1457
 
24429 jghali 1458
        for (auto ig = usedGlyphs.cbegin(); ig != usedGlyphs.cend(); ++ig)
20112 jghali 1459
        {
1460
                FPoint np, np1, np2;
1461
                bool nPath = true;
1462
                fon.resize(0);
24429 jghali 1463
 
1464
                uint gid = ig.key();
1465
                FPointArray glyphOutline = face.glyphOutline(gid);
1466
                if (glyphOutline.size() > 3)
20112 jghali 1467
                {
24429 jghali 1468
                        FPointArray gly = glyphOutline;
20112 jghali 1469
                        QTransform mat;
1470
                        mat.scale(100.0, -100.0);
1471
                        gly.map(mat);
1472
                        gly.translate(0, 1000);
23563 jghali 1473
                        for (int poi = 0; poi < gly.size() - 3; poi += 4)
20112 jghali 1474
                        {
1475
                                if (gly.isMarker(poi))
1476
                                {
1477
                                        fon += "h\n";
1478
                                        nPath = true;
1479
                                        continue;
1480
                                }
1481
                                if (nPath)
1482
                                {
1483
                                        np = gly.point(poi);
23561 jghali 1484
                                        fon += FToStr(np.x()) + " " + FToStr(np.y()) + " m\n";
20112 jghali 1485
                                        nPath = false;
1486
                                }
24429 jghali 1487
                                np = gly.point(poi + 1);
1488
                                np1 = gly.point(poi + 3);
1489
                                np2 = gly.point(poi + 2);
20112 jghali 1490
                                fon += FToStr(np.x()) + " " + FToStr(np.y()) + " " + FToStr(np1.x()) + " " + FToStr(np1.y()) + " " + FToStr(np2.x()) + " " + FToStr(np2.y()) + " c\n";
1491
                        }
22008 jghali 1492
                        fon += useNonZeroRule? "h f\n" : "h f*\n";
20112 jghali 1493
                        np = getMinClipF(&gly);
1494
                        np1 = getMaxClipF(&gly);
1495
                }
1496
                else
1497
                {
20684 jghali 1498
                        // 13654: make evince emit warning about "Syntax Error: No current point in closepath"
1499
                        // PDF specification is not quite clear about that, but a single "h" operator might not
1500
                        // be a valid way to make an empty path
1501
                        // fon = "h";
20112 jghali 1502
                        np = FPoint(0, 0);
1503
                        np1 = FPoint(0, 0);
1504
                }
23563 jghali 1505
                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 1506
                minx = qMin(minx, np.x());
1507
                miny = qMin(miny, np.y());
1508
                maxx = qMax(maxx, np1.x());
1509
                maxy = qMax(maxy, np1.y());
1510
                glyphWidths.append(qRound(np1.x()));
22142 jghali 1511
 
20112 jghali 1512
                PdfId charProcObject = writer.newObject();
1513
                writer.startObj(charProcObject);
1514
                if (Options.Compress)
1515
                        fon = CompressArray(fon);
22717 jghali 1516
                PutDoc("<< /Length " + Pdf::toPdf(fon.length() + 1));
20112 jghali 1517
                if (Options.Compress)
1518
                        PutDoc("\n/Filter /FlateDecode");
22717 jghali 1519
                PutDoc("\n>>\nstream\n" + EncStream(fon, charProcObject) + "\nendstream");
20112 jghali 1520
                writer.endObj(charProcObject);
22142 jghali 1521
 
22717 jghali 1522
                // #15449 : in some cases we cannot retrieve glyph names for all glyphs we need
1523
                // using ScFace's glyphNames(), so generate custom glyph names using glyph index.
1524
                // With Type 3 fonts we have more flexibility than with other fonts.
1525
                QString glGlyphName, glToUnicode;
24429 jghali 1526
                auto  glyphIt = gl.constFind(gid);
22717 jghali 1527
                if ((glyphIt != gl.constEnd()) && (glyphIt->charcode > 0))
1528
                        glToUnicode = QString::asprintf("%04X", glyphIt->charcode);
24429 jghali 1529
                if (glToUnicode.isEmpty() && usedGlyphs.contains(gid))
1530
                {
1531
                        QString uniStr = usedGlyphs.value(gid);
1532
                        for (int i = 0; i < uniStr.length(); ++i)
1533
                                glToUnicode += QString::asprintf("%04X", uniStr.at(i).unicode());
1534
                }
1535
                if (glToUnicode.isEmpty())
22717 jghali 1536
                        glToUnicode = QString("0000");
24429 jghali 1537
                glGlyphName = "gly" + QString::asprintf("%04X", gid);
22717 jghali 1538
 
1539
                charProcs.append(Pdf::toName(glGlyphName) + " " + Pdf::toPdf(charProcObject) + " 0 R\n");
1540
                encoding += Pdf::toName(glGlyphName) + " ";
24429 jghali 1541
                glyphMapping.insert(gid, glyphCount + SubFonts * 256);
22717 jghali 1542
 
1543
                QString tmp = QString::asprintf("%02X", glyphCount);
1544
                toUnicodeMap += "<" + Pdf::toAscii(tmp) + "> <" + Pdf::toAscii(glToUnicode) + ">\n";
22142 jghali 1545
                toUnicodeMapCounter++;
1546
                if (toUnicodeMapCounter == 100)
1547
                {
1548
                        toUnicodeMaps.append(toUnicodeMap);
1549
                        toUnicodeMapsCount.append(toUnicodeMapCounter);
22717 jghali 1550
                        toUnicodeMap.clear();
22142 jghali 1551
                        toUnicodeMapCounter = 0;
1552
                }
1553
 
20112 jghali 1554
                glyphCount++;
24429 jghali 1555
                int glyphsLeft = usedGlyphs.count() - SubFonts * 256;
20112 jghali 1556
                if ((glyphCount > 255) || (glyphCount == glyphsLeft))
1557
                {
1558
                        PdfId fontWidths = writer.newObject();
1559
                        writer.startObj(fontWidths);
1560
                        PutDoc("[ ");
1561
                        for (int ww = 0; ww < glyphWidths.count(); ++ww)
1562
                        {
23561 jghali 1563
                                PutDoc(Pdf::toPdf(glyphWidths[ww]) + " ");
20112 jghali 1564
                        }
1565
                        PutDoc("]");
1566
                        writer.endObj(fontWidths);
1567
                        PdfId fontCharProcs = writer.newObject();
1568
                        writer.startObj(fontCharProcs);
1569
                        PutDoc("<<\n");
1570
                        for (int ww = 0; ww < charProcs.count(); ++ww)
1571
                        {
1572
                                PutDoc(charProcs[ww]);
1573
                        }
1574
                        PutDoc(">>");
1575
                        writer.endObj(fontCharProcs);
1576
                        PdfId fontEncoding = writer.newObject();
1577
                        writer.startObj(fontEncoding);
1578
                        PutDoc(encoding);
1579
                        PutDoc("]\n");
1580
                        PutDoc(">>");
1581
                        writer.endObj(fontEncoding);
22142 jghali 1582
 
1583
                        if (toUnicodeMapCounter != 0)
1584
                        {
1585
                                toUnicodeMaps.append(toUnicodeMap);
1586
                                toUnicodeMapsCount.append(toUnicodeMapCounter);
1587
                        }
1588
 
22719 jghali 1589
                        QByteArray toUnicodeMapStream;
22142 jghali 1590
                        toUnicodeMapStream += "/CIDInit /ProcSet findresource begin\n";
1591
                        toUnicodeMapStream += "12 dict begin\n";
1592
                        toUnicodeMapStream += "begincmap\n";
1593
                        toUnicodeMapStream += "/CIDSystemInfo <<\n";
1594
                        toUnicodeMapStream += "/Registry (Adobe)\n";
1595
                        toUnicodeMapStream += "/Ordering (UCS)\n";
1596
                        toUnicodeMapStream += "/Supplement 0\n";
1597
                        toUnicodeMapStream += ">> def\n";
1598
                        toUnicodeMapStream += "/CMapName /Adobe-Identity-UCS def\n";
1599
                        toUnicodeMapStream += "/CMapType 2 def\n";
1600
                        toUnicodeMapStream += "1 begincodespacerange\n";
1601
                        toUnicodeMapStream += "<0000> <FFFF>\n";
1602
                        toUnicodeMapStream += "endcodespacerange\n";
1603
                        for (int uniC = 0; uniC < toUnicodeMaps.count(); uniC++)
1604
                        {
1605
                                toUnicodeMapStream += Pdf::toPdf(toUnicodeMapsCount[uniC]);
1606
                                toUnicodeMapStream += " beginbfchar\n";
1607
                                toUnicodeMapStream += toUnicodeMaps[uniC];
1608
                                toUnicodeMapStream += "endbfchar\n";
1609
                        }
1610
                        toUnicodeMapStream += "endcmap\n";
1611
                        toUnicodeMapStream += "CMapName currentdict /CMap defineresource pop\n";
1612
                        toUnicodeMapStream += "end\n";
1613
                        toUnicodeMapStream += "end\n";
1614
                        PdfId fontToUnicode = WritePDFStream(toUnicodeMapStream);
1615
 
20112 jghali 1616
                        PdfId font3Object = writer.newObject();
1617
                        writer.startObj(font3Object);
1618
                        PutDoc("<<\n/Type /Font\n/Subtype /Type3\n");
23561 jghali 1619
                        PutDoc("/Name /" + name + "S" + Pdf::toPdf(SubFonts) + "\n");
20112 jghali 1620
                        PutDoc("/FirstChar 0\n");
23561 jghali 1621
                        PutDoc("/LastChar " + Pdf::toPdf(glyphCount-1) + "\n");
1622
                        PutDoc("/Widths " + Pdf::toPdf(fontWidths) + " 0 R\n");
1623
                        PutDoc("/CharProcs " + Pdf::toPdf(fontCharProcs) + " 0 R\n");
23563 jghali 1624
                        PutDoc("/FontBBox [" + Pdf::toPdf(qRound(minx)) + " " + Pdf::toPdf(qRound(miny)) + " " + Pdf::toPdf(qRound(maxx)) + " " + Pdf::toPdf(qRound(maxy)) + "]\n");
20112 jghali 1625
                        PutDoc("/FontMatrix [0.001 0 0 0.001 0 0]\n");
23561 jghali 1626
                        PutDoc("/Encoding " + Pdf::toPdf(fontEncoding) + " 0 R\n");
22142 jghali 1627
                        PutDoc("/ToUnicode " + Pdf::toPdf(fontToUnicode) + " 0 R\n");
20112 jghali 1628
                        PutDoc(">>");
1629
                        writer.endObj(font3Object);
23561 jghali 1630
                        pageData.FObjects[name + "S" + Pdf::toPdf(SubFonts)] = font3Object;
20112 jghali 1631
                        charProcs.clear();
1632
                        glyphWidths.clear();
22142 jghali 1633
                        toUnicodeMaps.clear();
1634
                        toUnicodeMapsCount.clear();
1635
                        toUnicodeMap.clear();
1636
                        toUnicodeMapCounter = 0;
20112 jghali 1637
                        glyphCount = 0;
1638
                        ++SubFonts;
1639
                        minx =  std::numeric_limits<double>::max();
1640
                        miny =  std::numeric_limits<double>::max();
1641
                        maxx = -std::numeric_limits<double>::max();
1642
                        maxy = -std::numeric_limits<double>::max();
1643
                        encoding = "<< /Type /Encoding\n/Differences [ 0\n";
1644
                }
1645
        }
1646
        result.glyphmap = glyphMapping;
1647
        return result;
20103 avox 1648
}
1649
 
1650
 
24429 jghali 1651
PdfFont PDFLibCore::PDF_WriteGlyphsAsXForms(const QByteArray& fontName, ScFace& face, const QMap<uint, QString>& usedGlyphs)
20103 avox 1652
{
20112 jghali 1653
        PdfFont result;
1654
        result.name = Pdf::toName(fontName);
23729 jghali 1655
        result.usage = Used_in_Content;
20112 jghali 1656
        result.method = Use_XForm;
1657
        result.encoding = Encode_224;
20103 avox 1658
 
22008 jghali 1659
        // TrueType font rasterizers use non-zero winding file rule,
1660
        // vs even-odd for current Postscript font rasterizers
1661
        // Note: OTF CFF2 will also use non-zero winding rule, so
1662
        // change code below when adding CCF2 support to Scribus
1663
        // ref: https://www.microsoft.com/typography/OTSpec/cff2.htm
1664
        bool useNonZeroRule = (face.type() == ScFace::TTF);
1665
 
20112 jghali 1666
        QByteArray fon;
24429 jghali 1667
        for (auto ig = usedGlyphs.cbegin(); ig != usedGlyphs.cend(); ++ig)
20112 jghali 1668
        {
1669
                FPoint np, np1, np2;
1670
                bool nPath = true;
1671
                fon.resize(0);
24429 jghali 1672
 
1673
                uint gid = ig.key();
1674
                FPointArray glyphOutline = face.glyphOutline(gid);
1675
                if (glyphOutline.size() > 3)
20112 jghali 1676
                {
24429 jghali 1677
                        FPointArray gly = glyphOutline;
20112 jghali 1678
                        QTransform mat;
1679
                        mat.scale(0.1, 0.1);
1680
                        gly.map(mat);
23563 jghali 1681
                        for (int poi = 0; poi < gly.size() - 3; poi += 4)
20112 jghali 1682
                        {
1683
                                if (gly.isMarker(poi))
1684
                                {
1685
                                        fon += "h\n";
1686
                                        nPath = true;
1687
                                        continue;
1688
                                }
1689
                                if (nPath)
1690
                                {
1691
                                        np = gly.point(poi);
23561 jghali 1692
                                        fon += FToStr(np.x()) + " " + FToStr(-np.y()) + " m\n";
20112 jghali 1693
                                        nPath = false;
1694
                                }
24429 jghali 1695
                                np = gly.point(poi + 1);
1696
                                np1 = gly.point(poi + 3);
1697
                                np2 = gly.point(poi + 2);
20112 jghali 1698
                                fon += FToStr(np.x()) + " " + FToStr(-np.y()) + " " +
1699
                                FToStr(np1.x()) + " " + FToStr(-np1.y()) + " " +
1700
                                FToStr(np2.x()) + " " + FToStr(-np2.y()) + " c\n";
1701
                        }
22008 jghali 1702
                        fon += useNonZeroRule? "h f\n" : "h f*\n";
20112 jghali 1703
                        np = getMinClipF(&gly);
1704
                        np1 = getMaxClipF(&gly);
1705
                }
1706
                else
1707
                {
20684 jghali 1708
                        // 13654: make evince emit warning about "Syntax Error: No current point in closepath"
1709
                        // PDF specification is not quite clear about that, but a single "h" operator might not
1710
                        // be a valid way to make an empty path
1711
                        fon = " " /*"h"*/;
20112 jghali 1712
                        np = FPoint(0, 0);
1713
                        np1 = FPoint(0, 0);
1714
                }
1715
                PdfId fontGlyphXForm = writer.newObject();
1716
                writer.startObj(fontGlyphXForm);
1717
                PutDoc("<<\n/Type /XObject\n/Subtype /Form\n/FormType 1\n");
23563 jghali 1718
                PutDoc("/BBox [ " + FToStr(np.x()) + " " + FToStr(-np.y()) + " " + FToStr(np1.x()) + " " + FToStr(-np1.y()) + " ]\n");
20112 jghali 1719
                PutDoc("/Resources << /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]\n");
1720
                PutDoc(">>\n");
1721
                if (Options.Compress)
1722
                        fon = CompressArray(fon);
23563 jghali 1723
                PutDoc("/Length " + Pdf::toPdf(fon.length() + 1));
20112 jghali 1724
                if (Options.Compress)
1725
                        PutDoc("\n/Filter /FlateDecode");
23561 jghali 1726
                PutDoc(" >>\nstream\n" + EncStream(fon, fontGlyphXForm) + "\nendstream");
20112 jghali 1727
                writer.endObj(fontGlyphXForm);
24429 jghali 1728
                pageData.XObjects[fontName + "_gl" + Pdf::toPdf(gid)] = fontGlyphXForm;
20112 jghali 1729
        }
1730
        return result;
20103 avox 1731
}
1732
 
1733
PdfId PDFLibCore::PDF_EmbedFontObject(const QByteArray& font, const QByteArray& subtype)
1734
{
20112 jghali 1735
        PdfId embeddedFontObject = writer.newObject();
1736
        writer.startObj(embeddedFontObject);
1737
        int len = font.length();
1738
        QByteArray ttf = (Options.Compress? CompressArray(font) : font);
1739
        //qDebug() << QString("sfnt data: size=%1 compressed=%2").arg(len).arg(bb.length());
1740
        PutDoc("<<\n/Length " + Pdf::toPdf(ttf.length() + 1) + "\n");
1741
        PutDoc("/Length1 " + Pdf::toPdf(len) + "\n");
1742
        if (subtype.size() > 0)
1743
                PutDoc("/Subtype " + subtype);
1744
        if (Options.Compress)
1745
                PutDoc("/Filter /FlateDecode\n");
1746
        PutDoc(">>\nstream\n");
1747
        EncodeArrayToStream(ttf, embeddedFontObject);
1748
        PutDoc("\nendstream");
1749
        writer.endObj(embeddedFontObject);
1750
        return embeddedFontObject;
20103 avox 1751
}
1752
 
22600 craig 1753
QByteArray PDFLibCore::PDF_GenerateSubsetTag(const QByteArray& fontName, const QList<uint>& usedGlyphs)
21775 jghali 1754
{
1755
        uint hash, mod;
1756
        QVector<uint> glyphVec = usedGlyphs.toVector();
1757
 
1758
        hash = qHashBits(fontName.constData(), fontName.size(), 0);
1759
        hash = qHashBits(glyphVec.constData(), glyphVec.size() * sizeof(uint), hash);
1760
 
1761
        QByteArray subsetTag(6, (char) 0);
1762
        for (int i = 0; i < 6; ++i)
1763
        {
1764
                mod = hash % 26;
1765
                subsetTag[i] = 'A' + mod;
1766
                hash = (hash - mod) / 26;
1767
        }
1768
 
1769
        return subsetTag;
1770
}
1771
 
20103 avox 1772
PdfId PDFLibCore::PDF_WriteFontDescriptor(const QByteArray& fontName, ScFace& face, ScFace::FontFormat fformat, PdfId embeddedFontObject)
1773
{
20112 jghali 1774
        PdfId fontDescriptor = writer.newObject();
1775
        writer.startObj(fontDescriptor);
1776
        // TODO: think about QByteArray ScFace::getFontDescriptor() -- AV
1777
        PutDoc("<<\n/Type /FontDescriptor\n");
1778
        PutDoc("/FontName "  + Pdf::toName(fontName) + "\n");
1779
        PutDoc("/FontBBox [ " + Pdf::toAscii(face.pdfFontBBoxAsString()) + " ]\n");
1780
        PutDoc("/Flags ");
1781
        //FIXME: isItalic() should be queried from ScFace, not from Qt -- AV
1782
        //QFontInfo fo = QFontInfo(it.data());
1783
        int pfl = 0;
1784
        if (face.isFixedPitch())
1785
                pfl = pfl ^ 1;
1786
        //if (fo.italic())
1787
        if (face.italicAngleAsString() != "0")
1788
                pfl = pfl ^ 64;
1789
        //                      pfl = pfl ^ 4;
1790
        pfl = pfl ^ 32;
23561 jghali 1791
        PutDoc(Pdf::toPdf(pfl) + "\n");
1792
        PutDoc("/Ascent "      + Pdf::toAscii(face.pdfAscentAsString()) + "\n");
1793
        PutDoc("/Descent "     + Pdf::toAscii(face.pdfDescentAsString()) + "\n");
1794
        PutDoc("/CapHeight "   + Pdf::toAscii(face.pdfCapHeightAsString()) + "\n");
1795
        PutDoc("/ItalicAngle " + Pdf::toAscii(face.italicAngleAsString()) + "\n");
20112 jghali 1796
        PutDoc("/StemV 1\n");
1797
        if (embeddedFontObject != 0)
1798
        {
1799
                if (fformat == ScFace::SFNT || fformat == ScFace::TTCF)
1800
                {
1801
                        if (face.type() == ScFace::OTF)
1802
                        {
23548 jghali 1803
                                PutDoc("/FontFile3 " + Pdf::toPdf(embeddedFontObject) + " 0 R\n");
20112 jghali 1804
                        }
1805
                        else
1806
                        {
23548 jghali 1807
                                PutDoc("/FontFile2 " + Pdf::toPdf(embeddedFontObject) + " 0 R\n");
20112 jghali 1808
                        }
1809
                }
1810
                else if (fformat == ScFace::PFB)
23561 jghali 1811
                        PutDoc("/FontFile " + Pdf::toPdf(embeddedFontObject) + " 0 R\n");
20112 jghali 1812
                else if (fformat == ScFace::PFA)
23561 jghali 1813
                        PutDoc("/FontFile " + Pdf::toPdf(embeddedFontObject) + " 0 R\n");
20112 jghali 1814
        }
1815
        PutDoc(">>");
1816
        writer.endObj(fontDescriptor);
1817
        return fontDescriptor;
20103 avox 1818
}
1819
 
24429 jghali 1820
PdfFont PDFLibCore::PDF_EncodeCidFont(const QByteArray& fontName, ScFace& face, const QByteArray& baseFont, PdfId fontDes, const QMap<uint, QString>& usedGlyphs, const QMap<uint,uint>& glyphmap)
20103 avox 1821
{
20112 jghali 1822
        PdfFont result;
1823
        result.name = Pdf::toName(fontName);
23727 jghali 1824
        result.usage = Used_in_Content;
20112 jghali 1825
        result.method = glyphmap.isEmpty()? Use_Embedded : Use_Subset;
24416 jghali 1826
        result.encoding = glyphmap.isEmpty() ? Encode_IdentityH : Encode_Subset;
20112 jghali 1827
        result.glyphmap = glyphmap;
1828
 
1829
        PdfId fontWidths2 = writer.newObject();
1830
        writer.startObj(fontWidths2);
1831
        QList<QByteArray> toUnicodeMaps;
1832
        QList<int> toUnicodeMapsCount;
22719 jghali 1833
        QByteArray toUnicodeMap;
20112 jghali 1834
        int toUnicodeMapCounter = 0;
24427 jghali 1835
 
24429 jghali 1836
        ScFace::FaceEncoding encoding;
1837
        face.glyphNames(encoding);
1838
        QList<uint> keys = encoding.uniqueKeys();
24427 jghali 1839
        bool seenNotDef = false;
20112 jghali 1840
 
1841
        PutDoc("[ ");
23483 jghali 1842
        for (auto git = keys.begin(); git != keys.end(); ++git)
20112 jghali 1843
        {
24427 jghali 1844
                uint gid = (result.encoding == Encode_Subset) ? glyphmap.value(*git, 0) : *git;
1845
                if (gid == 0 && seenNotDef)
1846
                        continue;
1847
                seenNotDef |= (gid == 0);
1848
 
1849
                PutDoc(Pdf::toPdf(gid) + " [" + Pdf::toPdf(static_cast<int>(face.glyphWidth(*git) * 1000)) + "] ");
1850
                QString tmp = QString::asprintf("%04X", gid);
24429 jghali 1851
                QString tmp2;
1852
                auto  glyphIt = encoding.constFind(*git);
1853
                if ((glyphIt != encoding.constEnd()) && (glyphIt->charcode > 0))
1854
                        tmp2 = glyphIt->toUnicode;
1855
                if (tmp2.isEmpty() && usedGlyphs.contains(*git))
1856
                {
1857
                        QString uniStr = usedGlyphs.value(*git);
1858
                        for (int i = 0; i < uniStr.length(); ++i)
1859
                                tmp2 += QString::asprintf("%04X", uniStr.at(i).unicode());
1860
                }
1861
                if (tmp2.isEmpty())
1862
                        tmp2 = "0000";
24427 jghali 1863
                toUnicodeMap += "<" + Pdf::toAscii(tmp) + "> <" + Pdf::toAscii(tmp2) + ">\n";
1864
                toUnicodeMapCounter++;
1865
                if (toUnicodeMapCounter == 100)
20112 jghali 1866
                {
24427 jghali 1867
                        toUnicodeMaps.append(toUnicodeMap);
1868
                        toUnicodeMapsCount.append(toUnicodeMapCounter);
1869
                        toUnicodeMap.clear();
1870
                        toUnicodeMapCounter = 0;
20112 jghali 1871
                }
1872
        }
1873
        PutDoc("]");
1874
        writer.endObj(fontWidths2);
1875
        if (toUnicodeMapCounter != 0)
1876
        {
1877
                toUnicodeMaps.append(toUnicodeMap);
1878
                toUnicodeMapsCount.append(toUnicodeMapCounter);
1879
        }
22719 jghali 1880
        QByteArray toUnicodeMapStream;
20112 jghali 1881
        toUnicodeMapStream += "/CIDInit /ProcSet findresource begin\n";
1882
        toUnicodeMapStream += "12 dict begin\n";
1883
        toUnicodeMapStream += "begincmap\n";
1884
        toUnicodeMapStream += "/CIDSystemInfo <<\n";
1885
        toUnicodeMapStream += "/Registry (Adobe)\n";
1886
        toUnicodeMapStream += "/Ordering (UCS)\n";
1887
        toUnicodeMapStream += "/Supplement 0\n";
1888
        toUnicodeMapStream += ">> def\n";
1889
        toUnicodeMapStream += "/CMapName /Adobe-Identity-UCS def\n";
1890
        toUnicodeMapStream += "/CMapType 2 def\n";
1891
        toUnicodeMapStream += "1 begincodespacerange\n";
1892
        toUnicodeMapStream += "<0000> <FFFF>\n";
1893
        toUnicodeMapStream += "endcodespacerange\n";
1894
        for (int uniC = 0; uniC < toUnicodeMaps.count(); uniC++)
1895
        {
1896
                toUnicodeMapStream += Pdf::toPdf(toUnicodeMapsCount[uniC]);
1897
                toUnicodeMapStream += " beginbfchar\n";
1898
                toUnicodeMapStream += toUnicodeMaps[uniC];
1899
                toUnicodeMapStream += "endbfchar\n";
1900
        }
1901
        toUnicodeMapStream += "endcmap\n";
1902
        toUnicodeMapStream += "CMapName currentdict /CMap defineresource pop\n";
1903
        toUnicodeMapStream += "end\n";
1904
        toUnicodeMapStream += "end\n";
1905
        PdfId fontToUnicode2 = WritePDFStream(toUnicodeMapStream);
1906
        PdfId fontObject2 = writer.newObject();
1907
        writer.startObj(fontObject2);
1908
        PutDoc("<<\n/Type /Font\n/Subtype /Type0\n");
1909
        PutDoc("/Name " + Pdf::toName(fontName) + "\n");
23563 jghali 1910
        PutDoc("/BaseFont " + Pdf::toName(baseFont) + "\n");
20112 jghali 1911
        PutDoc("/Encoding /Identity-H\n");
23561 jghali 1912
        PutDoc("/ToUnicode " + Pdf::toPdf(fontToUnicode2) + " 0 R\n");
20112 jghali 1913
        PutDoc("/DescendantFonts [");
1914
        PutDoc("<</Type /Font");
1915
        if (face.type() == ScFace::OTF)
1916
                PutDoc("/Subtype /CIDFontType0");
1917
        else
1918
                PutDoc("/Subtype /CIDFontType2");
21759 jghali 1919
        PutDoc("/BaseFont " + Pdf::toName(baseFont));
23563 jghali 1920
        PutDoc("/FontDescriptor " + Pdf::toPdf(fontDes) + " 0 R");
20112 jghali 1921
        PutDoc("/CIDSystemInfo <</Ordering(Identity)/Registry(Adobe)/Supplement 0>>");
1922
        PutDoc("/DW 1000");
23561 jghali 1923
        PutDoc("/W " + Pdf::toPdf(fontWidths2) + " 0 R");
20112 jghali 1924
        PutDoc("/CIDToGIDMap /Identity");
1925
        PutDoc(">>"); // close CIDFont dictionary
1926
        PutDoc("]\n"); // close DescendantFonts array
1927
        PutDoc(">>");
1928
        writer.endObj(fontObject2);
1929
        pageData.FObjects[fontName] = fontObject2;
1930
        return result;
20103 avox 1931
}
1932
 
1933
 
24429 jghali 1934
PdfFont PDFLibCore::PDF_EncodeSimpleFont(const QByteArray& fontName, ScFace& face, const QByteArray& baseFont, const QByteArray& subtype, bool isEmbedded, PdfId fontDes, const QMap<uint, QString>& usedGlyphs)
20103 avox 1935
{
20112 jghali 1936
        PdfFont result;
1937
        result.name = Pdf::toName(fontName);
23728 jghali 1938
        result.usage = Used_in_Content;
20112 jghali 1939
        result.method = isEmbedded? Use_Embedded : Use_System;
1940
        result.encoding = Encode_224;
24429 jghali 1941
 
1942
        ScFace::FaceEncoding gl;
1943
        face.glyphNames(gl);
20112 jghali 1944
 
1945
        int nglyphs = 0;
22717 jghali 1946
        for (auto gli = gl.cbegin(); gli != gl.cend(); ++gli)
20112 jghali 1947
        {
1948
                if (gli.key() > static_cast<uint>(nglyphs))
1949
                        nglyphs = gli.key();
1950
        }
1951
        ++nglyphs;
1952
        //                      qDebug() << QString("pdflib: nglyphs %1 max %2").arg(nglyphs).arg(face.maxGlyph());
1953
 
1954
        uint Fcc = nglyphs / 224;
1955
        if ((nglyphs % 224) != 0)
1956
                Fcc += 1;
1957
        for (uint Fc = 0; Fc < Fcc; ++Fc)
1958
        {
1959
                PdfId fontWidths2 = writer.newObject();
1960
                writer.startObj(fontWidths2);
1961
                int chCount = 32;
1962
                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 ");
1963
                for (int ww = 32; ww < 256; ++ww)
1964
                {
1965
                        uint glyph = 224 * Fc + ww - 32;
1966
                        if (gl.contains(glyph))
23561 jghali 1967
                                PutDoc(Pdf::toPdf(static_cast<int>(face.glyphWidth(glyph)* 1000)) + " ");
20112 jghali 1968
                        else
1969
                                PutDoc("0 ");
1970
                        chCount++;
1971
                        if (signed(glyph) == nglyphs-1)
1972
                                break;
1973
                }
1974
                PutDoc("]");
1975
                writer.endObj(fontWidths2);
22717 jghali 1976
 
20112 jghali 1977
                QStringList toUnicodeMaps;
22719 jghali 1978
                QList<int>  toUnicodeMapsCount;
1979
                QString toUnicodeMap;
20112 jghali 1980
                int toUnicodeMapCounter = 0;
22717 jghali 1981
                int crc = 0;
1982
                bool startOfSeq = true;
1983
 
1984
                PdfId fontEncoding2 = writer.newObject();
1985
                writer.startObj(fontEncoding2);
20112 jghali 1986
                PutDoc("<< /Type /Encoding\n");
1987
                PutDoc("/Differences [ \n");
1988
                for (int ww2 = 32; ww2 < 256; ++ww2)
1989
                {
1990
                        uint glyph = 224 * Fc + ww2 - 32;
1991
                        ScFace::FaceEncoding::ConstIterator glIt = gl.find(glyph);
22143 jghali 1992
                        if (glIt != gl.cend() && !glIt.value().glyphName.isEmpty())
20112 jghali 1993
                        {
22143 jghali 1994
                                const ScFace::GlyphEncoding& glEncoding = glIt.value();
20112 jghali 1995
                                if (startOfSeq)
1996
                                {
23561 jghali 1997
                                        PutDoc(Pdf::toPdf(ww2) + " ");
20112 jghali 1998
                                        startOfSeq = false;
1999
                                }
23561 jghali 2000
                                PutDoc(Pdf::toName(glEncoding.glyphName) + " ");
23483 jghali 2001
                                QString tmp = QString::asprintf("%02X", ww2);
24429 jghali 2002
                                QString tmp2 = (glEncoding.charcode > 0) ? glEncoding.toUnicode : QString();
2003
                                if (tmp2.isEmpty() && usedGlyphs.contains(glyph))
2004
                                {
2005
                                        QString uniStr = usedGlyphs.value(glyph);
2006
                                        for (int i = 0; i < uniStr.length(); ++i)
2007
                                                tmp2 += QString::asprintf("%04X", uniStr.at(i).unicode());
2008
                                }
2009
                                if (tmp2.isEmpty())
2010
                                        tmp2 = "0000";
20112 jghali 2011
                                toUnicodeMap += "<" + Pdf::toAscii(tmp) + "> <" + Pdf::toAscii(tmp2) + ">\n";
2012
                                toUnicodeMapCounter++;
2013
                                if (toUnicodeMapCounter == 100)
2014
                                {
2015
                                        toUnicodeMaps.append(toUnicodeMap);
2016
                                        toUnicodeMapsCount.append(toUnicodeMapCounter);
22717 jghali 2017
                                        toUnicodeMap.clear();
20112 jghali 2018
                                        toUnicodeMapCounter = 0;
2019
                                }
2020
                                crc++;
2021
                        }
2022
                        else
2023
                        {
2024
                                startOfSeq = true;
2025
                        }
2026
                        if (signed(glyph) == nglyphs-1)
2027
                                break;
2028
                        if (crc > 8)
2029
                        {
2030
                                PutDoc("\n");
2031
                                crc = 0;
2032
                        }
2033
                }
2034
                if (toUnicodeMapCounter != 0)
2035
                {
2036
                        toUnicodeMaps.append(toUnicodeMap);
2037
                        toUnicodeMapsCount.append(toUnicodeMapCounter);
2038
                }
2039
                PutDoc("]\n");
2040
                PutDoc(">>");
2041
                writer.endObj(fontEncoding2);
22719 jghali 2042
                QByteArray toUnicodeMapStream;