Subversion Repositories Scribus

Rev

Rev 23729 | Rev 23760 | 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)
4264 craig 528
        : QObject(&docu),
4223 craig 529
        doc(docu),
22600 craig 530
        ActPageP(nullptr),
14925 cbradney 531
        Options(doc.pdfOptions()),
22600 craig 532
        Bvie(nullptr),
533
        ucs2Codec(nullptr),
4223 craig 534
        ResNam("RE"),
535
        ResCount(0),
536
        NDnam("LI"),
537
        NDnum(0),
538
        spotNam("Spot"),
539
        spotCount(0),
22600 craig 540
        progressDialog(nullptr),
4223 craig 541
        abortExport(false),
20089 jghali 542
        usingGUI(ScCore->usingGUI()),
543
        bleedDisplacementX(0),
544
        bleedDisplacementY(0)
3133 fschmid 545
{
20103 avox 546
//      KeyGen.resize(32);
547
//      OwnerKey.resize(32);
548
//      UserKey.resize(32);
549
//      FileID.resize(16);
550
//      EncryKey.resize(5);
3133 fschmid 551
        Catalog.Outlines = 2;
552
        Catalog.PageTree = 3;
553
        Catalog.Dest = 4;
554
        Outlines.First = 0;
555
        Outlines.Last = 0;
556
        Outlines.Count = 0;
20103 avox 557
        pageData.ObjNum = 0;
558
        pageData.Thumb = 0;
559
//      int kg_array[] = {0x28, 0xbf, 0x4e, 0x5e, 0x4e, 0x75, 0x8a, 0x41, 0x64, 0x00, 0x4e, 0x56, 0xff, 0xfa,
560
//                        0x01, 0x08, 0x2e, 0x2e, 0x00, 0xb6, 0xd0, 0x68, 0x3e, 0x80, 0x2f, 0x0c, 0xa9, 0xfe,
561
//                        0x64, 0x53, 0x69, 0x7a};
562
//      for (int a = 0; a < 32; ++a)
563
//              KeyGen[a] = kg_array[a];
4028 cbradney 564
        if (usingGUI)
565
        {
10508 cbradney 566
                progressDialog = new MultiProgressDialog( tr("Saving PDF"), CommonStrings::tr_Cancel, doc.scMW());
4224 craig 567
                Q_CHECK_PTR(progressDialog);
568
                QStringList barNames, barTexts;
569
                barNames << "EMP" << "EP" << "ECPI";
5370 cbradney 570
                barTexts << tr("Exporting Master Page:") << tr("Exporting Page:") << tr("Exporting Items on Current Page:");
9803 fschmid 571
                QList<bool> barsNumeric;
5370 cbradney 572
                barsNumeric << true << true << false;
573
                progressDialog->addExtraProgressBars(barNames, barTexts, barsNumeric);
8559 subik 574
                connect(progressDialog, SIGNAL(canceled()), this, SLOT(cancelRequested()));
4028 cbradney 575
        }
3133 fschmid 576
}
577
 
10054 craig 578
PDFLibCore::~PDFLibCore()
4224 craig 579
{
580
        delete progressDialog;
581
}
582
 
4229 craig 583
 
19115 avox 584
bool PDFLibCore::PDF_IsPDFX()
585
{
23548 jghali 586
        if (Options.Version == PDFVersion::PDF_X1a)
19115 avox 587
                return true;
23548 jghali 588
        if (Options.Version == PDFVersion::PDF_X3)
19115 avox 589
                return true;
23548 jghali 590
        if (Options.Version == PDFVersion::PDF_X4)
19115 avox 591
                return true;
592
        return false;
593
}
594
 
23548 jghali 595
bool PDFLibCore::PDF_IsPDFX(const PDFVersion& ver)
19115 avox 596
{
23548 jghali 597
        if (ver == PDFVersion::PDF_X1a)
19115 avox 598
                return true;
23548 jghali 599
        if (ver == PDFVersion::PDF_X3)
19115 avox 600
                return true;
23548 jghali 601
        if (ver == PDFVersion::PDF_X4)
19115 avox 602
                return true;
603
        return false;
604
}
605
 
10054 craig 606
bool PDFLibCore::doExport(const QString& fn, const QString& nam, int Components,
22459 jghali 607
                                          const std::vector<int> & pageNs, const QMap<int, QImage> & thumbs)
3133 fschmid 608
{
22459 jghali 609
        QImage thumb;
11060 jghali 610
        bool ret = false, error = false;
611
        int  pc_exportpages=0;
612
        int  pc_exportmasterpages=0;
4224 craig 613
        if (usingGUI)
614
                progressDialog->show();
23560 jghali 615
 
19114 avox 616
        ucs2Codec = QTextCodec::codecForName("ISO-10646-UCS-2");
17758 jghali 617
        if (!ucs2Codec)
19114 avox 618
                ucs2Codec = QTextCodec::codecForName("UTF-16");
619
        if (!ucs2Codec)
13764 jghali 620
        {
13791 pierre 621
                PDF_Error( tr("Qt build miss both \"UTF-16\" and \"ISO-10646-UCS-2\" text codecs, pdf export is not possible") );
13764 jghali 622
                return false;
623
        }
23560 jghali 624
 
625
        QMap<QString, QMap<uint, FPointArray> > usedFonts;
626
        doc.getUsedFonts(usedFonts);
627
 
23060 craig 628
        if (PDF_Begin_Doc(fn, PrefsManager::instance().appPrefs.fontPrefs.AvailFonts, usedFonts, doc.scMW()->bookmarkPalette->BView))
3133 fschmid 629
        {
630
                QMap<int, int> pageNsMpa;
631
                for (uint a = 0; a < pageNs.size(); ++a)
632
                {
23563 jghali 633
                        pageNsMpa.insert(doc.MasterNames[doc.DocPages.at(pageNs[a] - 1)->masterPageName()], 0);
3133 fschmid 634
                }
4028 cbradney 635
                if (usingGUI)
3133 fschmid 636
                {
23563 jghali 637
                        progressDialog->setOverallTotalSteps(pageNsMpa.count() + pageNs.size());
4028 cbradney 638
                        progressDialog->setTotalSteps("EMP", pageNsMpa.count());
639
                        progressDialog->setTotalSteps("EP", pageNs.size());
640
                        progressDialog->setOverallProgress(0);
641
                        progressDialog->setProgress("EMP", 0);
642
                        progressDialog->setProgress("EP", 0);
643
                }
10018 fschmid 644
                for (int ap = 0; ap < doc.MasterPages.count() && !abortExport; ++ap)
4028 cbradney 645
                {
4264 craig 646
                        if (doc.MasterItems.count() != 0)
3133 fschmid 647
                        {
648
                                if (pageNsMpa.contains(ap))
649
                                {
5243 cbradney 650
                                        qApp->processEvents();
11060 jghali 651
                                        if (!PDF_TemplatePage(doc.MasterPages.at(ap)))
652
                                                error = abortExport = true;
4028 cbradney 653
                                        ++pc_exportmasterpages;
3133 fschmid 654
                                }
655
                        }
4546 subik 656
                        if (usingGUI)
657
                        {
4028 cbradney 658
                                progressDialog->setProgress("EMP", pc_exportmasterpages);
659
                                progressDialog->setOverallProgress(pc_exportmasterpages+pc_exportpages);
660
                        }
3133 fschmid 661
                }
4028 cbradney 662
                for (uint a = 0; a < pageNs.size() && !abortExport; ++a)
3133 fschmid 663
                {
14925 cbradney 664
                        if (doc.pdfOptions().Thumbnails)
22459 jghali 665
                                thumb = thumbs[pageNs[a]];
5243 cbradney 666
                        qApp->processEvents();
4028 cbradney 667
                        if (abortExport) break;
11060 jghali 668
 
22459 jghali 669
                        PDF_Begin_Page(doc.DocPages.at(pageNs[a]-1), thumb);
5243 cbradney 670
                        qApp->processEvents();
4028 cbradney 671
                        if (abortExport) break;
11060 jghali 672
 
14925 cbradney 673
                        if (!PDF_ProcessPage(doc.DocPages.at(pageNs[a]-1), pageNs[a]-1, doc.pdfOptions().doClip))
11060 jghali 674
                                error = abortExport = true;
5243 cbradney 675
                        qApp->processEvents();
4028 cbradney 676
                        if (abortExport) break;
11060 jghali 677
 
22434 jghali 678
                        PDF_End_Page();
4028 cbradney 679
                        pc_exportpages++;
680
                        if (usingGUI)
681
                        {
682
                                progressDialog->setProgress("EP", pc_exportpages);
683
                                progressDialog->setOverallProgress(pc_exportmasterpages+pc_exportpages);
684
                        }
3133 fschmid 685
                }
20681 craig 686
                ret = true;//Even when aborting we return true. Don't want that "couldnt write msg"
4028 cbradney 687
                if (!abortExport)
688
                {
19115 avox 689
                        if (PDF_IsPDFX(doc.pdfOptions().Version))
14925 cbradney 690
                                ret = PDF_End_Doc(ScCore->PrinterProfiles[doc.pdfOptions().PrintProf], nam, Components);
4028 cbradney 691
                        else
12387 jghali 692
                                ret = PDF_End_Doc();
4028 cbradney 693
                }
3133 fschmid 694
                else
4028 cbradney 695
                        closeAndCleanup();
3133 fschmid 696
        }
4029 cbradney 697
        if (usingGUI)
698
                progressDialog->close();
11060 jghali 699
        return (ret && !error);
3133 fschmid 700
}
701
 
22600 craig 702
const QString& PDFLibCore::errorMessage() const
11060 jghali 703
{
704
        return ErrorMessage;
705
}
706
 
22600 craig 707
bool PDFLibCore::exportAborted() const
11441 jghali 708
{
709
        return abortExport;
710
}
711
 
20103 avox 712
//#define StartObj(n) writer.startObj((n))
713
#define PutDoc(s) writer.write(s)
714
//#define newObject() writer.newObject()
3133 fschmid 715
 
716
 
20103 avox 717
//void PDFLibCore::StartObj(PdfId nr)
718
//{
719
//      for (int i=XRef.size(); i < nr; ++i)
720
//              XRef.append(0);
721
//      XRef[nr-1] = bytesWritten();
23563 jghali 722
//      PutDoc(Pdf::toPdf(nr) + " 0 obj\n");
20103 avox 723
//}
724
 
725
//// Encode a string for inclusion in a
726
//// PDF (literal) .
727
//static QByteArray PDFEncode(const QString & in)
728
//{
729
//      QByteArray tmp("");
730
//      for (int d = 0; d < in.length(); ++d)
731
//      {
732
//              QChar cc(in.at(d));
733
//              if ((cc == '(') || (cc == ')') || (cc == '\\'))
734
//                      tmp += '\\';
735
//              else if ((cc == '\r') || (cc == '\n'))
736
//              {
737
//                      tmp += (cc == '\r') ? "\\r" : "\\n";
738
//                      continue;
739
//              }
740
//              tmp += cc;
741
//      }
742
//      return tmp;
743
//}
744
 
