Subversion Repositories Scribus

Rev

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