Subversion Repositories Scribus

Rev

Rev 23727 | Rev 23729 | 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);
1434
        result.method = Use_Type3;
1435
        result.encoding = Encode_256;
20103 avox 1436
 
22008 jghali 1437
        // TrueType font rasterizers use non-zero winding file rule,
1438
        // vs even-odd for current Postscript font rasterizers
1439
        // Note: OTF CFF2 will also use non-zero winding rule, so
1440
        // change code below when adding CCF2 support to Scribus
1441
        // ref: https://www.microsoft.com/typography/OTSpec/cff2.htm
1442
        bool useNonZeroRule = (face.type() == ScFace::TTF);
1443
 
20112 jghali 1444
        uint SubFonts = 0;
1445
        int glyphCount = 0;
1446
        double minx =  std::numeric_limits<double>::max();
1447
        double miny =  std::numeric_limits<double>::max();
1448
        double maxx = -std::numeric_limits<double>::max();
1449
        double maxy = -std::numeric_limits<double>::max();
22142 jghali 1450
 
20112 jghali 1451
        QList<uint> glyphWidths;
1452
        QList<QByteArray> charProcs;
22142 jghali 1453
        QStringList toUnicodeMaps;
1454
        QList<int> toUnicodeMapsCount;
22719 jghali 1455
        QString toUnicodeMap;
22142 jghali 1456
        int toUnicodeMapCounter = 0;
20112 jghali 1457
        QByteArray encoding = "<< /Type /Encoding\n/Differences [ 0\n";
1458
        QByteArray fon;
1459
        QMap<uint, uint> glyphMapping;
22142 jghali 1460
 
20112 jghali 1461
        ScFace::FaceEncoding gl;
1462
        face.glyphNames(gl);
22142 jghali 1463
 
20112 jghali 1464
        QMap<uint,FPointArray>::ConstIterator ig;
1465
        for (ig = RealGlyphs.cbegin(); ig != RealGlyphs.cend(); ++ig)