745
static QByteArray blendMode(int code)
12080 avox 746
{
747
        switch (code)
748
        {
749
                case 0:
750
                        return("Normal");
751
                        break;
752
                case 1:
753
                        return("Darken");
754
                        break;
755
                case 2:
756
                        return("Lighten");
757
                        break;
758
                case 3:
759
                        return("Multiply");
760
                        break;
761
                case 4:
762
                        return("Screen");
763
                        break;
764
                case 5:
765
                        return("Overlay");
766
                        break;
767
                case 6:
768
                        return("HardLight");
769
                        break;
770
                case 7:
771
                        return("SoftLight");
772
                        break;
773
                case 8:
774
                        return("Difference");
775
                        break;
776
                case 9:
777
                        return("Exclusion");
778
                        break;
779
                case 10:
780
                        return("ColorDodge");
781
                        break;
782
                case 11:
783
                        return("ColorBurn");
784
                        break;
785
                case 12:
786
                        return("Hue");
787
                        break;
788
                case 13:
789
                        return("Saturation");
790
                        break;
791
                case 14:
792
                        return("Color");
793
                        break;
794
                case 15:
795
                        return("Luminosity");
796
                        break;
797
                default:
798
                        return "";
799
        }
14043 jghali 800
}
20103 avox 801
//
802
//QByteArray PDFLibCore::EncodeUTF16(const QString &in)
803
//{
804
//      QString tmp = in;
805
//      QByteArray cres = ucs2Codec->fromUnicode( tmp );
806
//#ifndef WORDS_BIGENDIAN
807
//      // on little endian systems we need to swap bytes:
808
//      uchar sw;
23563 jghali 809
//      for (int d = 0; d < cres.size() - 1; d += 2)
20103 avox 810
//      {
811
//              sw = cres[d];
812
//              cres[d] = cres[d+1];
813
//              cres[d+1] = sw;
814
//      }
815
//#endif
816
//      return cres;
817
//}
12080 avox 818
 
20103 avox 819
QByteArray PDFLibCore::EncStream(const QByteArray & in, PdfId ObjNum)
19114 avox 820
{
821
        if (in.length() < 1)
20103 avox 822
                return QByteArray();
22638 craig 823
        if (!Options.Encrypt)
19114 avox 824
                return in;
22638 craig 825
        return writer.encryptBytes(in, ObjNum);
20103 avox 826
//      rc4_context_t rc4;
827
//      QByteArray tmp(in);
828
//      QByteArray us(tmp.length(), ' ');
829
//      QByteArray ou(tmp.length(), ' ');
830
//      for (int a = 0; a < tmp.length(); ++a)
20112 jghali 831
//              us[a] = (tmp.at(a));
832
//      QByteArray step1 = writer.ComputeRC4Key(ObjNum);
20103 avox 833
//      rc4_init(&rc4, reinterpret_cast<uchar*>(step1.data()), qMin(KeyLen+5, 16));
834
//      rc4_encrypt(&rc4, reinterpret_cast<uchar*>(us.data()), reinterpret_cast<uchar*>(ou.data()), tmp.length());
835
//      QString uk = "";
836
//      for (int cl = 0; cl < tmp.length(); ++cl)
837
//              uk += QChar(ou[cl]);
838
//      return ou;
19114 avox 839
}
840
 
20103 avox 841
QByteArray PDFLibCore::EncString(const QByteArray & in, PdfId ObjNum)
19114 avox 842
{
843
        if (in.length() < 1)
844
                return "<>";
845
        if (!Options.Encrypt)
846
        {
20103 avox 847
                //tmp = "(" + PDFEncode(in) + ")";
20112 jghali 848
                return Pdf::toLiteralString(in);
19114 avox 849
        }
20112 jghali 850
 
20103 avox 851
//      rc4_context_t rc4;
852
//      QByteArray us(in.length(), ' ');
853
//      QByteArray ou(in.length(), ' ');
854
//      for (int a = 0; a < in.length(); ++a)
855
//              us[a] = static_cast<uchar>(QChar(in.at(a)).cell());
856
//      QByteArray step1 = writer.ComputeRC4Key(ObjNum);
857
//      rc4_init(&rc4, reinterpret_cast<uchar*>(step1.data()), qMin(KeyLen+5, 16));
858
//      rc4_encrypt(&rc4, reinterpret_cast<uchar*>(us.data()), reinterpret_cast<uchar*>(ou.data()), in.length());
859
//      QString uk = "";
860
//      for (int cl = 0; cl < in.length(); ++cl)
861
//              uk += QChar(ou[cl]);
20112 jghali 862
 
863
        return Pdf::toHexString(writer.encryptBytes(in, ObjNum));
19114 avox 864
}
865
 
20103 avox 866
QByteArray PDFLibCore::EncStringUTF16(const QString & in, PdfId ObjNum)
19114 avox 867
{
868
        if (in.length() < 1)
869
                return "<>";
870
        if (!Options.Encrypt)
871
        {
20112 jghali 872
                QByteArray us = Pdf::toUTF16(in);
20103 avox 873
//              QString uk = "";
874
//              for (int cl = 0; cl < us.size(); ++cl)
875
//                      uk += QChar(us[cl]);
876
                return Pdf::toHexString(us);
19114 avox 877
        }
20103 avox 878
//      rc4_context_t rc4;
20112 jghali 879
        QByteArray us = Pdf::toUTF16(in);
20103 avox 880
//      QByteArray ou(us.size(), ' ');
881
//      QByteArray step1 = writer.ComputeRC4Key(ObjNum);
882
//      rc4_init(&rc4, reinterpret_cast<uchar*>(step1.data()), qMin(KeyLen+5, 16));
883
//      rc4_encrypt(&rc4, reinterpret_cast<uchar*>(us.data()), reinterpret_cast<uchar*>(ou.data()), ou.size());
884
//      QString uk = "";
885
//      for (int cl = 0; cl < ou.size(); ++cl)
886
//              uk += QChar(ou[cl]);
887
        QByteArray tmp = Pdf::toHexString(writer.encryptBytes(us, ObjNum));
19114 avox 888
        return tmp;
889
}
890
 
20103 avox 891
bool PDFLibCore::EncodeArrayToStream(const QByteArray& in, PdfId ObjNum)
19114 avox 892
{
893
        if (in.size() < 1)
894
                return true;
895
        bool succeed = false;
896
        if (Options.Encrypt)
897
        {
20103 avox 898
                ScStreamFilter* rc4Encode = writer.openStreamFilter(true, ObjNum);
899
                if (rc4Encode->openFilter())
19114 avox 900
                {
20103 avox 901
                        succeed  = rc4Encode->writeData(in.data(), in.size());
902
                        succeed &= rc4Encode->closeFilter();
19114 avox 903
                }
20112 jghali 904
                delete rc4Encode;
19114 avox 905
        }
906
        else
20103 avox 907
                writer.write(in);
908
        return (writer.getOutStream().status() == QDataStream::Ok);
19114 avox 909
}
910
 
20103 avox 911
int PDFLibCore::WriteImageToStream(ScImage& image, PdfId ObjNum, ColorSpaceEnum format, bool precal)
11599 jghali 912
{
17758 jghali 913
        bool fromCmyk, succeed = false;
11599 jghali 914
        int  bytesWritten = 0;
20103 avox 915
 
20112 jghali 916
        ScStreamFilter* rc4Encode = writer.openStreamFilter(Options.Encrypt, ObjNum);
917
        if (rc4Encode->openFilter())
918
        {
919
                switch (format)
920
                {
921
                        case ColorSpaceMonochrome :
922
                                fromCmyk = !Options.UseRGB && !Options.isGrayscale && !(doc.HasCMS && Options.UseProfiles2);
923
                                succeed = image.writeMonochromeDataToFilter(rc4Encode, fromCmyk); break;
924
                        case ColorSpaceGray :
925
                                succeed = image.writeGrayDataToFilter(rc4Encode, precal); break;
926
                        case ColorSpaceCMYK :
927
                                succeed = image.writeCMYKDataToFilter(rc4Encode); break;
928
                        default :
929
                                succeed = image.writeRGBDataToFilter(rc4Encode); break;
930
                }
931
                succeed &= rc4Encode->closeFilter();
932
                bytesWritten = rc4Encode->writtenToStream();
933
                delete rc4Encode;
934
        }
11599 jghali 935
        return (succeed ? bytesWritten : 0);
936
}
937
 
20103 avox 938
int PDFLibCore::WriteJPEGImageToStream(ScImage& image, const QString& fn, PdfId ObjNum, int quality, ColorSpaceEnum format,
17758 jghali 939
                                                                                 bool sameFile, bool precal)
11599 jghali 940
{
941
        bool succeed = true;
942
        int  bytesWritten = 0;
943
        QFileInfo fInfo(fn);
944
        QString   ext = fInfo.suffix().toLower();
945
        QString   jpgFileName, tmpFile;
946
        if (extensionIndicatesJPEG(ext) && sameFile)
947
                jpgFileName = fn;
11445 jghali 948
        else
11599 jghali 949
        {
21526 craig 950
                tmpFile  = QDir::toNativeSeparators(ScPaths::tempFileDir() + "sc.jpg");
17758 jghali 951
                if (format == ColorSpaceGray && (!precal))
11599 jghali 952
                        image.convertToGray();
17758 jghali 953
                if (image.convert2JPG(tmpFile, quality, format == ColorSpaceCMYK, format == ColorSpaceGray))
11599 jghali 954
                        jpgFileName = tmpFile;
955
        }
956
        if (jpgFileName.isEmpty())
11792 fschmid 957
                return 0;
11599 jghali 958
        if (Options.Encrypt)
959
        {
960
                succeed = false;
20112 jghali 961
                ScStreamFilter* rc4Encode = writer.openStreamFilter(true, ObjNum);
962
                if (rc4Encode->openFilter())
11599 jghali 963
                {
20103 avox 964
                        succeed  = copyFileToFilter(jpgFileName, *rc4Encode);
965
                        succeed &= rc4Encode->closeFilter();
966
                        bytesWritten = rc4Encode->writtenToStream();
11599 jghali 967
                }
20112 jghali 968
                delete rc4Encode;
11599 jghali 969
        }
970
        else
971
        {
20103 avox 972
                succeed &= copyFileToStream(jpgFileName, writer.getOutStream());
11599 jghali 973
                QFileInfo jpgInfo(jpgFileName);
974
                bytesWritten = jpgInfo.size();
975
        }
976
        if (!tmpFile.isEmpty() && QFile::exists(tmpFile))
977
                QFile::remove(tmpFile);
978
        return (succeed ? bytesWritten : 0);
11445 jghali 979
}
980
 
