Subversion Repositories Scribus

Rev

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

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