1466
        {
1467
                FPoint np, np1, np2;
1468
                bool nPath = true;
1469
                fon.resize(0);
1470
                if (ig.value().size() > 3)
1471
                {
1472
                        FPointArray gly = ig.value();
1473
                        QTransform mat;
1474
                        mat.scale(100.0, -100.0);
1475
                        gly.map(mat);
1476
                        gly.translate(0, 1000);
23563 jghali 1477
                        for (int poi = 0; poi < gly.size() - 3; poi += 4)
20112 jghali 1478
                        {
1479
                                if (gly.isMarker(poi))
1480
                                {
1481
                                        fon += "h\n";
1482
                                        nPath = true;
1483
                                        continue;
1484
                                }
1485
                                if (nPath)
1486
                                {
1487
                                        np = gly.point(poi);
23561 jghali 1488
                                        fon += FToStr(np.x()) + " " + FToStr(np.y()) + " m\n";
20112 jghali 1489
                                        nPath = false;
1490
                                }
1491
                                np = gly.point(poi+1);
1492
                                np1 = gly.point(poi+3);
1493
                                np2 = gly.point(poi+2);
1494
                                fon += FToStr(np.x()) + " " + FToStr(np.y()) + " " + FToStr(np1.x()) + " " + FToStr(np1.y()) + " " + FToStr(np2.x()) + " " + FToStr(np2.y()) + " c\n";
1495
                        }
22008 jghali 1496
                        fon += useNonZeroRule? "h f\n" : "h f*\n";
20112 jghali 1497
                        np = getMinClipF(&gly);
1498
                        np1 = getMaxClipF(&gly);
1499
                }
1500
                else
1501
                {
20684 jghali 1502
                        // 13654: make evince emit warning about "Syntax Error: No current point in closepath"
1503
                        // PDF specification is not quite clear about that, but a single "h" operator might not
1504
                        // be a valid way to make an empty path
1505
                        // fon = "h";
20112 jghali 1506
                        np = FPoint(0, 0);
1507
                        np1 = FPoint(0, 0);
1508
                }
23563 jghali 1509
                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 1510
                minx = qMin(minx, np.x());
1511
                miny = qMin(miny, np.y());
1512
                maxx = qMax(maxx, np1.x());
1513
                maxy = qMax(maxy, np1.y());
1514
                glyphWidths.append(qRound(np1.x()));
22142 jghali 1515
 
20112 jghali 1516
                PdfId charProcObject = writer.newObject();
1517
                writer.startObj(charProcObject);
1518
                if (Options.Compress)
1519
                        fon = CompressArray(fon);
22717 jghali 1520
                PutDoc("<< /Length " + Pdf::toPdf(fon.length() + 1));
20112 jghali 1521
                if (Options.Compress)
1522
                        PutDoc("\n/Filter /FlateDecode");
22717 jghali 1523
                PutDoc("\n>>\nstream\n" + EncStream(fon, charProcObject) + "\nendstream");
20112 jghali 1524
                writer.endObj(charProcObject);
22142 jghali 1525
 
22717 jghali 1526
                // #15449 : in some cases we cannot retrieve glyph names for all glyphs we need
1527
                // using ScFace's glyphNames(), so generate custom glyph names using glyph index.
1528
                // With Type 3 fonts we have more flexibility than with other fonts.
1529
                QString glGlyphName, glToUnicode;
1530
                auto  glyphIt = gl.constFind(ig.key());
1531
                if ((glyphIt != gl.constEnd()) && (glyphIt->charcode > 0))
1532
                        glToUnicode = QString::asprintf("%04X", glyphIt->charcode);
1533
                else
1534
                        glToUnicode = QString("0000");
1535
                glGlyphName = "gly" + QString::asprintf("%04X", ig.key());
1536
 
1537
                charProcs.append(Pdf::toName(glGlyphName) + " " + Pdf::toPdf(charProcObject) + " 0 R\n");
1538
                encoding += Pdf::toName(glGlyphName) + " ";
1539
                glyphMapping.insert(ig.key(), glyphCount + SubFonts * 256);
1540
 
1541
                QString tmp = QString::asprintf("%02X", glyphCount);
1542
                toUnicodeMap += "<" + Pdf::toAscii(tmp) + "> <" + Pdf::toAscii(glToUnicode) + ">\n";
22142 jghali 1543
                toUnicodeMapCounter++;
1544
                if (toUnicodeMapCounter == 100)
1545
                {
1546
                        toUnicodeMaps.append(toUnicodeMap);
1547
                        toUnicodeMapsCount.append(toUnicodeMapCounter);
22717 jghali 1548
                        toUnicodeMap.clear();
22142 jghali 1549
                        toUnicodeMapCounter = 0;
1550
                }
1551
 
20112 jghali 1552
                glyphCount++;
1553
                int glyphsLeft = RealGlyphs.count() - SubFonts * 256;
1554
                if ((glyphCount > 255) || (glyphCount == glyphsLeft))
1555
                {
1556
                        PdfId fontWidths = writer.newObject();
1557
                        writer.startObj(fontWidths);
1558
                        PutDoc("[ ");
1559
                        for (int ww = 0; ww < glyphWidths.count(); ++ww)
1560
                        {
23561 jghali 1561
                                PutDoc(Pdf::toPdf(glyphWidths[ww]) + " ");
20112 jghali 1562
                        }
1563
                        PutDoc("]");
1564
                        writer.endObj(fontWidths);
1565
                        PdfId fontCharProcs = writer.newObject();
1566
                        writer.startObj(fontCharProcs);
1567
                        PutDoc("<<\n");
1568
                        for (int ww = 0; ww < charProcs.count(); ++ww)
1569
                        {
1570
                                PutDoc(charProcs[ww]);
1571
                        }
1572
                        PutDoc(">>");
1573
                        writer.endObj(fontCharProcs);
1574
                        PdfId fontEncoding = writer.newObject();
1575
                        writer.startObj(fontEncoding);
1576
                        PutDoc(encoding);
1577
                        PutDoc("]\n");
1578
                        PutDoc(">>");
1579
                        writer.endObj(fontEncoding);
22142 jghali 1580
 
1581
                        if (toUnicodeMapCounter != 0)
1582
                        {
1583
                                toUnicodeMaps.append(toUnicodeMap);
1584
                                toUnicodeMapsCount.append(toUnicodeMapCounter);
1585
                        }
1586
 
22719 jghali 1587
                        QByteArray toUnicodeMapStream;
22142 jghali 1588
                        toUnicodeMapStream += "/CIDInit /ProcSet findresource begin\n";
1589
                        toUnicodeMapStream += "12 dict begin\n";
1590
                        toUnicodeMapStream += "begincmap\n";
1591
                        toUnicodeMapStream += "/CIDSystemInfo <<\n";
1592
                        toUnicodeMapStream += "/Registry (Adobe)\n";
1593
                        toUnicodeMapStream += "/Ordering (UCS)\n";
1594
                        toUnicodeMapStream += "/Supplement 0\n";
1595
                        toUnicodeMapStream += ">> def\n";
1596
                        toUnicodeMapStream += "/CMapName /Adobe-Identity-UCS def\n";
1597
                        toUnicodeMapStream += "/CMapType 2 def\n";
1598
                        toUnicodeMapStream += "1 begincodespacerange\n";
1599
                        toUnicodeMapStream += "<0000> <FFFF>\n";
1600
                        toUnicodeMapStream += "endcodespacerange\n";
1601
                        for (int uniC = 0; uniC < toUnicodeMaps.count(); uniC++)
1602
                        {
1603
                                toUnicodeMapStream += Pdf::toPdf(toUnicodeMapsCount[uniC]);
1604
                                toUnicodeMapStream += " beginbfchar\n";
1605
                                toUnicodeMapStream += toUnicodeMaps[uniC];
1606
                                toUnicodeMapStream += "endbfchar\n";
1607
                        }
1608
                        toUnicodeMapStream += "endcmap\n";
1609
                        toUnicodeMapStream += "CMapName currentdict /CMap defineresource pop\n";
1610
                        toUnicodeMapStream += "end\n";
1611
                        toUnicodeMapStream += "end\n";
1612
                        PdfId fontToUnicode = WritePDFStream(toUnicodeMapStream);
1613
 
20112 jghali 1614
                        PdfId font3Object = writer.newObject();
1615
                        writer.startObj(font3Object);
1616
                        PutDoc("<<\n/Type /Font\n/Subtype /Type3\n");
23561 jghali 1617
                        PutDoc("/Name /" + name + "S" + Pdf::toPdf(SubFonts) + "\n");
20112 jghali 1618
                        PutDoc("/FirstChar 0\n");
23561 jghali 1619
                        PutDoc("/LastChar " + Pdf::toPdf(glyphCount-1) + "\n");
1620
                        PutDoc("/Widths " + Pdf::toPdf(fontWidths) + " 0 R\n");
1621
                        PutDoc("/CharProcs " + Pdf::toPdf(fontCharProcs) + " 0 R\n");
23563 jghali 1622
                        PutDoc("/FontBBox [" + Pdf::toPdf(qRound(minx)) + " " + Pdf::toPdf(qRound(miny)) + " " + Pdf::toPdf(qRound(maxx)) + " " + Pdf::toPdf(qRound(maxy)) + "]\n");
20112 jghali 1623
                        PutDoc("/FontMatrix [0.001 0 0 0.001 0 0]\n");
23561 jghali 1624
                        PutDoc("/Encoding " + Pdf::toPdf(fontEncoding) + " 0 R\n");
22142 jghali 1625
                        PutDoc("/ToUnicode " + Pdf::toPdf(fontToUnicode) + " 0 R\n");
20112 jghali 1626
                        PutDoc(">>");
1627
                        writer.endObj(font3Object);
23561 jghali 1628
                        pageData.FObjects[name + "S" + Pdf::toPdf(SubFonts)] = font3Object;
20112 jghali 1629
                        charProcs.clear();
1630
                        glyphWidths.clear();
22142 jghali 1631
                        toUnicodeMaps.clear();
1632
                        toUnicodeMapsCount.clear();
1633
                        toUnicodeMap.clear();
1634
                        toUnicodeMapCounter = 0;
20112 jghali 1635
                        glyphCount = 0;
1636
                        ++SubFonts;
1637
                        minx =  std::numeric_limits<double>::max();
1638
                        miny =  std::numeric_limits<double>::max();
1639
                        maxx = -std::numeric_limits<double>::max();
1640
                        maxy = -std::numeric_limits<double>::max();
1641
                        encoding = "<< /Type /Encoding\n/Differences [ 0\n";
1642
                }
1643
        }