20103 avox 981
int PDFLibCore::WriteFlateImageToStream(ScImage& image, PdfId ObjNum, ColorSpaceEnum format, bool precal)
11599 jghali 982
{
20112 jghali 983
        bool fromCmyk, succeed = false;
984
        int  bytesWritten = 0;
985
        ScStreamFilter* rc4Encode = writer.openStreamFilter(Options.Encrypt, ObjNum);
986
        ScFlateEncodeFilter flateEncode(rc4Encode);
987
        if (flateEncode.openFilter())
988
        {
989
                switch (format)
990
                {
991
                        case ColorSpaceMonochrome :
992
                                fromCmyk = !Options.UseRGB && !Options.isGrayscale && !(doc.HasCMS && Options.UseProfiles2);
993
                                succeed = image.writeMonochromeDataToFilter(&flateEncode, fromCmyk); break;
994
                        case ColorSpaceGray :
995
                                succeed = image.writeGrayDataToFilter(&flateEncode, precal); break;
996
                        case ColorSpaceCMYK :
997
                                succeed = image.writeCMYKDataToFilter(&flateEncode); break;
998
                        default :
999
                                succeed = image.writeRGBDataToFilter(&flateEncode); break;
1000
                }
1001
                succeed &= flateEncode.closeFilter();
1002
                bytesWritten = flateEncode.writtenToStream();
1003
        }
1004
        delete rc4Encode;
1005
        return (succeed ? bytesWritten : 0);
11599 jghali 1006
}
1007
 
3133 fschmid 1008
 
19114 avox 1009
 
20103 avox 1010
bool PDFLibCore::PDF_Begin_Doc(const QString& fn, SCFonts &AllFonts, const QMap<QString, QMap<uint, FPointArray> >& DocFonts, BookMView* vi)
19114 avox 1011
{
20112 jghali 1012
        if (!writer.open(fn))
1013
                return false;
1014
 
6407 fschmid 1015
        inPattern = 0;
3133 fschmid 1016
        Bvie = vi;
1017
        BookMinUse = false;
1018
        UsedFontsP.clear();
8454 fschmid 1019
        UsedFontsF.clear();
20112 jghali 1020
 
1021
        writer.writeHeader(Options.Version);
20103 avox 1022
 
20112 jghali 1023
        PDF_Begin_Catalog();
1024
        PDF_Begin_MetadataAndEncrypt();
1025
        PDF_Begin_WriteUsedFonts(AllFonts, PDF_Begin_FindUsedFonts(AllFonts, DocFonts));
1026
        PDF_Begin_Colors();
1027
        PDF_Begin_Layers();
1028
 
1029
        return true;
20103 avox 1030
}
1031
 
1032
void PDFLibCore::PDF_Begin_Catalog()
1033
{
20112 jghali 1034
        writer.startObj(writer.CatalogObj);
22386 jghali 1035
        PutDoc("<<\n/Type /Catalog\n");
1036
        PutDoc("/Pages " + Pdf::toObjRef(writer.PagesObj) + "\n");
1037
        if (!PDF_IsPDFX())
1038
        {
1039
                if (doc.useAcroFormFields())
1040
                {
1041
                        writer.AcroFormObj = writer.newObject();
1042
                        PutDoc("/AcroForm " + Pdf::toObjRef(writer.AcroFormObj) + "\n");
1043
                }
1044
                if (doc.useAnnotations())
1045
                {
1046
                        writer.DestsObj = writer.newObject();
1047
                        PutDoc("/Dests " + Pdf::toObjRef(writer.DestsObj) + "\n");
1048
                }
1049
                if (doc.JavaScripts.count() > 0)
1050
                {
1051
                        writer.NamesObj = writer.newObject();
1052
                        PutDoc("/Names " + Pdf::toObjRef(writer.NamesObj) + "\n");
1053
                }
1054
        }
1055
        if (Options.Bookmarks)
1056
        {
1057
                writer.OutlinesObj = writer.newObject();
1058
                PutDoc("/Outlines " + Pdf::toObjRef(writer.OutlinesObj) + "\n");
1059
        }
1060
        if (Options.Articles)
1061
        {
1062
                writer.ThreadsObj = writer.newObject();
1063
                PutDoc("/Threads " + Pdf::toObjRef(writer.ThreadsObj) + "\n");
1064
        }
23548 jghali 1065
        if (Options.exportsLayers())
20112 jghali 1066
        {
1067
                writer.OCPropertiesObj = writer.newObject();
20103 avox 1068
                PutDoc("/OCProperties " + Pdf::toObjRef(writer.OCPropertiesObj) + "\n");
20112 jghali 1069
        }
1070
        if (PDF_IsPDFX())
19114 avox 1071
        {
20112 jghali 1072
                writer.OutputIntentObj = writer.newObject();
23561 jghali 1073
                PutDoc("/OutputIntents [ " + Pdf::toObjRef(writer.OutputIntentObj) + " ]\n");
20112 jghali 1074
        }
23548 jghali 1075
        if ((Options.Version == PDFVersion::PDF_X4))
20112 jghali 1076
        {
1077
                writer.MetaDataObj = writer.newObject();
23561 jghali 1078
                PutDoc("/Metadata " + Pdf::toObjRef(writer.MetaDataObj) + "\n");
19114 avox 1079
        }
1080
        PutDoc("/PageLayout ");
1081
        switch (Options.PageLayout)
1082
        {
1083
                case PDFOptions::SinglePage:
1084
                        PutDoc("/SinglePage\n");
1085
                        break;
1086
                case PDFOptions::OneColumn:
1087
                        PutDoc("/OneColumn\n");
1088
                        break;
1089
                case PDFOptions::TwoColumnLeft:
1090
                        PutDoc("/TwoColumnLeft\n");
1091
                        break;
1092
                case PDFOptions::TwoColumnRight:
1093
                        PutDoc("/TwoColumnRight\n");
1094
                        break;
1095
        }
1096
        if (Options.displayBookmarks)
1097
                PutDoc("/PageMode /UseOutlines\n");
1098
        else if (Options.displayFullscreen)
1099
                PutDoc("/PageMode /FullScreen\n");
1100
        else if (Options.displayThumbs)
1101
                PutDoc("/PageMode /UseThumbs\n");
23548 jghali 1102
        else if ((Options.Version == PDFVersion::PDF_15 || Options.Version == PDFVersion::PDF_16) && (Options.displayLayers))
19114 avox 1103
                        PutDoc("/PageMode /UseOC\n");
1104
        if (!Options.openAction.isEmpty())
1105
        {
22389 jghali 1106
                writer.OpenActionObj = writer.newObject();
1107
                PutDoc("/OpenAction << /S /JavaScript /JS " + Pdf::toPdf(writer.OpenActionObj) + " 0 R >>\n");
19114 avox 1108
        }
21942 craig 1109
        QDateTime dt = QDateTime::currentDateTimeUtc();
20112 jghali 1110
        Datum = Pdf::toDateString(dt);
20103 avox 1111
//    "D:";
1112
//      tmp.sprintf("%4d", d.year());
1113
//      tmp.replace(QRegExp(" "), "0");
1114
//      Datum += tmp;
1115
//      tmp.sprintf("%2d", d.month());
1116
//      tmp.replace(QRegExp(" "), "0");
1117
//      Datum += tmp;
1118
//      tmp.sprintf("%2d", d.day());
1119
//      tmp.replace(QRegExp(" "), "0");
1120
//      Datum += tmp;
1121
//      tmp = dt.time().toString();
1122
//      tmp.replace(QRegExp(":"), "");
1123
//      Datum += tmp;
1124
//      Datum += "Z";
12429 fschmid 1125
 
19114 avox 1126
        // only include XMP to PDF/X-4 at the moment, could easily be extended to include it to any PDF
23548 jghali 1127
        if (Options.Version == PDFVersion::PDF_X4)
19114 avox 1128
                generateXMP(dt.toString("yyyy-MM-ddThh:mm:ssZ"));
1129
 
1130
/* The following code makes the resulting PDF "Reader enabled" in Acrobat Reader 8
1131
   but sadly it doesn't work with newer version, because its based on a bug in AR 8
1132
        PutDoc("/Perms\n");
1133
        PutDoc("<<\n");
1134
        PutDoc("/UR3\n");
1135
        PutDoc("<<\n");
23561 jghali 1136
        PutDoc("/M (" + Datum + ")\n");
1137
        PutDoc("/Name (Scribus " + QString(VERSION) + ")\n");
19114 avox 1138
        PutDoc("/Reference [\n");
1139
        PutDoc("<<\n");
1140
        PutDoc("/TransformParams\n");
1141
        PutDoc("<<\n");
1142
        PutDoc("/Type /TransformParams\n");
1143
        PutDoc("/V /2.2\n");
1144
        PutDoc("/Document [/FullSave]\n");
1145
        PutDoc("/Annots [/Create/Delete/Modify/Copy/Import/Export]\n");
1146
        PutDoc("/Form [/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate]\n");
1147
        PutDoc("/Signature [/Modify]\n");
1148
        PutDoc(">>\n");
1149
        PutDoc("/TransformMethod /UR3\n");
1150
        PutDoc("/Type /SigRef\n");
1151
        PutDoc(">>\n");
1152
        PutDoc("]\n");
1153
        PutDoc("/Type /Sig\n");
1154
        PutDoc(">>\n");
1155
        PutDoc(">>\n");
1156
*/
1157
 
1158
        PutDoc("/ViewerPreferences\n<<\n/PageDirection ");
1159
        PutDoc( Options.Binding == 0 ? "/L2R\n" : "/R2L\n");
1160
        if (Options.hideToolBar)
1161
                PutDoc("/HideToolbar true\n");
1162
        if (Options.hideMenuBar)
1163
                PutDoc("/HideMenubar true\n");
1164
        if (Options.fitWindow)
1165
                PutDoc("/FitWindow true\n");
20112 jghali 1166
        PutDoc(" >>\n>>");
1167
        writer.endObj(writer.CatalogObj);
20103 avox 1168
}
1169
 
