Subversion Repositories Scribus

Rev

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