1644
        result.glyphmap = glyphMapping;
1645
        return result;
20103 avox 1646
}
1647
 
1648
 
1649
PdfFont PDFLibCore::PDF_WriteGlyphsAsXForms(const QByteArray& fontName, ScFace& face, const QMap<uint,FPointArray>& RealGlyphs)
1650
{
20112 jghali 1651
        PdfFont result;
1652
        result.name = Pdf::toName(fontName);
1653
        result.method = Use_XForm;
1654
        result.encoding = Encode_224;
20103 avox 1655
 
22008 jghali 1656
        // TrueType font rasterizers use non-zero winding file rule,
1657
        // vs even-odd for current Postscript font rasterizers
1658
        // Note: OTF CFF2 will also use non-zero winding rule, so
1659
        // change code below when adding CCF2 support to Scribus
1660
        // ref: https://www.microsoft.com/typography/OTSpec/cff2.htm
1661
        bool useNonZeroRule = (face.type() == ScFace::TTF);
1662
 
20112 jghali 1663
        QByteArray fon;
1664
        QMap<uint,FPointArray>::ConstIterator ig;
1665
        for (ig = RealGlyphs.cbegin(); ig != RealGlyphs.cend(); ++ig)
1666
        {
1667
                FPoint np, np1, np2;
1668
                bool nPath = true;
1669
                fon.resize(0);
1670
                if (ig.value().size() > 3)
1671
                {
1672
                        FPointArray gly = ig.value();
1673
                        QTransform mat;
1674
                        mat.scale(0.1, 0.1);
1675
                        gly.map(mat);
23563 jghali 1676
                        for (int poi = 0; poi < gly.size() - 3; poi += 4)
20112 jghali 1677
                        {
1678
                                if (gly.isMarker(poi))
1679
                                {
1680
                                        fon += "h\n";
1681
                                        nPath = true;
1682
                                        continue;
1683
                                }
1684
                                if (nPath)
1685
                                {
1686
                                        np = gly.point(poi);
23561 jghali 1687
                                        fon += FToStr(np.x()) + " " + FToStr(-np.y()) + " m\n";
20112 jghali 1688
                                        nPath = false;
1689
                                }
1690
                                np = gly.point(poi+1);
1691
                                np1 = gly.point(poi+3);
1692
                                np2 = gly.point(poi+2);
1693
                                fon += FToStr(np.x()) + " " + FToStr(-np.y()) + " " +
1694
                                FToStr(np1.x()) + " " + FToStr(-np1.y()) + " " +
1695
                                FToStr(np2.x()) + " " + FToStr(-np2.y()) + " c\n";
1696
                        }
22008 jghali 1697
                        fon += useNonZeroRule? "h f\n" : "h f*\n";
20112 jghali 1698
                        np = getMinClipF(&gly);
1699
                        np1 = getMaxClipF(&gly);
1700
                }
1701
                else
1702
                {
20684 jghali 1703
                        // 13654: make evince emit warning about "Syntax Error: No current point in closepath"
1704
                        // PDF specification is not quite clear about that, but a single "h" operator might not
1705
                        // be a valid way to make an empty path
1706
                        fon = " " /*"h"*/;
20112 jghali 1707
                        np = FPoint(0, 0);
1708
                        np1 = FPoint(0, 0);
1709
                }
1710
                PdfId fontGlyphXForm = writer.newObject();
1711
                writer.startObj(fontGlyphXForm);
1712
                PutDoc("<<\n/Type /XObject\n/Subtype /Form\n/FormType 1\n");
23563 jghali 1713
                PutDoc("/BBox [ " + FToStr(np.x()) + " " + FToStr(-np.y()) + " " + FToStr(np1.x()) + " " + FToStr(-np1.y()) + " ]\n");
20112 jghali 1714
                PutDoc("/Resources << /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]\n");
1715
                PutDoc(">>\n");
1716
                if (Options.Compress)
1717
                        fon = CompressArray(fon);
23563 jghali 1718
                PutDoc("/Length " + Pdf::toPdf(fon.length() + 1));
20112 jghali 1719
                if (Options.Compress)
1720
                        PutDoc("\n/Filter /FlateDecode");
23561 jghali 1721
                PutDoc(" >>\nstream\n" + EncStream(fon, fontGlyphXForm) + "\nendstream");
20112 jghali 1722
                writer.endObj(fontGlyphXForm);
21894 jghali 1723
                pageData.XObjects[fontName + "_gl" + Pdf::toPdf(ig.key())] = fontGlyphXForm;
20112 jghali 1724
        }
1725
        return result;
20103 avox 1726
}
1727
 