1170
void PDFLibCore::PDF_Begin_MetadataAndEncrypt()
1171
{
19114 avox 1172
        QString IDg(Datum);
1173
        IDg += Options.fileName;
23561 jghali 1174
        IDg += "Scribus " + QString(VERSION);
1175
        IDg += "Scribus PDF Library " + QString(VERSION);
19114 avox 1176
        IDg += doc.documentInfo().title();
1177
        IDg += doc.documentInfo().author();
1178
        IDg += "/False";
20112 jghali 1179
        writer.setFileId(Pdf::toPdfDocEncoding(IDg));
19114 avox 1180
        if (Options.Encrypt)
1181
        {
23548 jghali 1182
                writer.setEncryption(Options.Version.supports128BitsEncryption(), Pdf::toPdfDocEncoding(Options.PassOwner), Pdf::toPdfDocEncoding(Options.PassUser), Options.Permissions);
19114 avox 1183
        }
20103 avox 1184
        writer.startObj(writer.InfoObj);
20112 jghali 1185
        PutDoc("<<\n/Creator " + EncString(QByteArray("Scribus ") + VERSION, writer.InfoObj) + "\n");
20103 avox 1186
        PutDoc("/Producer " + EncString(QByteArray("Scribus PDF Library ") + VERSION, writer.InfoObj) + "\n");
19114 avox 1187
        QString docTitle = doc.documentInfo().title();
19115 avox 1188
        if ((PDF_IsPDFX()) && (docTitle.isEmpty()))
22832 craig 1189
                PutDoc("/Title " + EncStringUTF16(doc.documentFileName(), writer.InfoObj) + "\n");
19114 avox 1190
        else
20103 avox 1191
                PutDoc("/Title " + EncStringUTF16(doc.documentInfo().title(), writer.InfoObj) + "\n");
1192
        PutDoc("/Author " + EncStringUTF16(doc.documentInfo().author(), writer.InfoObj) + "\n");
1193
        PutDoc("/Subject " + EncStringUTF16(doc.documentInfo().subject(), writer.InfoObj) + "\n");
1194
        PutDoc("/Keywords " + EncStringUTF16(doc.documentInfo().keywords(), writer.InfoObj) + "\n");
1195
        PutDoc("/CreationDate " + EncString(Datum, writer.InfoObj) + "\n");
1196
        PutDoc("/ModDate " + EncString(Datum, writer.InfoObj) + "\n");
23548 jghali 1197
        if (Options.Version == PDFVersion::PDF_X1a)
19114 avox 1198
        {
1199
                PutDoc("/GTS_PDFXVersion (PDF/X-1:2001)\n");
1200
                PutDoc("/GTS_PDFXConformance (PDF/X-1a:2001)\n");
1201
        }
23548 jghali 1202
        if (Options.Version == PDFVersion::PDF_X3)
19115 avox 1203
                PutDoc("/GTS_PDFXVersion (PDF/X-3:2002)\n");
23548 jghali 1204
        if (Options.Version == PDFVersion::PDF_X4)
19114 avox 1205
                PutDoc("/GTS_PDFXVersion (PDF/X-4)\n");
20112 jghali 1206
        PutDoc("/Trapped /False\n>>");
1207
        writer.endObj(writer.InfoObj);
20103 avox 1208
}
1209
 
1210
QMap<QString, QMap<uint, FPointArray> >
1211
PDFLibCore::PDF_Begin_FindUsedFonts(SCFonts &AllFonts, const QMap<QString, QMap<uint, FPointArray> >& DocFonts)
1212
{
5387 avox 1213
        QMap<QString, QMap<uint, FPointArray> > ReallyUsed;
3133 fschmid 1214
        ReallyUsed.clear();
1215
        PageItem* pgit;
20103 avox 1216
        QMap<int, QByteArray> ind2PDFabr;
1217
        const QByteArray tmpf[] = {"/Courier", "/Courier-Bold", "/Courier-Oblique", "/Courier-BoldOblique",
4017 fschmid 1218
                                                                                                "/Helvetica", "/Helvetica-Bold", "/Helvetica-Oblique", "/Helvetica-BoldOblique",
4546 subik 1219
                                                                                                "/Times-Roman", "/Times-Bold", "/Times-Italic", "/Times-BoldItalic",
4017 fschmid 1220
                                                                                                "/ZapfDingbats", "/Symbol"};
1221
        size_t ar = sizeof(tmpf) / sizeof(*tmpf);
1222
        for (uint ax = 0; ax < ar; ++ax)
1223
                ind2PDFabr[ax] = tmpf[ax];
21709 jghali 1224
 
1225
        QList<PageItem*> allItems = doc.FrameItems.values();
1226
        while (allItems.count() > 0)
3133 fschmid 1227
        {
21709 jghali 1228
                pgit = allItems.takeFirst();
1229
                if (pgit->isGroup() || pgit->isTable())
3133 fschmid 1230
                {
22224 jghali 1231
                        allItems = pgit->getChildren() + allItems;
21709 jghali 1232
                        continue;
1233
                }
1234
                if ((pgit->itemType() == PageItem::TextFrame) || (pgit->itemType() == PageItem::PathText))
1235
                {
1236
                        if (pgit->isAnnotation())
17312 fschmid 1237
                        {
21709 jghali 1238
                                int annotType  = pgit->annotation().Type();
1239
                                bool mustEmbed = ((annotType >= Annotation::Button) && (annotType <= Annotation::Listbox) && (annotType != Annotation::Checkbox));
1240
                                if ((pgit->annotation().Type() == Annotation::Checkbox) || (pgit->annotation().Type() == Annotation::RadioButton))
1241
                                        StdFonts.insert("/ZapfDingbats", "");
1242
                                if (pgit->itemText.length() > 0 || mustEmbed)
17312 fschmid 1243
                                {
23548 jghali 1244
                                        if (Options.Version < PDFVersion::PDF_14)
21709 jghali 1245
                                                StdFonts.insert(ind2PDFabr[pgit->annotation().Font()], "");
21744 jghali 1246
                                        QString replacementName = pgit->itemText.defaultStyle().charStyle().font().replacementName();
1247
                                        ReallyUsed.insert(replacementName, DocFonts[replacementName]);
17312 fschmid 1248
                                }
1249
                        }
21709 jghali 1250
                        uint start = pgit->isTextFrame() ? (uint) pgit->firstInFrame() : 0;
1251
                        uint stop  = pgit->isTextFrame() ? (uint) pgit->lastInFrame() + 1 : (uint) pgit->itemText.length();
1252
                        for (uint e = start; e < stop; ++e)
8092 fschmid 1253
                        {
21709 jghali 1254
                                QString replacementName = pgit->itemText.charStyle(e).font().replacementName();
1255
                                ReallyUsed.insert(replacementName, DocFonts[replacementName]);
8092 fschmid 1256
                        }
3133 fschmid 1257
                }
1258
        }
21709 jghali 1259
 
1260
        allItems = doc.MasterItems;
1261
        while (allItems.count() > 0)
3133 fschmid 1262
        {
21709 jghali 1263
                pgit = allItems.takeFirst();
1264
                if (pgit->isGroup() || pgit->isTable())
3133 fschmid 1265
                {
22224 jghali 1266
                        allItems = pgit->getChildren() + allItems;
21709 jghali 1267
                        continue;
1268
                }
1269
                if ((pgit->itemType() == PageItem::TextFrame) || (pgit->itemType() == PageItem::PathText))
1270
                {
1271
                        if (pgit->isAnnotation())
17312 fschmid 1272
                        {
21709 jghali 1273
                                int annotType  = pgit->annotation().Type();
1274
                                bool mustEmbed = ((annotType >= Annotation::Button) && (annotType <= Annotation::Listbox) && (annotType != Annotation::Checkbox));
1275
                                if ((pgit->annotation().Type() == Annotation::Checkbox) || (pgit->annotation().Type() == Annotation::RadioButton))
1276
                                        StdFonts.insert("/ZapfDingbats", "");
1277
                                if (pgit->itemText.length() > 0 || mustEmbed)
17312 fschmid 1278
                                {
23548 jghali 1279
                                        if (Options.Version < PDFVersion::PDF_14)
21709 jghali 1280
                                                StdFonts.insert(ind2PDFabr[pgit->annotation().Font()], "");
1281
                                        QString replacementName = pgit->itemText.defaultStyle().charStyle().font().replacementName();
1282
                                        ReallyUsed.insert(replacementName, DocFonts[replacementName]);
17312 fschmid 1283
                                }
1284
                        }
21709 jghali 1285
                        uint start = pgit->isTextFrame() ? (uint) pgit->firstInFrame() : 0;
1286
                        uint stop  = pgit->isTextFrame() ? (uint) pgit->lastInFrame() + 1 : (uint) pgit->itemText.length();
1287
                        for (uint e = start; e < stop; ++e)
8092 fschmid 1288
                        {
21709 jghali 1289
                                QString replacementName = pgit->itemText.charStyle(e).font().replacementName();
1290
                                ReallyUsed.insert(replacementName, DocFonts[replacementName]);
1291
                        }
1292
                }
1293
        }
1294
 
1295
        allItems = *(doc.Items);
1296
        while (allItems.count() > 0)
1297
        {
1298
                pgit = allItems.takeFirst();
1299
                if (pgit->isGroup() || pgit->isTable())
1300
                {
22224 jghali 1301
                        allItems = pgit->getChildren() + allItems;
21709 jghali 1302
                        continue;
1303
                }
1304
                if ((pgit->itemType() == PageItem::TextFrame) || (pgit->itemType() == PageItem::PathText))
1305
                {
1306
                        if (pgit->isAnnotation())
1307
                        {
1308
                                int annotType  = pgit->annotation().Type();
1309
                                bool mustEmbed = ((annotType >= Annotation::Button) && (annotType <= Annotation::Listbox) && (annotType != Annotation::Checkbox));
1310
                                if ((pgit->annotation().Type() == Annotation::Checkbox) || (pgit->annotation().Type() == Annotation::RadioButton))
1311
                                        StdFonts.insert("/ZapfDingbats", "");
1312
                                if (pgit->itemText.length() > 0 || mustEmbed)
12763 fschmid 1313
                                {
23548 jghali 1314
                                        if (Options.Version < PDFVersion::PDF_14)
21709 jghali 1315
                                                StdFonts.insert(ind2PDFabr[pgit->annotation().Font()], "");
1316
                                        QString replacementName = pgit->itemText.defaultStyle().charStyle().font().replacementName();
21707 jghali 1317
                                        ReallyUsed.insert(replacementName, DocFonts[replacementName]);
16953 fschmid 1318
                                }
8092 fschmid 1319
                        }
21709 jghali 1320
                        uint start = pgit->isTextFrame() ? (uint) pgit->firstInFrame() : 0;
1321
                        uint stop  = pgit->isTextFrame() ? (uint) pgit->lastInFrame() + 1 : (uint) pgit->itemText.length();
1322
                        for (uint e = start; e < stop; ++e)
1323
                        {
1324
                                QString replacementName = pgit->itemText.charStyle(e).font().replacementName();
1325
                                ReallyUsed.insert(replacementName, DocFonts[replacementName]);
1326
                        }
3133 fschmid 1327
                }
1328
        }
21709 jghali 1329
/*      if (Options.docInfoMarks)
3133 fschmid 1330
        {
21709 jghali 1331
                StdFonts.insert("/Helvetica", "");
1332
        } */
1333
        QStringList patterns = doc.getPatternDependencyList(doc.getUsedPatterns());
1334
        for (int c = 0; c < patterns.count(); ++c)
1335
        {
1336
                ScPattern pa = doc.docPatterns[patterns[c]];
1337
                allItems = pa.items;
1338
                while (allItems.count() > 0)
3133 fschmid 1339
                {
21709 jghali 1340
                        pgit = allItems.takeFirst();
1341
                        if (pgit->isGroup() || pgit->isTable())
17312 fschmid 1342
                        {
22224 jghali 1343
                                allItems = pgit->getChildren() + allItems;
21709 jghali 1344
                                continue;
17312 fschmid 1345
                        }
16953 fschmid 1346
                        if ((pgit->itemType() == PageItem::TextFrame) || (pgit->itemType() == PageItem::PathText))
8092 fschmid 1347
                        {
16953 fschmid 1348
                                if (pgit->isAnnotation())
12763 fschmid 1349
                                {
17952 fschmid 1350
                                        if ((pgit->annotation().Type() == Annotation::Checkbox) || (pgit->annotation().Type() == Annotation::RadioButton))
16953 fschmid 1351
                                                StdFonts.insert("/ZapfDingbats", "");
21709 jghali 1352
                                        if (pgit->itemText.length() > 0)
16953 fschmid 1353
                                        {
23548 jghali 1354
                                                if (Options.Version < PDFVersion::PDF_14)
16953 fschmid 1355
                                                        StdFonts.insert(ind2PDFabr[pgit->annotation().Font()], "");
21707 jghali 1356
                                                QString replacementName = pgit->itemText.defaultStyle().charStyle().font().replacementName();
1357
                                                ReallyUsed.insert(replacementName, DocFonts[replacementName]);
16953 fschmid 1358
                                        }
12763 fschmid 1359
                                }
17751 jghali 1360
                                uint start = pgit->isTextFrame() ? (uint) pgit->firstInFrame() : 0;
1361
                                uint stop  = pgit->isTextFrame() ? (uint) pgit->lastInFrame() + 1 : (uint) pgit->itemText.length();
1362
                                for (uint e = start; e < stop; ++e)
16953 fschmid 1363
                                {
21707 jghali 1364
                                        QString replacementName = pgit->itemText.charStyle(e).font().replacementName();
1365
                                        ReallyUsed.insert(replacementName, DocFonts[replacementName]);
16953 fschmid 1366
                                }
8092 fschmid 1367
                        }
3133 fschmid 1368
                }
1369
        }
20112 jghali 1370
        return ReallyUsed;
20103 avox 1371
}
17628 jghali 1372
 
20103 avox 1373
static QByteArray sanitizeFontName(QString fn)
1374
{
20112 jghali 1375
        return Pdf::toPdfDocEncoding(fn.replace( QRegExp("[\\s\\/\\{\\[\\]\\}\\<\\>\\(\\)\\%]"), "_" ));
20103 avox 1376
}
17628 jghali 1377
 
22507 jghali 1378
static QList<Pdf::Resource> asColorSpace(const QList<PdfICCD>& iccCSlist)
20103 avox 1379
{
20112 jghali 1380
        QList<Pdf::Resource> result;
22507 jghali 1381
        for (const Pdf::Resource& r : iccCSlist)
20112 jghali 1382
                result.append(r);
1383
        return result;
20103 avox 1384
}
1385
 
22507 jghali 1386
static QList<Pdf::Resource> asColorSpace(const QList<PdfSpotC>& spotMapValues)
20103 avox 1387
{
20112 jghali 1388
        QList<Pdf::Resource> result;
22507 jghali 1389
        for (const Pdf::Resource& r : spotMapValues)
20112 jghali 1390
                result.append(r);
1391
        return result;
20103 avox 1392
}
1393
 
1394
void PDFLibCore::PDF_WriteStandardFonts()
1395
{
20112 jghali 1396
        int a = 0;
1397
        QMap<QString, QString>::Iterator itStd;
1398
        for (itStd = StdFonts.begin(); itStd != StdFonts.end(); ++itStd)
1399
        {
1400
                PdfId fontObject = writer.newObject();
1401
                writer.startObj(fontObject);
1402
                PutDoc("<<\n/Type /Font\n/Subtype /Type1\n");
23561 jghali 1403
                PutDoc("/Name /FoStd" + Pdf::toPdf(a) + "\n");
1404
                PutDoc("/BaseFont " + Pdf::toPdfDocEncoding(itStd.key()) + "\n");
20112 jghali 1405
                if (itStd.key() != "/ZapfDingbats")
1406
                {
1407
                        PutDoc("/Encoding << \n");
1408
                        PutDoc("/Differences [ \n");
1409
                        PutDoc("24 /breve /caron /circumflex /dotaccent /hungarumlaut /ogonek /ring /tilde\n");
1410
                        PutDoc("39 /quotesingle 96 /grave 128 /bullet /dagger /daggerdbl /ellipsis /emdash /endash /florin /fraction /guilsinglleft /guilsinglright\n");
1411
                        PutDoc("/minus /perthousand /quotedblbase /quotedblleft /quotedblright /quoteleft /quoteright /quotesinglbase /trademark /fi /fl /Lslash /OE /Scaron\n");
21744 jghali 1412
                        PutDoc("/Ydieresis /Zcaron /dotlessi /lslash /oe /scaron /zcaron 160 /Euro 164 /currency 166 /brokenbar 168 /dieresis /copyright /ordfeminine 172 /logicalnot\n");
20112 jghali 1413
                        PutDoc("/.notdef /registered /macron /degree /plusminus /twosuperior /threesuperior /acute /mu 183 /periodcentered /cedilla /onesuperior /ordmasculine\n");
1414
                        PutDoc("188 /onequarter /onehalf /threequarters 192 /Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla /Egrave /Eacute /Ecircumflex\n");
1415
                        PutDoc("/Edieresis /Igrave /Iacute /Icircumflex /Idieresis /Eth /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply /Oslash\n");
1416
                        PutDoc("/Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn /germandbls /agrave /aacute /acircumflex /atilde /adieresis /aring /ae /ccedilla\n");
1417
                        PutDoc("/egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex /idieresis /eth /ntilde /ograve /oacute /ocircumflex /otilde /odieresis\n");
1418
                        PutDoc("/divide /oslash /ugrave /uacute /ucircumflex /udieresis /yacute /thorn /ydieresis\n");
1419
                        PutDoc("] >>\n");
1420
                }
1421
                PutDoc(">>");
1422
                writer.endObj(fontObject);
23561 jghali 1423
                pageData.FObjects["FoStd" + Pdf::toPdf(a)] = fontObject;
1424
                itStd.value() = "FoStd" + Pdf::toPdf(a);
20112 jghali 1425
                a++;
1426
        }
20103 avox 1427
}
1428
 
1429
 