1728
PdfId PDFLibCore::PDF_EmbedFontObject(const QByteArray& font, const QByteArray& subtype)
1729
{
20112 jghali 1730
        PdfId embeddedFontObject = writer.newObject();
1731
        writer.startObj(embeddedFontObject);
1732
        int len = font.length();
1733
        QByteArray ttf = (Options.Compress? CompressArray(font) : font);
1734
        //qDebug() << QString("sfnt data: size=%1 compressed=%2").arg(len).arg(bb.length());
1735
        PutDoc("<<\n/Length " + Pdf::toPdf(ttf.length() + 1) + "\n");
1736
        PutDoc("/Length1 " + Pdf::toPdf(len) + "\n");
1737
        if (subtype.size() > 0)
1738
                PutDoc("/Subtype " + subtype);
1739
        if (Options.Compress)
1740
                PutDoc("/Filter /FlateDecode\n");
1741
        PutDoc(">>\nstream\n");
1742
        EncodeArrayToStream(ttf, embeddedFontObject);
1743
        PutDoc("\nendstream");
1744
        writer.endObj(embeddedFontObject);
1745
        return embeddedFontObject;
20103 avox 1746
}
1747
 
22600 craig 1748
QByteArray PDFLibCore::PDF_GenerateSubsetTag(const QByteArray& fontName, const QList<uint>& usedGlyphs)
21775 jghali 1749
{
1750
        uint hash, mod;
1751
        QVector<uint> glyphVec = usedGlyphs.toVector();
1752
 
1753
        hash = qHashBits(fontName.constData(), fontName.size(), 0);
1754
        hash = qHashBits(glyphVec.constData(), glyphVec.size() * sizeof(uint), hash);
1755
 
1756
        QByteArray subsetTag(6, (char) 0);
1757
        for (int i = 0; i < 6; ++i)
1758
        {
1759
                mod = hash % 26;
1760
                subsetTag[i] = 'A' + mod;
1761
                hash = (hash - mod) / 26;
1762
        }
1763
 
1764
        return subsetTag;
1765
}
1766
 
20103 avox 1767
PdfId PDFLibCore::PDF_WriteFontDescriptor(const QByteArray& fontName, ScFace& face, ScFace::FontFormat fformat, PdfId embeddedFontObject)
1768
{
20112 jghali 1769
        PdfId fontDescriptor = writer.newObject();
1770
        writer.startObj(fontDescriptor);
1771
        // TODO: think about QByteArray ScFace::getFontDescriptor() -- AV
1772
        PutDoc("<<\n/Type /FontDescriptor\n");
1773
        PutDoc("/FontName "  + Pdf::toName(fontName) + "\n");
1774
        PutDoc("/FontBBox [ " + Pdf::toAscii(face.pdfFontBBoxAsString()) + " ]\n");
1775
        PutDoc("/Flags ");
1776
        //FIXME: isItalic() should be queried from ScFace, not from Qt -- AV
1777
        //QFontInfo fo = QFontInfo(it.data());
1778
        int pfl = 0;
1779
        if (face.isFixedPitch())
1780
                pfl = pfl ^ 1;
1781
        //if (fo.italic())
1782
        if (face.italicAngleAsString() != "0")
1783
                pfl = pfl ^ 64;
1784
        //                      pfl = pfl ^ 4;
1785
        pfl = pfl ^ 32;
23561 jghali 1786
        PutDoc(Pdf::toPdf(pfl) + "\n");
1787
        PutDoc("/Ascent "      + Pdf::toAscii(face.pdfAscentAsString()) + "\n");
1788
        PutDoc("/Descent "     + Pdf::toAscii(face.pdfDescentAsString()) + "\n");
1789
        PutDoc("/CapHeight "   + Pdf::toAscii(face.pdfCapHeightAsString()) + "\n");
1790
        PutDoc("/ItalicAngle " + Pdf::toAscii(face.italicAngleAsString()) + "\n");
20112 jghali 1791
        PutDoc("/StemV 1\n");
1792
        if (embeddedFontObject != 0)
1793
        {
1794
                if (fformat == ScFace::SFNT || fformat == ScFace::TTCF)
1795
                {
1796
                        if (face.type() == ScFace::OTF)
1797
                        {
23548 jghali 1798
                                PutDoc("/FontFile3 " + Pdf::toPdf(embeddedFontObject) + " 0 R\n");
20112 jghali 1799
                        }
1800
                        else
1801
                        {
23548 jghali 1802
                                PutDoc("/FontFile2 " + Pdf::toPdf(embeddedFontObject) + " 0 R\n");
20112 jghali 1803
                        }
1804
                }
1805
                else if (fformat == ScFace::PFB)
23561 jghali 1806
                        PutDoc("/FontFile " + Pdf::toPdf(embeddedFontObject) + " 0 R\n");
20112 jghali 1807
                else if (fformat == ScFace::PFA)
23561 jghali 1808
                        PutDoc("/FontFile " + Pdf::toPdf(embeddedFontObject) + " 0 R\n");
20112 jghali 1809
        }
1810
        PutDoc(">>");
1811
        writer.endObj(fontDescriptor);
1812
        return fontDescriptor;
20103 avox 1813
}
1814
 