1430
PdfFont PDFLibCore::PDF_WriteType3Font(const QByteArray& name, ScFace& face, const QMap<uint,FPointArray>& RealGlyphs)
1431
{
20112 jghali 1432
        PdfFont result;
1433
        result.name = Pdf::toName(name);
23759 jghali 1434
        result.usage = Used_in_Content;
20112 jghali 1435
        result.method = Use_Type3;
1436
        result.encoding = Encode_256;
20103 avox 1437
 
22008 jghali 1438
        // TrueType font rasterizers use non-zero winding file rule,
1439
        // vs even-odd for current Postscript font rasterizers
1440
        // Note: OTF CFF2 will also use non-zero winding rule, so
1441
        // change code below when adding CCF2 support to Scribus
1442
        // ref: https://www.microsoft.com/typography/OTSpec/cff2.htm
1443
        bool useNonZeroRule = (face.type() == ScFace::TTF);
1444
 
20112 jghali 1445
        uint SubFonts = 0;
1446
        int glyphCount = 0;
1447
        double minx =  std::numeric_limits<double>::max();
1448
        double miny =  std::numeric_limits<double>::max();
1449
        double maxx = -std::numeric_limits<double>::max();
1450
        double maxy = -std::numeric_limits<double>::max();
22142 jghali 1451
 
20112 jghali 1452
        QList<uint> glyphWidths;
1453
        QList<QByteArray> charProcs;
22142 jghali 1454
        QStringList toUnicodeMaps;
1455
        QList<int> toUnicodeMapsCount;
22719 jghali 1456
        QString toUnicodeMap;
22142 jghali 1457
        int toUnicodeMapCounter = 0;
20112 jghali 1458
        QByteArray encoding = "<< /Type /Encoding\n/Differences [ 0\n";
1459
        QByteArray fon;
1460
        QMap<uint, uint> glyphMapping;
22142 jghali 1461
 
20112 jghali 1462
        ScFace::FaceEncoding gl;
1463
        face.glyphNames(gl);
22142 jghali 1464
 
20112 jghali 1465
        QMap<uint,FPointArray>::ConstIterator ig;
1466
        for (ig = RealGlyphs.cbegin(); ig != RealGlyphs.cend(); ++ig)
1467
        {
1468
                FPoint np, np1, np2;
1469
                bool nPath = true;
1470
                fon.resize(0);
1471
                if (ig.value().size() > 3)
1472
                {
1473
                        FPointArray gly = ig.value();
1474
                        QTransform mat;
1475
                        mat.scale(100.0, -100.0);
1476
                        gly.map(mat);
1477
                        gly.translate(0, 1000);
23563 jghali 1478
                        for (int poi = 0; poi < gly.size() - 3; poi += 4)
20112 jghali 1479
                        {
1480
                                if (gly.isMarker(poi))
1481
                                {
1482
                                        fon += "h\n";
1483
                                        nPath = true;
1484
                                        continue;
1485
                                }
1486
                                if (nPath)
1487
                                {
1488
                                        np = gly.point(poi);
23561 jghali 1489
                                        fon += FToStr(np.x()) + " " + FToStr(np.y()) + " m\n";
20112 jghali 1490
                                        nPath = false;
1491
                                }
1492
                                np = gly.point(poi+1);
1493
                                np1 = gly.point(poi+3);
1494
                                np2 = gly.point(poi+2);
1495
                                fon += FToStr(np.x()) + " " + FToStr(np.y()) + " " + FToStr(np1.x()) + " " + FToStr(np1.y()) + " " + FToStr(np2.x()) + " " + FToStr(np2.y()) + " c\n";
1496
                        }
22008 jghali 1497
                        fon += useNonZeroRule? "h f\n" : "h f*\n";
20112 jghali 1498
                        np = getMinClipF(&gly);
1499
                        np1 = getMaxClipF(&gly);
1500
                }
1501
                else
1502
                {
20684 jghali 1503
                        // 13654: make evince emit warning about "Syntax Error: No current point in closepath"
1504
                        // PDF specification is not quite clear about that, but a single "h" operator might not
1505
                        // be a valid way to make an empty path
1506
                        // fon = "h";
20112 jghali 1507
                        np = FPoint(0, 0);
1508
                        np1 = FPoint(0, 0);
1509
                }
23563 jghali 1510
                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 1511
                minx = qMin(minx, np.x());
1512
                miny = qMin(miny, np.y());
1513
                maxx = qMax(maxx, np1.x());
1514
                maxy = qMax(maxy, np1.y());
1515
                glyphWidths.append(qRound(np1.x()));
22142 jghali 1516
 
20112 jghali 1517
                PdfId charProcObject = writer.newObject();
1518
                writer.startObj(charProcObject);
1519
                if (Options.Compress)
1520
                        fon = CompressArray(fon);
22717 jghali 1521
                PutDoc("<< /Length " + Pdf::toPdf(fon.length() + 1));
20112 jghali 1522
                if (Options.Compress)
1523
                        PutDoc("\n/Filter /FlateDecode");
22717 jghali 1524
                PutDoc("\n>>\nstream\n" + EncStream(fon, charProcObject) + "\nendstream");
20112 jghali 1525
                writer.endObj(charProcObject);
22142 jghali 1526
 
22717 jghali 1527
                // #15449 : in some cases we cannot retrieve glyph names for all glyphs we need
1528
                // using ScFace's glyphNames(), so generate custom glyph names using glyph index.
1529
                // With Type 3 fonts we have more flexibility than with other fonts.
1530
                QString glGlyphName, glToUnicode;
1531
                auto  glyphIt = gl.constFind(ig.key());
1532
                if ((glyphIt != gl.constEnd()) && (glyphIt->charcode > 0))
1533
                        glToUnicode = QString::asprintf("%04X", glyphIt->charcode);
1534
                else
1535
                        glToUnicode = QString("0000");
1536
                glGlyphName = "gly" + QString::asprintf("%04X", ig.key());
1537
 
1538
                charProcs.append(Pdf::toName(glGlyphName) + " " + Pdf::toPdf(charProcObject) + " 0 R\n");
1539
                encoding += Pdf::toName(glGlyphName) + " ";
1540
                glyphMapping.insert(ig.key(), glyphCount + SubFonts * 256);
1541
 
1542
                QString tmp = QString::asprintf("%02X", glyphCount);
1543
                toUnicodeMap += "<" + Pdf::toAscii(tmp) + "> <" + Pdf::toAscii(glToUnicode) + ">\n";
22142 jghali 1544
                toUnicodeMapCounter++;
1545
                if (toUnicodeMapCounter == 100)
1546
                {
1547
                        toUnicodeMaps.append(toUnicodeMap);
1548
                        toUnicodeMapsCount.append(toUnicodeMapCounter);
22717 jghali 1549
                        toUnicodeMap.clear();
22142 jghali 1550
                        toUnicodeMapCounter = 0;
1551
                }
1552
 
20112 jghali 1553
                glyphCount++;
1554
                int glyphsLeft = RealGlyphs.count() - SubFonts * 256;
1555
                if ((glyphCount > 255) || (glyphCount == glyphsLeft))
1556
                {
1557
                        PdfId fontWidths = writer.newObject();
1558
                        writer.startObj(fontWidths);
1559
                        PutDoc("[ ");
1560
                        for (int ww = 0; ww < glyphWidths.count(); ++ww)
1561
                        {
23561 jghali 1562
                                PutDoc(Pdf::toPdf(glyphWidths[ww]) + " ");
20112 jghali 1563
                        }
1564
                        PutDoc("]");
1565
                        writer.endObj(fontWidths);
1566
                        PdfId fontCharProcs = writer.newObject();
1567
                        writer.startObj(fontCharProcs);
1568
                        PutDoc("<<\n");
1569
                        for (int ww = 0; ww < charProcs.count(); ++ww)
1570
                        {
1571
                                PutDoc(charProcs[ww]);
1572
                        }
1573
                        PutDoc(">>");
1574
                        writer.endObj(fontCharProcs);
1575
                        PdfId fontEncoding = writer.newObject();
1576
                        writer.startObj(fontEncoding);
1577
                        PutDoc(encoding);
1578
                        PutDoc("]\n");
1579
                        PutDoc(">>");
1580
                        writer.endObj(fontEncoding);
22142 jghali 1581
 
1582
                        if (toUnicodeMapCounter != 0)
1583
                        {
1584
                                toUnicodeMaps.append(toUnicodeMap);
1585
                                toUnicodeMapsCount.append(toUnicodeMapCounter);
1586
                        }
1587
 
22719 jghali 1588
                        QByteArray toUnicodeMapStream;
22142 jghali 1589
                        toUnicodeMapStream += "/CIDInit /ProcSet findresource begin\n";
1590
                        toUnicodeMapStream += "12 dict begin\n";
1591
                        toUnicodeMapStream += "begincmap\n";
1592
                        toUnicodeMapStream += "/CIDSystemInfo <<\n";
1593
                        toUnicodeMapStream += "/Registry (Adobe)\n";
1594
                        toUnicodeMapStream += "/Ordering (UCS)\n";
1595
                        toUnicodeMapStream += "/Supplement 0\n";
1596
                        toUnicodeMapStream += ">> def\n";
1597
                        toUnicodeMapStream += "/CMapName /Adobe-Identity-UCS def\n";
1598
                        toUnicodeMapStream += "/CMapType 2 def\n";
1599
                        toUnicodeMapStream += "1 begincodespacerange\n";
1600
                        toUnicodeMapStream += "<0000> <FFFF>\n";
1601
                        toUnicodeMapStream += "endcodespacerange\n";
1602
                        for (int uniC = 0; uniC < toUnicodeMaps.count(); uniC++)
1603
                        {
1604
                                toUnicodeMapStream += Pdf::toPdf(toUnicodeMapsCount[uniC]);
1605
                                toUnicodeMapStream += " beginbfchar\n";
1606
                                toUnicodeMapStream += toUnicodeMaps[uniC];
1607
                                toUnicodeMapStream += "endbfchar\n";
1608
                        }
1609
                        toUnicodeMapStream += "endcmap\n";
1610
                        toUnicodeMapStream += "CMapName currentdict /CMap defineresource pop\n";
1611
                        toUnicodeMapStream += "end\n";
1612
                        toUnicodeMapStream += "end\n";
1613
                        PdfId fontToUnicode = WritePDFStream(toUnicodeMapStream);
1614
 
20112 jghali 1615
                        PdfId font3Object = writer.newObject();
1616
                        writer.startObj(font3Object);
1617
                        PutDoc("<<\n/Type /Font\n/Subtype /Type3\n");
23561 jghali 1618
                        PutDoc("/Name /" + name + "S" + Pdf::toPdf(SubFonts) + "\n");
20112 jghali 1619
                        PutDoc("/FirstChar 0\n");
23561 jghali 1620
                        PutDoc("/LastChar " + Pdf::toPdf(glyphCount-1) + "\n");
1621
                        PutDoc("/Widths " + Pdf::toPdf(fontWidths) + " 0 R\n");
1622
                        PutDoc("/CharProcs " + Pdf::toPdf(fontCharProcs) + " 0 R\n");
23563 jghali 1623
                        PutDoc("/FontBBox [" + Pdf::toPdf(qRound(minx)) + " " + Pdf::toPdf(qRound(miny)) + " " + Pdf::toPdf(qRound(maxx)) + " " + Pdf::toPdf(qRound(maxy)) + "]\n");
20112 jghali 1624
                        PutDoc("/FontMatrix [0.001 0 0 0.001 0 0]\n");
23561 jghali 1625
                        PutDoc("/Encoding " + Pdf::toPdf(fontEncoding) + " 0 R\n");
22142 jghali 1626
                        PutDoc("/ToUnicode " + Pdf::toPdf(fontToUnicode) + " 0 R\n");
20112 jghali 1627
                        PutDoc(">>");
1628
                        writer.endObj(font3Object);
23561 jghali 1629
                        pageData.FObjects[name + "S" + Pdf::toPdf(SubFonts)] = font3Object;
20112 jghali 1630
                        charProcs.clear();
1631
                        glyphWidths.clear();
22142 jghali 1632
                        toUnicodeMaps.clear();
1633
                        toUnicodeMapsCount.clear();
1634
                        toUnicodeMap.clear();
1635
                        toUnicodeMapCounter = 0;
20112 jghali 1636
                        glyphCount = 0;
1637
                        ++SubFonts;
1638
                        minx =  std::numeric_limits<double>::max();
1639
                        miny =  std::numeric_limits<double>::max();
1640
                        maxx = -std::numeric_limits<double>::max();
1641
                        maxy = -std::numeric_limits<double>::max();
1642
                        encoding = "<< /Type /Encoding\n/Differences [ 0\n";
1643
                }
1644
        }
1645
        result.glyphmap = glyphMapping;
1646
        return result;
20103 avox 1647
}
1648
 
1649
 
1650
PdfFont PDFLibCore::PDF_WriteGlyphsAsXForms(const QByteArray& fontName, ScFace& face, const QMap<uint,FPointArray>& RealGlyphs)
1651
{
20112 jghali 1652
        PdfFont result;
1653
        result.name = Pdf::toName(fontName);
23729 jghali 1654
        result.usage = Used_in_Content;
20112 jghali 1655
        result.method = Use_XForm;
1656
        result.encoding = Encode_224;
20103 avox 1657
 
22008 jghali 1658
        // TrueType font rasterizers use non-zero winding file rule,
1659
        // vs even-odd for current Postscript font rasterizers
1660
        // Note: OTF CFF2 will also use non-zero winding rule, so
1661
        // change code below when adding CCF2 support to Scribus
1662
        // ref: https://www.microsoft.com/typography/OTSpec/cff2.htm
1663
        bool useNonZeroRule = (face.type() == ScFace::TTF);
1664
 
20112 jghali 1665
        QByteArray fon;
1666
        QMap<uint,FPointArray>::ConstIterator ig;
1667
        for (ig = RealGlyphs.cbegin(); ig != RealGlyphs.cend(); ++ig)
1668
        {
1669
                FPoint np, np1, np2;
1670
                bool nPath = true;
1671
                fon.resize(0);
1672
                if (ig.value().size() > 3)
1673
                {
1674
                        FPointArray gly = ig.value();
1675
                        QTransform mat;
1676
                        mat.scale(0.1, 0.1);
1677
                        gly.map(mat);
23563 jghali 1678
                        for (int poi = 0; poi < gly.size() - 3; poi += 4)
20112 jghali 1679
                        {
1680
                                if (gly.isMarker(poi))
1681
                                {
1682
                                        fon += "h\n";
1683
                                        nPath = true;
1684
                                        continue;
1685
                                }
1686
                                if (nPath)
1687
                                {
1688
                                        np = gly.point(poi);
23561 jghali 1689
                                        fon += FToStr(np.x()) + " " + FToStr(-np.y()) + " m\n";
20112 jghali 1690
                                        nPath = false;
1691
                                }
1692
                                np = gly.point(poi+1);
1693
                                np1 = gly.point(poi+3);
1694
                                np2 = gly.point(poi+2);
1695
                                fon += FToStr(np.x()) + " " + FToStr(-np.y()) + " " +
1696
                                FToStr(np1.x()) + " " + FToStr(-np1.y()) + " " +
1697
                                FToStr(np2.x()) + " " + FToStr(-np2.y()) + " c\n";
1698
                        }
22008 jghali 1699
                        fon += useNonZeroRule? "h f\n" : "h f*\n";
20112 jghali 1700
                        np = getMinClipF(&gly);
1701
                        np1 = getMaxClipF(&gly);
1702
                }
1703
                else
1704
                {
20684 jghali 1705
                        // 13654: make evince emit warning about "Syntax Error: No current point in closepath"
1706
                        // PDF specification is not quite clear about that, but a single "h" operator might not
1707
                        // be a valid way to make an empty path
1708
                        fon = " " /*"h"*/;
20112 jghali 1709
                        np = FPoint(0, 0);
1710
                        np1 = FPoint(0, 0);
1711
                }
1712
                PdfId fontGlyphXForm = writer.newObject();
1713
                writer.startObj(fontGlyphXForm);
1714
                PutDoc("<<\n/Type /XObject\n/Subtype /Form\n/FormType 1\n");
23563 jghali 1715
                PutDoc("/BBox [ " + FToStr(np.x()) + " " + FToStr(-np.y()) + " " + FToStr(np1.x()) + " " + FToStr(-np1.y()) + " ]\n");
20112 jghali 1716
                PutDoc("/Resources << /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]\n");
1717
                PutDoc(">>\n");
1718
                if (Options.Compress)
1719
                        fon = CompressArray(fon);
23563 jghali 1720
                PutDoc("/Length " + Pdf::toPdf(fon.length() + 1));
20112 jghali 1721
                if (Options.Compress)
1722
                        PutDoc("\n/Filter /FlateDecode");
23561 jghali 1723
                PutDoc(" >>\nstream\n" + EncStream(fon, fontGlyphXForm) + "\nendstream");
20112 jghali 1724
                writer.endObj(fontGlyphXForm);
21894 jghali 1725
                pageData.XObjects[fontName + "_gl" + Pdf::toPdf(ig.key())] = fontGlyphXForm;
20112 jghali 1726
        }
1727
        return result;
20103 avox 1728
}
1729
 
1730
PdfId PDFLibCore::PDF_EmbedFontObject(const QByteArray& font, const QByteArray& subtype)
1731
{
20112 jghali 1732
        PdfId embeddedFontObject = writer.newObject();
1733
        writer.startObj(embeddedFontObject);
1734
        int len = font.length();
1735
        QByteArray ttf = (Options.Compress? CompressArray(font) : font);
1736
        //qDebug() << QString("sfnt data: size=%1 compressed=%2").arg(len).arg(bb.length());
1737
        PutDoc("<<\n/Length " + Pdf::toPdf(ttf.length() + 1) + "\n");
1738
        PutDoc("/Length1 " + Pdf::toPdf(len) + "\n");
1739
        if (subtype.size() > 0)
1740
                PutDoc("/Subtype " + subtype);
1741
        if (Options.Compress)
1742
                PutDoc("/Filter /FlateDecode\n");
1743
        PutDoc(">>\nstream\n");
1744
        EncodeArrayToStream(ttf, embeddedFontObject);
1745
        PutDoc("\nendstream");
1746
        writer.endObj(embeddedFontObject);
1747
        return embeddedFontObject;
20103 avox 1748
}
1749
 
22600 craig 1750
QByteArray PDFLibCore::PDF_GenerateSubsetTag(const QByteArray& fontName, const QList<uint>& usedGlyphs)
21775 jghali 1751
{
1752
        uint hash, mod;
1753
        QVector<uint> glyphVec = usedGlyphs.toVector();
1754
 
1755
        hash = qHashBits(fontName.constData(), fontName.size(), 0);
1756
        hash = qHashBits(glyphVec.constData(), glyphVec.size() * sizeof(uint), hash);
1757
 
1758
        QByteArray subsetTag(6, (char) 0);
1759
        for (int i = 0; i < 6; ++i)
1760
        {
1761
                mod = hash % 26;
1762
                subsetTag[i] = 'A' + mod;
1763
                hash = (hash - mod) / 26;
1764
        }
1765
 
1766
        return subsetTag;
1767
}
1768
 
20103 avox 1769
PdfId PDFLibCore::PDF_WriteFontDescriptor(const QByteArray& fontName, ScFace& face, ScFace::FontFormat fformat, PdfId embeddedFontObject)
1770
{
20112 jghali 1771
        PdfId fontDescriptor = writer.newObject();
1772
        writer.startObj(fontDescriptor);
1773
        // TODO: think about QByteArray ScFace::getFontDescriptor() -- AV
1774
        PutDoc("<<\n/Type /FontDescriptor\n");
1775
        PutDoc("/FontName "  + Pdf::toName(fontName) + "\n");
1776
        PutDoc("/FontBBox [ " + Pdf::toAscii(face.pdfFontBBoxAsString()) + " ]\n");
1777
        PutDoc("/Flags ");
1778
        //FIXME: isItalic() should be queried from ScFace, not from Qt -- AV
1779
        //QFontInfo fo = QFontInfo(it.data());
1780
        int pfl = 0;
1781
        if (face.isFixedPitch())
1782
                pfl = pfl ^ 1;
1783
        //if (fo.italic())
1784
        if (face.italicAngleAsString() != "0")
1785
                pfl = pfl ^ 64;
1786
        //                      pfl = pfl ^ 4;
1787
        pfl = pfl ^ 32;
23561 jghali 1788
        PutDoc(Pdf::toPdf(pfl) + "\n");
1789
        PutDoc("/Ascent "      + Pdf::toAscii(face.pdfAscentAsString()) + "\n");
1790
        PutDoc("/Descent "     + Pdf::toAscii(face.pdfDescentAsString()) + "\n");
1791
        PutDoc("/CapHeight "   + Pdf::toAscii(face.pdfCapHeightAsString()) + "\n");
1792
        PutDoc("/ItalicAngle " + Pdf::toAscii(face.italicAngleAsString()) + "\n");
20112 jghali 1793
        PutDoc("/StemV 1\n");
1794
        if (embeddedFontObject != 0)
1795
        {
1796
                if (fformat == ScFace::SFNT || fformat == ScFace::TTCF)
1797
                {
1798
                        if (face.type() == ScFace::OTF)
1799
                        {
23548 jghali 1800
                                PutDoc("/FontFile3 " + Pdf::toPdf(embeddedFontObject) + " 0 R\n");
20112 jghali 1801
                        }
1802
                        else
1803
                        {
23548 jghali 1804
                                PutDoc("/FontFile2 " + Pdf::toPdf(embeddedFontObject) + " 0 R\n");
20112 jghali 1805
                        }
1806
                }
1807
                else if (fformat == ScFace::PFB)
23561 jghali 1808
                        PutDoc("/FontFile " + Pdf::toPdf(embeddedFontObject) + " 0 R\n");
20112 jghali 1809
                else if (fformat == ScFace::PFA)
23561 jghali 1810
                        PutDoc("/FontFile " + Pdf::toPdf(embeddedFontObject) + " 0 R\n");
20112 jghali 1811
        }
1812
        PutDoc(">>");
1813
        writer.endObj(fontDescriptor);
1814
        return fontDescriptor;
20103 avox 1815
}
1816
 
22600 craig 1817
PdfFont PDFLibCore::PDF_EncodeCidFont(const QByteArray& fontName, ScFace& face, const QByteArray& baseFont, PdfId fontDes, const ScFace::FaceEncoding& gl, const QMap<uint,uint>& glyphmap  )
20103 avox 1818
{
20112 jghali 1819
        PdfFont result;
1820
        result.name = Pdf::toName(fontName);
23727 jghali 1821
        result.usage = Used_in_Content;
20112 jghali 1822
        result.method = glyphmap.isEmpty()? Use_Embedded : Use_Subset;
1823
        result.encoding = glyphmap.isEmpty()? Encode_IdentityH : Encode_Subset;
1824
        result.glyphmap = glyphmap;
1825
 
1826
        PdfId fontWidths2 = writer.newObject();
1827
        writer.startObj(fontWidths2);
1828
        QList<QByteArray> toUnicodeMaps;
1829
        QList<int> toUnicodeMapsCount;
22719 jghali 1830
        QByteArray toUnicodeMap;
20112 jghali 1831
        int toUnicodeMapCounter = 0;
1832
 
1833
        PutDoc("[ ");
1834
        QList<uint> keys = gl.uniqueKeys();
1835
        bool seenNotDef = false;
23483 jghali 1836
        for (auto git = keys.begin(); git != keys.end(); ++git)
20112 jghali 1837
        {
1838
                uint gid = result.encoding == Encode_Subset? glyphmap[*git] : *git;
1839
                if (gid > 0 || !seenNotDef)
1840
                {
1841
                        seenNotDef |= (gid == 0);
23483 jghali 1842
                        PutDoc(Pdf::toPdf(gid) + " [" + Pdf::toPdf(static_cast<int>(face.glyphWidth(*git) * 1000)) + "] " );
1843
                        QString tmp = QString::asprintf("%04X", gid);
1844
                        QString tmp2 = gl.value(*git).toUnicode;
23563 jghali 1845
                        toUnicodeMap += "<" + Pdf::toAscii(tmp) + "> <" + Pdf::toAscii(tmp2) + ">\n";
20112 jghali 1846
                        toUnicodeMapCounter++;
1847
                        if (toUnicodeMapCounter == 100)
1848
                        {
1849
                                toUnicodeMaps.append(toUnicodeMap);
1850
                                toUnicodeMapsCount.append(toUnicodeMapCounter);
22719 jghali 1851
                                toUnicodeMap.clear();
20112 jghali 1852
                                toUnicodeMapCounter = 0;
1853
                        }
1854
                }
1855
        }
1856
        PutDoc("]");
1857
        writer.endObj(fontWidths2);
1858
        if (toUnicodeMapCounter != 0)
1859
        {
1860
                toUnicodeMaps.append(toUnicodeMap);
1861
                toUnicodeMapsCount.append(toUnicodeMapCounter);
1862
        }
22719 jghali 1863
        QByteArray toUnicodeMapStream;
20112 jghali 1864
        toUnicodeMapStream += "/CIDInit /ProcSet findresource begin\n";
1865
        toUnicodeMapStream += "12 dict begin\n";
1866
        toUnicodeMapStream += "begincmap\n";
1867
        toUnicodeMapStream += "/CIDSystemInfo <<\n";
1868
        toUnicodeMapStream += "/Registry (Adobe)\n";
1869
        toUnicodeMapStream += "/Ordering (UCS)\n";
1870
        toUnicodeMapStream += "/Supplement 0\n";
1871
        toUnicodeMapStream += ">> def\n";
1872
        toUnicodeMapStream += "/CMapName /Adobe-Identity-UCS def\n";
1873
        toUnicodeMapStream += "/CMapType 2 def\n";
1874
        toUnicodeMapStream += "1 begincodespacerange\n";
1875
        toUnicodeMapStream += "<0000> <FFFF>\n";
1876
        toUnicodeMapStream += "endcodespacerange\n";
1877
        for (int uniC = 0; uniC < toUnicodeMaps.count(); uniC++)
1878
        {
1879
                toUnicodeMapStream += Pdf::toPdf(toUnicodeMapsCount[uniC]);
1880
                toUnicodeMapStream += " beginbfchar\n";
1881
                toUnicodeMapStream += toUnicodeMaps[uniC];
1882
                toUnicodeMapStream += "endbfchar\n";
1883
        }
1884
        toUnicodeMapStream += "endcmap\n";
1885
        toUnicodeMapStream += "CMapName currentdict /CMap defineresource pop\n";
1886
        toUnicodeMapStream += "end\n";
1887
        toUnicodeMapStream += "end\n";
1888
        PdfId fontToUnicode2 = WritePDFStream(toUnicodeMapStream);
1889
        PdfId fontObject2 = writer.newObject();
1890
        writer.startObj(fontObject2);
1891
        PutDoc("<<\n/Type /Font\n/Subtype /Type0\n");
1892
        PutDoc("/Name " + Pdf::toName(fontName) + "\n");
23563 jghali 1893
        PutDoc("/BaseFont " + Pdf::toName(baseFont) + "\n");
20112 jghali 1894
        PutDoc("/Encoding /Identity-H\n");
23561 jghali 1895
        PutDoc("/ToUnicode " + Pdf::toPdf(fontToUnicode2) + " 0 R\n");
20112 jghali 1896
        PutDoc("/DescendantFonts [");
1897
        PutDoc("<</Type /Font");
1898
        if (face.type() == ScFace::OTF)
1899
                PutDoc("/Subtype /CIDFontType0");
1900
        else
1901
                PutDoc("/Subtype /CIDFontType2");
21759 jghali 1902
        PutDoc("/BaseFont " + Pdf::toName(baseFont));
23563 jghali 1903
        PutDoc("/FontDescriptor " + Pdf::toPdf(fontDes) + " 0 R");
20112 jghali 1904
        PutDoc("/CIDSystemInfo <</Ordering(Identity)/Registry(Adobe)/Supplement 0>>");
1905
        PutDoc("/DW 1000");
23561 jghali 1906
        PutDoc("/W " + Pdf::toPdf(fontWidths2) + " 0 R");
20112 jghali 1907
        PutDoc("/CIDToGIDMap /Identity");
1908
        PutDoc(">>"); // close CIDFont dictionary
1909
        PutDoc("]\n"); // close DescendantFonts array
1910
        PutDoc(">>");
1911
        writer.endObj(fontObject2);
1912
        pageData.FObjects[fontName] = fontObject2;
1913
        return result;
20103 avox 1914
}
1915
 