22600 craig 1815
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 1816
{
20112 jghali 1817
        PdfFont result;
1818
        result.name = Pdf::toName(fontName);
23727 jghali 1819
        result.usage = Used_in_Content;
20112 jghali 1820
        result.method = glyphmap.isEmpty()? Use_Embedded : Use_Subset;
1821
        result.encoding = glyphmap.isEmpty()? Encode_IdentityH : Encode_Subset;
1822
        result.glyphmap = glyphmap;
1823
 
1824
        PdfId fontWidths2 = writer.newObject();
1825
        writer.startObj(fontWidths2);
1826
        QList<QByteArray> toUnicodeMaps;
1827
        QList<int> toUnicodeMapsCount;
22719 jghali 1828
        QByteArray toUnicodeMap;
20112 jghali 1829
        int toUnicodeMapCounter = 0;
1830
 
1831
        PutDoc("[ ");
1832
        QList<uint> keys = gl.uniqueKeys();
1833
        bool seenNotDef = false;
23483 jghali 1834
        for (auto git = keys.begin(); git != keys.end(); ++git)
20112 jghali 1835
        {
1836
                uint gid = result.encoding == Encode_Subset? glyphmap[*git] : *git;
1837
                if (gid > 0 || !seenNotDef)
1838
                {
1839
                        seenNotDef |= (gid == 0);
23483 jghali 1840
                        PutDoc(Pdf::toPdf(gid) + " [" + Pdf::toPdf(static_cast<int>(face.glyphWidth(*git) * 1000)) + "] " );
1841
                        QString tmp = QString::asprintf("%04X", gid);
1842
                        QString tmp2 = gl.value(*git).toUnicode;
23563 jghali 1843
                        toUnicodeMap += "<" + Pdf::toAscii(tmp) + "> <" + Pdf::toAscii(tmp2) + ">\n";
20112 jghali 1844
                        toUnicodeMapCounter++;
1845
                        if (toUnicodeMapCounter == 100)
1846
                        {
1847
                                toUnicodeMaps.append(toUnicodeMap);
1848
                                toUnicodeMapsCount.append(toUnicodeMapCounter);
22719 jghali 1849
                                toUnicodeMap.clear();
20112 jghali 1850
                                toUnicodeMapCounter = 0;
1851
                        }
1852
                }
1853
        }
1854
        PutDoc("]");
1855
        writer.endObj(fontWidths2);
1856
        if (toUnicodeMapCounter != 0)
1857
        {
1858
                toUnicodeMaps.append(toUnicodeMap);
1859
                toUnicodeMapsCount.append(toUnicodeMapCounter);
1860
        }
22719 jghali 1861
        QByteArray toUnicodeMapStream;
20112 jghali 1862
        toUnicodeMapStream += "/CIDInit /ProcSet findresource begin\n";
1863
        toUnicodeMapStream += "12 dict begin\n";
1864
        toUnicodeMapStream += "begincmap\n";
1865
        toUnicodeMapStream += "/CIDSystemInfo <<\n";
1866
        toUnicodeMapStream += "/Registry (Adobe)\n";
1867
        toUnicodeMapStream += "/Ordering (UCS)\n";
1868
        toUnicodeMapStream += "/Supplement 0\n";
1869
        toUnicodeMapStream += ">> def\n";
1870
        toUnicodeMapStream += "/CMapName /Adobe-Identity-UCS def\n";
1871
        toUnicodeMapStream += "/CMapType 2 def\n";
1872
        toUnicodeMapStream += "1 begincodespacerange\n";
1873
        toUnicodeMapStream += "<0000> <FFFF>\n";
1874
        toUnicodeMapStream += "endcodespacerange\n";
1875
        for (int uniC = 0; uniC < toUnicodeMaps.count(); uniC++)
1876
        {
1877
                toUnicodeMapStream += Pdf::toPdf(toUnicodeMapsCount[uniC]);
1878
                toUnicodeMapStream += " beginbfchar\n";
1879
                toUnicodeMapStream += toUnicodeMaps[uniC];
1880
                toUnicodeMapStream += "endbfchar\n";
1881
        }
1882
        toUnicodeMapStream += "endcmap\n";
1883
        toUnicodeMapStream += "CMapName currentdict /CMap defineresource pop\n";
1884
        toUnicodeMapStream += "end\n";
1885
        toUnicodeMapStream += "end\n";
1886
        PdfId fontToUnicode2 = WritePDFStream(toUnicodeMapStream);
1887
        PdfId fontObject2 = writer.newObject();
1888
        writer.startObj(fontObject2);
1889
        PutDoc("<<\n/Type /Font\n/Subtype /Type0\n");
1890
        PutDoc("/Name " + Pdf::toName(fontName) + "\n");
23563 jghali 1891
        PutDoc("/BaseFont " + Pdf::toName(baseFont) + "\n");
20112 jghali 1892
        PutDoc("/Encoding /Identity-H\n");
23561 jghali 1893
        PutDoc("/ToUnicode " + Pdf::toPdf(fontToUnicode2) + " 0 R\n");
20112 jghali 1894
        PutDoc("/DescendantFonts [");
1895
        PutDoc("<</Type /Font");
1896
        if (face.type() == ScFace::OTF)
1897
                PutDoc("/Subtype /CIDFontType0");
1898
        else
1899
                PutDoc("/Subtype /CIDFontType2");
21759 jghali 1900
        PutDoc("/BaseFont " + Pdf::toName(baseFont));
23563 jghali 1901
        PutDoc("/FontDescriptor " + Pdf::toPdf(fontDes) + " 0 R");
20112 jghali 1902
        PutDoc("/CIDSystemInfo <</Ordering(Identity)/Registry(Adobe)/Supplement 0>>");
1903
        PutDoc("/DW 1000");
23561 jghali 1904
        PutDoc("/W " + Pdf::toPdf(fontWidths2) + " 0 R");
20112 jghali 1905
        PutDoc("/CIDToGIDMap /Identity");
1906
        PutDoc(">>"); // close CIDFont dictionary
1907
        PutDoc("]\n"); // close DescendantFonts array
1908
        PutDoc(">>");
1909
        writer.endObj(fontObject2);
1910
        pageData.FObjects[fontName] = fontObject2;
1911
        return result;
20103 avox 1912
}
1913
 
1914
 
1915
PdfFont PDFLibCore::PDF_EncodeSimpleFont(const QByteArray& fontName, ScFace& face, const QByteArray& baseFont, const QByteArray& subtype, bool isEmbedded, PdfId fontDes, const ScFace::FaceEncoding& gl)
1916
{
20112 jghali 1917
        PdfFont result;
1918
        result.name = Pdf::toName(fontName);
23728 jghali 1919
        result.usage = Used_in_Content;
20112 jghali 1920
        result.method = isEmbedded? Use_Embedded : Use_System;
1921
        result.encoding = Encode_224;
1922
 
1923
        int nglyphs = 0;
22717 jghali 1924
        for (auto gli = gl.cbegin(); gli != gl.cend(); ++gli)
20112 jghali 1925
        {
1926
                if (gli.key() > static_cast<uint>(nglyphs))
1927
                        nglyphs = gli.key();
1928
        }
1929
        ++nglyphs;
1930
        //                      qDebug() << QString("pdflib: nglyphs %1 max %2").arg(nglyphs).arg(face.maxGlyph());
1931
 
1932
        uint Fcc = nglyphs / 224;
1933
        if ((nglyphs % 224) != 0)
1934
                Fcc += 1;
1935
        for (uint Fc = 0; Fc < Fcc; ++Fc)
1936
        {
1937
                PdfId fontWidths2 = writer.newObject();
1938
                writer.startObj(fontWidths2);
1939
                int chCount = 32;
1940
                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 ");
1941
                for (int ww = 32; ww < 256; ++ww)
1942
                {
1943
                        uint glyph = 224 * Fc + ww - 32;
1944
                        if (gl.contains(glyph))
23561 jghali 1945
                                PutDoc(Pdf::toPdf(static_cast<int>(face.glyphWidth(glyph)* 1000)) + " ");
20112 jghali 1946
                        else
1947
                                PutDoc("0 ");
1948
                        chCount++;
1949
                        if (signed(glyph) == nglyphs-1)
1950
                                break;
1951
                }
1952
                PutDoc("]");
1953
                writer.endObj(fontWidths2);
22717 jghali 1954
 
20112 jghali 1955
                QStringList toUnicodeMaps;
22719 jghali 1956
                QList<int>  toUnicodeMapsCount;
1957
                QString toUnicodeMap;
20112 jghali 1958
                int toUnicodeMapCounter = 0;
22717 jghali 1959
                int crc = 0;
1960
                bool startOfSeq = true;
1961
 
1962
                PdfId fontEncoding2 = writer.newObject();
1963
                writer.startObj(fontEncoding2);
20112 jghali 1964
                PutDoc("<< /Type /Encoding\n");
1965
                PutDoc("/Differences [ \n");
1966
                for (int ww2 = 32; ww2 < 256; ++ww2)
1967
                {
1968
                        uint glyph = 224 * Fc + ww2 - 32;
1969
                        ScFace::FaceEncoding::ConstIterator glIt = gl.find(glyph);
22143 jghali 1970
                        if (glIt != gl.cend() && !glIt.value().glyphName.isEmpty())
20112 jghali 1971
                        {
22143 jghali 1972
                                const ScFace::GlyphEncoding& glEncoding = glIt.value();
20112 jghali 1973
                                if (startOfSeq)
1974
                                {
23561 jghali 1975
                                        PutDoc(Pdf::toPdf(ww2) + " ");
20112 jghali 1976
                                        startOfSeq = false;
1977
                                }
23561 jghali 1978
                                PutDoc(Pdf::toName(glEncoding.glyphName) + " ");
23483 jghali 1979
                                QString tmp = QString::asprintf("%02X", ww2);
1980
                                QString tmp2 = glEncoding.toUnicode;
20112 jghali 1981
                                toUnicodeMap += "<" + Pdf::toAscii(tmp) + "> <" + Pdf::toAscii(tmp2) + ">\n";
1982
                                //QString("<%1> <%2>\n").arg(tmp).arg((tmp2));
1983
                                toUnicodeMapCounter++;
1984
                                if (toUnicodeMapCounter == 100)
1985
                                {
1986
                                        toUnicodeMaps.append(toUnicodeMap);
1987
                                        toUnicodeMapsCount.append(toUnicodeMapCounter);
22717 jghali 1988
                                        toUnicodeMap.clear();
20112 jghali 1989
                                        toUnicodeMapCounter = 0;
1990
                                }
1991
                                crc++;
1992
                        }
1993
                        else
1994
                        {
1995
                                startOfSeq = true;
1996
                        }
1997
                        if (signed(glyph) == nglyphs-1)
1998
                                break;
1999
                        if (crc > 8)
2000
                        {
2001
                                PutDoc("\n");
2002
                                crc = 0;
2003
                        }
2004
                }
2005
                if (toUnicodeMapCounter != 0)
2006
                {
2007
                        toUnicodeMaps.append(toUnicodeMap);
2008
                        toUnicodeMapsCount.append(toUnicodeMapCounter);
2009
                }
2010
                PutDoc("]\n");
2011
                PutDoc(">>");
2012
                writer.endObj(fontEncoding2);
22719 jghali 2013
                QByteArray toUnicodeMapStream;
20112 jghali 2014
                toUnicodeMapStream += "/CIDInit /ProcSet findresource begin\n";
2015
                toUnicodeMapStream += "12 dict begin\n";
2016
                toUnicodeMapStream += "begincmap\n";
2017
                toUnicodeMapStream += "/CIDSystemInfo <<\n";
2018
                toUnicodeMapStream += "/Registry (Adobe)\n";
2019
                toUnicodeMapStream += "/Ordering (UCS)\n";
2020
                toUnicodeMapStream += "/Supplement 0\n";
2021
                toUnicodeMapStream += ">> def\n";
2022
                toUnicodeMapStream += "/CMapName /Adobe-Identity-UCS def\n";
2023
                toUnicodeMapStream += "/CMapType 2 def\n";
2024
                toUnicodeMapStream += "1 begincodespacerange\n";
2025
                toUnicodeMapStream += "<0000> <FFFF>\n";
2026
                toUnicodeMapStream += "endcodespacerange\n";
2027
                for (int uniC = 0; uniC < toUnicodeMaps.count(); uniC++)
2028
                {
2029
                        toUnicodeMapStream += Pdf::toPdf(toUnicodeMapsCount[uniC]);
2030
                        toUnicodeMapStream += " beginbfchar\n";
2031
                        toUnicodeMapStream += toUnicodeMaps[uniC];
2032
                        toUnicodeMapStream += "endbfchar\n";
2033
                }
2034
                toUnicodeMapStream += "endcmap\n";
2035
                toUnicodeMapStream += "CMapName currentdict /CMap defineresource pop\n";
2036
                toUnicodeMapStream += "end\n";
2037
                toUnicodeMapStream += "end\n";
2038
                PdfId fontToUnicode2 = WritePDFStream(toUnicodeMapStream);
2039
                PdfId fontObject2 = writer.newObject();
2040
                writer.startObj(fontObject2);
2041
                PutDoc("<<\n/Type /Font\n/Subtype ");
2042
                PutDoc(subtype + "\n");
23561 jghali 2043
                PutDoc("/Name " + Pdf::toName(fontName) + "S" + Pdf::toPdf(Fc) + "\n");
23563 jghali 2044
                PutDoc("/BaseFont " + Pdf::toName(baseFont) + "\n");
20112 jghali 2045
                PutDoc("/FirstChar 0\n");
23561 jghali 2046
                PutDoc("/LastChar " + Pdf::toPdf(chCount-1) + "\n");
2047
                PutDoc("/Widths " + Pdf::toPdf(fontWidths2) + " 0 R\n");
2048
                PutDoc("/Encoding " + Pdf::toPdf(fontEncoding2) + " 0 R\n");
2049
                PutDoc("/ToUnicode " + Pdf::toPdf(fontToUnicode2) + " 0 R\n");
2050
                PutDoc("/FontDescriptor " + Pdf::toPdf(fontDes) + " 0 R\n");
20112 jghali 2051
                PutDoc(">>");
2052
                writer.endObj(fontObject2);
23561 jghali 2053
                pageData.FObjects[fontName + "S" + Pdf::toPdf(Fc)] = fontObject2;
22720 jghali 2054
        } // for (Fc)