1916
 
1917
PdfFont PDFLibCore::PDF_EncodeSimpleFont(const QByteArray& fontName, ScFace& face, const QByteArray& baseFont, const QByteArray& subtype, bool isEmbedded, PdfId fontDes, const ScFace::FaceEncoding& gl)
1918
{
20112 jghali 1919
        PdfFont result;
1920
        result.name = Pdf::toName(fontName);
23728 jghali 1921
        result.usage = Used_in_Content;
20112 jghali 1922
        result.method = isEmbedded? Use_Embedded : Use_System;
1923
        result.encoding = Encode_224;
1924
 
1925
        int nglyphs = 0;
22717 jghali 1926
        for (auto gli = gl.cbegin(); gli != gl.cend(); ++gli)
20112 jghali 1927
        {
1928
                if (gli.key() > static_cast<uint>(nglyphs))
1929
                        nglyphs = gli.key();
1930
        }
1931
        ++nglyphs;
1932
        //                      qDebug() << QString("pdflib: nglyphs %1 max %2").arg(nglyphs).arg(face.maxGlyph());
1933
 
1934
        uint Fcc = nglyphs / 224;
1935
        if ((nglyphs % 224) != 0)
1936
                Fcc += 1;
1937
        for (uint Fc = 0; Fc < Fcc; ++Fc)
1938
        {
1939
                PdfId fontWidths2 = writer.newObject();
1940
                writer.startObj(fontWidths2);
1941
                int chCount = 32;
1942
                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 ");
1943
                for (int ww = 32; ww < 256; ++ww)
1944
                {
1945
                        uint glyph = 224 * Fc + ww - 32;
1946
                        if (gl.contains(glyph))
23561 jghali 1947
                                PutDoc(Pdf::toPdf(static_cast<int>(face.glyphWidth(glyph)* 1000)) + " ");
20112 jghali 1948
                        else
1949
                                PutDoc("0 ");
1950
                        chCount++;
1951
                        if (signed(glyph) == nglyphs-1)
1952
                                break;
1953
                }
1954
                PutDoc("]");
1955
                writer.endObj(fontWidths2);
22717 jghali 1956
 
20112 jghali 1957
                QStringList toUnicodeMaps;
22719 jghali 1958
                QList<int>  toUnicodeMapsCount;
1959
                QString toUnicodeMap;
20112 jghali 1960
                int toUnicodeMapCounter = 0;
22717 jghali 1961
                int crc = 0;
1962
                bool startOfSeq = true;
1963
 
1964
                PdfId fontEncoding2 = writer.newObject();
1965
                writer.startObj(fontEncoding2);
20112 jghali 1966
                PutDoc("<< /Type /Encoding\n");
1967
                PutDoc("/Differences [ \n");
1968
                for (int ww2 = 32; ww2 < 256; ++ww2)
1969
                {
1970
                        uint glyph = 224 * Fc + ww2 - 32;
1971
                        ScFace::FaceEncoding::ConstIterator glIt = gl.find(glyph);
22143 jghali 1972
                        if (glIt != gl.cend() && !glIt.value().glyphName.isEmpty())
20112 jghali 1973
                        {
22143 jghali 1974
                                const ScFace::GlyphEncoding& glEncoding = glIt.value();
20112 jghali 1975
                                if (startOfSeq)
1976
                                {
23561 jghali 1977
                                        PutDoc(Pdf::toPdf(ww2) + " ");
20112 jghali 1978
                                        startOfSeq = false;
1979
                                }
23561 jghali 1980
                                PutDoc(Pdf::toName(glEncoding.glyphName) + " ");
23483 jghali 1981
                                QString tmp = QString::asprintf("%02X", ww2);
1982
                                QString tmp2 = glEncoding.toUnicode;
20112 jghali 1983
                                toUnicodeMap += "<" + Pdf::toAscii(tmp) + "> <" + Pdf::toAscii(tmp2) + ">\n";
1984
                                //QString("<%1> <%2>\n").arg(tmp).arg((tmp2));
1985
                                toUnicodeMapCounter++;
1986
                                if (toUnicodeMapCounter == 100)
1987
                                {
1988
                                        toUnicodeMaps.append(toUnicodeMap);
1989
                                        toUnicodeMapsCount.append(toUnicodeMapCounter);
22717 jghali 1990
                                        toUnicodeMap.clear();
20112 jghali 1991
                                        toUnicodeMapCounter = 0;
1992
                                }
1993
                                crc++;
1994
                        }
1995
                        else
1996
                        {
1997
                                startOfSeq = true;
1998
                        }
1999
                        if (signed(glyph) == nglyphs-1)
2000
                                break;
2001
                        if (crc > 8)
2002
                        {
2003
                                PutDoc("\n");
2004
                                crc = 0;
2005
                        }
2006
                }
2007
                if (toUnicodeMapCounter != 0)
2008
                {
2009
                        toUnicodeMaps.append(toUnicodeMap);
2010
                        toUnicodeMapsCount.append(toUnicodeMapCounter);
2011
                }
2012
                PutDoc("]\n");
2013
                PutDoc(">>");
2014
                writer.endObj(fontEncoding2);
22719 jghali 2015
                QByteArray toUnicodeMapStream;
20112 jghali 2016
                toUnicodeMapStream += "/CIDInit /ProcSet findresource begin\n";
2017
                toUnicodeMapStream += "12 dict begin\n";
2018
                toUnicodeMapStream += "begincmap\n";
2019
                toUnicodeMapStream += "/CIDSystemInfo <<\n";
2020
                toUnicodeMapStream += "/Registry (Adobe)\n";
2021
                toUnicodeMapStream += "/Ordering (UCS)\n";
2022
                toUnicodeMapStream += "/Supplement 0\n";
2023
                toUnicodeMapStream += ">> def\n";
2024
                toUnicodeMapStream += "/CMapName /Adobe-Identity-UCS def\n";
2025
                toUnicodeMapStream += "/CMapType 2 def\n";
2026
                toUnicodeMapStream += "1 begincodespacerange\n";
2027
                toUnicodeMapStream += "<0000> <FFFF>\n";
2028
                toUnicodeMapStream += "endcodespacerange\n";
2029
                for (int uniC = 0; uniC < toUnicodeMaps.count(); uniC++)
2030
                {
2031
                        toUnicodeMapStream += Pdf::toPdf(toUnicodeMapsCount[uniC]);
2032
                        toUnicodeMapStream += " beginbfchar\n";
2033
                        toUnicodeMapStream += toUnicodeMaps[uniC];
2034
                        toUnicodeMapStream += "endbfchar\n";
2035
                }
2036
                toUnicodeMapStream += "endcmap\n";
2037
                toUnicodeMapStream += "CMapName currentdict /CMap defineresource pop\n";
2038
                toUnicodeMapStream += "end\n";
2039
                toUnicodeMapStream += "end\n";
2040
                PdfId fontToUnicode2 = WritePDFStream(toUnicodeMapStream);
2041
                PdfId fontObject2 = writer.newObject();
2042
                writer.startObj(fontObject2);
2043
                PutDoc("<<\n/Type /Font\n/Subtype ");
2044
                PutDoc(subtype + "\n");
23561 jghali 2045
                PutDoc("/Name " + Pdf::toName(fontName) + "S" + Pdf::toPdf(Fc) + "\n");
23563 jghali 2046
                PutDoc("/BaseFont " + Pdf::toName(baseFont) + "\n");
20112 jghali 2047
                PutDoc("/FirstChar 0\n");
23561 jghali 2048
                PutDoc("/LastChar " + Pdf::toPdf(chCount-1) + "\n");
2049
                PutDoc("/Widths " + Pdf::toPdf(fontWidths2) + " 0 R\n");
2050
                PutDoc("/Encoding " + Pdf::toPdf(fontEncoding2) + " 0 R\n");
2051
                PutDoc("/ToUnicode " + Pdf::toPdf(fontToUnicode2) + " 0 R\n");
2052
                PutDoc("/FontDescriptor " + Pdf::toPdf(fontDes) + " 0 R\n");
20112 jghali 2053
                PutDoc(">>");
2054
                writer.endObj(fontObject2);