Subversion Repositories Scribus

Rev

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

Rev Author Line No. Line
4430 cbradney 1
/*
2
For general Scribus (>=1.3.2) copyright and licensing information please refer
3
to the COPYING file provided with the program. Following this notice may exist
4
a copyright and/or license notice that predates the release of Scribus 1.3.2
5
for which a new license (GPL+exception) is in place.
6
*/
3133 fschmid 7
/***************************************************************************
14043 jghali 8
                                                  pdflib_core.cpp  -  description
9
                                                         -------------------
10
        begin                : Sat Jan 19 2002
11
        copyright            : (C) 2002 by Franz Schmid
12
        email                : Franz.Schmid@altmuehlnet.de
3133 fschmid 13
 ***************************************************************************/
14
 
15
/***************************************************************************
16
 *                                                                         *
17
 *   This program is free software; you can redistribute it and/or modify  *
18
 *   it under the terms of the GNU General Public License as published by  *
19
 *   the Free Software Foundation; either version 2 of the License, or     *
20
 *   (at your option) any later version.                                   *
21
 *                                                                         *
22
 ***************************************************************************/
23
 
10054 craig 24
#include "pdflib_core.h"
3133 fschmid 25
 
26
#include "scconfig.h"
27
 
17539 jghali 28
#if defined(_MSC_VER) && !defined(_USE_MATH_DEFINES)
14170 jghali 29
#define _USE_MATH_DEFINES
30
#endif
12080 avox 31
#include <cmath>
32
#include <cstdlib>
4229 craig 33
#include <string>
12080 avox 34
#ifdef HAVE_UNISTD_H
35
#include <unistd.h>
36
#endif
37
 
38
#include "rc4.h"
39
 
40
#include <QByteArray>
19914 craig 41
#include <QCryptographicHash>
10223 cbradney 42
#include <QDateTime>
10004 fschmid 43
#include <QDataStream>
12080 avox 44
#include <QDebug>
10223 cbradney 45
#include <QDir>
12080 avox 46
#include <QFileInfo>
47
#include <QImage>
9803 fschmid 48
#include <QList>
11453 fschmid 49
#include <QPainterPath>
12080 avox 50
#include <QRect>
51
#include <QRegExp>
19671 jghali 52
#include <QScopedPointer>
12080 avox 53
#include <QStack>
54
#include <QString>
13839 fschmid 55
#include <QTemporaryFile>
10614 cbradney 56
#include <QTextCodec>
14043 jghali 57
#include <QtXml>
58
#include <QUuid>
3133 fschmid 59
 
10601 mrdocs 60
#include "cmsettings.h"
4028 cbradney 61
#include "commonstrings.h"
3133 fschmid 62
#include "pageitem.h"
10601 mrdocs 63
#include "pageitem_textframe.h"
16258 fschmid 64
#include "pageitem_group.h"
17312 fschmid 65
#include "pageitem_table.h"
10212 cbradney 66
#include "pdfoptions.h"
67
#include "prefscontext.h"
68
#include "prefsmanager.h"
69
#include "sccolor.h"
70
#include "sccolorengine.h"
71
#include "scfonts.h"
21107 craig 72
#include "text/textlayoutpainter.h"
20103 avox 73
#include "fonts/cff.h"
74
#include "fonts/sfnt.h"
16736 jghali 75
#include "scpage.h"
5917 jghali 76
#include "scpaths.h"
10212 cbradney 77
#include "scpattern.h"
19115 avox 78
 
5243 cbradney 79
#include "scribuscore.h"
3699 cbradney 80
#include "scribusdoc.h"
11599 jghali 81
#include "scstreamfilter_flate.h"
82
#include "scstreamfilter_rc4.h"
17312 fschmid 83
#include "tableutils.h"
3133 fschmid 84
#include "util.h"
11605 jghali 85
#include "util_file.h"
10212 cbradney 86
#include "util_formats.h"
10203 cbradney 87
#include "util_math.h"
12517 fschmid 88
#include "util_ghostscript.h"
21107 craig 89
#include "text/boxes.h"
16736 jghali 90
 
13839 fschmid 91
#ifdef HAVE_OSG
14930 fschmid 92
        #include "third_party/prc/exportPRC.h"
13839 fschmid 93
#endif
3133 fschmid 94
 
16736 jghali 95
#include "ui/bookmwin.h"
96
#include "ui/bookmarkpalette.h"
97
#include "ui/multiprogressdialog.h"
98
 
3133 fschmid 99
using namespace std;
17312 fschmid 100
using namespace TableUtils;
3133 fschmid 101
 
12096 jghali 102
#if defined(_WIN32)
103
#undef GetObject
104
#endif
3133 fschmid 105
 
21107 craig 106
static inline QByteArray FToStr(double c)
107
{
108
        double v = c;
109
        if (fabs(c) < 0.0000001)
110
                v = 0.0;
111
        return QByteArray::number(v, 'f', 5);
112
}
6562 fschmid 113
 
21107 craig 114
class PdfPainter: public TextLayoutPainter
115
{
22987 jghali 116
        QByteArray m_backBuffer;
21107 craig 117
        QByteArray m_glyphBuffer;
118
        QByteArray m_pathBuffer;
21942 craig 119
//      PageItem* m_item;
21107 craig 120
        QMap<QString, PdfFont>  m_UsedFontsP;
121
        PDFLibCore *m_pdf;
122
        uint m_PNr;
123
        const ScPage* m_page;
124
 
22098 jghali 125
        QByteArray m_prevFontName;
126
        int        m_prevFontSize;
127
 
22600 craig 128
        QByteArray transformToStr(const QTransform& tr)
21107 craig 129
        {
130
                return FToStr(tr.m11()) + " " + FToStr(-tr.m12()) + " " + FToStr(-tr.m21()) + " " + FToStr(tr.m22()) + " " + FToStr(tr.dx()) + " " + FToStr(-tr.dy());
131
        }
132
 
133
public:
134
        PdfPainter(PageItem *ite, PDFLibCore *pdf, uint num, const ScPage* pag) :
22987 jghali 135
                m_backBuffer(),
21107 craig 136
                m_glyphBuffer(),
137
                m_pathBuffer(),
21942 craig 138
//              m_item(ite),
21107 craig 139
                m_pdf(pdf),
140
                m_PNr(num),
22098 jghali 141
                m_page(pag),
142
                m_prevFontSize(-1)
21107 craig 143
        {}
144
 
145
        ~PdfPainter() {}
146
 
23073 jghali 147
        void drawGlyph(const GlyphCluster& gc) override
21107 craig 148
        {
21563 jghali 149
                if (gc.isControlGlyphs() || gc.isEmpty())
21107 craig 150
                        return;
21563 jghali 151
 
152
                double current_x = 0.0;
22507 jghali 153
                for (const GlyphLayout& gl : gc.glyphs())
154
                {
22780 jghali 155
                        if (gl.glyph >= ScFace::CONTROL_GLYPHS)
156
                        {
23072 jghali 157
                                current_x += gl.xadvance * gl.scaleH;
22780 jghali 158
                                continue;
159
                        }
160
 
21563 jghali 161
                        PdfFont pdfFont = m_pdf->UsedFontsP[font().replacementName()];
162
                        QByteArray StrokeColor;
163
                        QByteArray FillColor;
21107 craig 164
 
21563 jghali 165
                        if (strokeColor().color != CommonStrings::None)
166
                                StrokeColor = m_pdf->putColor(strokeColor().color, strokeColor().shade, false);
167
                        if (fillColor().color != CommonStrings::None)
168
                                FillColor = m_pdf->putColor(fillColor().color, fillColor().shade, true);
21107 craig 169
 
21563 jghali 170
                        QTransform transform = matrix();
171
                        if (pdfFont.method == Use_XForm)
172
                        {
173
                                if (!FillColor.isEmpty())
174
                                        m_pathBuffer += FillColor;
21107 craig 175
 
21563 jghali 176
                                m_pathBuffer += "q\n";
177
                                m_pathBuffer += transformToStr(transform) + " cm\n";
23072 jghali 178
                                m_pathBuffer += FToStr(fontSize()) + " 0 0 " + FToStr(fontSize()) + " " + FToStr(x() + gl.xoffset + current_x) + " " + FToStr(-y() + fontSize() - gl.yoffset) + " cm\n";
21107 craig 179
 
21563 jghali 180
                                if (gl.scaleV != 1.0)
23072 jghali 181
                                        m_pathBuffer += "1 0 0 1 0 " + FToStr(gl.scaleV - 1.0) + " cm\n";
21563 jghali 182
                                m_pathBuffer += FToStr(qMax(gl.scaleH, 0.1)) + " 0 0 " + FToStr(qMax(gl.scaleV, 0.1)) + " 0 0 cm\n";
21107 craig 183
 
21563 jghali 184
                                if (!FillColor.isEmpty())
21894 jghali 185
                                        m_pathBuffer += pdfFont.name + "_gl" + Pdf::toPdf(gl.glyph) + " Do\n";
21563 jghali 186
                                m_pathBuffer += "Q\n";
21107 craig 187
                        }
188
                        else
21563 jghali 189
                        {
190
                                uint gid = gl.glyph;
191
                                uint fontNr = 65535;
21107 craig 192
 
193
                                switch (pdfFont.encoding)
194
                                {
21563 jghali 195
                                case  Encode_256:
196
                                        gid = pdfFont.glyphmap[gid];
197
                                        fontNr = gid / 256;
198
                                        gid = gid % 256;
199
                                        break;
21107 craig 200
                                case Encode_224:
21563 jghali 201
                                        fontNr = gid / 224;
202
                                        gid = gid % 224 + 32;
21107 craig 203
                                        break;
21563 jghali 204
                                case Encode_Subset:
205
                                        gid = pdfFont.glyphmap[gid];
206
                                        break;
21107 craig 207
                                case Encode_IdentityH:
208
                                        break;
209
                                }
210
 
22098 jghali 211
                                QByteArray pdfFontName = pdfFont.name;
212
                                if (fontNr != 65535)
213
                                {
214
                                        pdfFontName += "S";
215
                                        pdfFontName += Pdf::toPdf(fontNr);
216
                                }
21107 craig 217
 
22099 jghali 218
                                if ((pdfFontName != m_prevFontName) || (fontSize() != m_prevFontSize))
22098 jghali 219
                                {
220
                                        m_glyphBuffer += pdfFontName + " " + FToStr(fontSize()) + " Tf\n";
221
                                        m_prevFontName = pdfFontName;
222
                                        m_prevFontSize = fontSize();
223
                                }
224
 
21563 jghali 225
                                if (!StrokeColor.isEmpty())
226
                                        m_glyphBuffer += StrokeColor;
21107 craig 227
 
21563 jghali 228
                                if (!FillColor.isEmpty())
229
                                        m_glyphBuffer += FillColor;
21107 craig 230
 
21563 jghali 231
                                m_glyphBuffer += "0 Tr\n";
21107 craig 232
 
21563 jghali 233
                                transform.translate(x() + gl.xoffset + current_x , y() + gl.yoffset);
234
                                transform.scale(qMax(gl.scaleH, 0.1), qMax(gl.scaleV, 0.1));
235
                                m_glyphBuffer += transformToStr(transform) + " Tm\n";
21107 craig 236
 
21563 jghali 237
                                if (pdfFont.method != Use_Type3 || !FillColor.isEmpty())
21107 craig 238
                                {
21563 jghali 239
                                        switch (pdfFont.encoding)
21107 craig 240
                                        {
21563 jghali 241
                                        case Encode_224:
242
                                        case Encode_256:
243
                                                m_glyphBuffer += Pdf::toHexString8(gid) + " Tj\n";
244
                                                break;
245
                                        case Encode_IdentityH:
246
                                        default:
247
                                                m_glyphBuffer += Pdf::toHexString16(gid) + " Tj\n";
248
                                                break;
21107 craig 249
                                        }
250
                                }
251
                        }
23072 jghali 252
                        current_x += gl.xadvance * gl.scaleH;
21107 craig 253
                }
21563 jghali 254
        }
21107 craig 255
 
23073 jghali 256
        void drawGlyphOutline(const GlyphCluster& gc, bool fill) override
21563 jghali 257
        {
258
                if (gc.isControlGlyphs() || gc.isEmpty())
259
                        return;
21107 craig 260
 
21563 jghali 261
                double current_x = 0.0;
22507 jghali 262
                for (const GlyphLayout& gl : gc.glyphs())
263
                {
22780 jghali 264
                        if (gl.glyph >= ScFace::CONTROL_GLYPHS)
265
                        {
23072 jghali 266
                                current_x += gl.xadvance * gl.scaleH;
22780 jghali 267
                                continue;
268
                        }
269
 
21563 jghali 270
                        PdfFont pdfFont = m_pdf->UsedFontsP[font().replacementName()];
271
                        QByteArray StrokeColor;
272
                        QByteArray FillColor;
273
                        QTransform transform = matrix();
21107 craig 274
 
21563 jghali 275
                        if (strokeColor().color != CommonStrings::None)
276
                                StrokeColor = m_pdf->putColor(strokeColor().color, strokeColor().shade, false);
277
                        if (fill && fillColor().color != CommonStrings::None)
278
                                FillColor = m_pdf->putColor(fillColor().color, fillColor().shade, true);
21107 craig 279
 
21563 jghali 280
                        if (pdfFont.method == Use_XForm)
281
                        {
23072 jghali 282
                                m_pathBuffer += "q\n";
21563 jghali 283
                                if (!StrokeColor.isEmpty())
284
                                {
23072 jghali 285
                                        m_pathBuffer += FToStr(strokeWidth()) + " w\n[] 0 d\n0 J\n0 j\n";
21563 jghali 286
                                        m_pathBuffer += StrokeColor;
287
                                }
23072 jghali 288
                                m_pathBuffer += transformToStr(transform) + " cm\n";
21107 craig 289
 
21563 jghali 290
                                if (!FillColor.isEmpty())
23072 jghali 291
                                {
292
                                        m_pathBuffer += "q\n";
21563 jghali 293
                                        m_pathBuffer += FillColor;
23072 jghali 294
                                        m_pathBuffer += FToStr(fontSize()) + " 0 0 " + FToStr(fontSize()) + " " + FToStr(x() + gl.xoffset + current_x) + " " + FToStr(-y() + fontSize() - gl.yoffset) + " cm\n";
295
                                        if (gc.scaleV() != 1.0)
296
                                                m_pathBuffer += "1 0 0 1 0 " + FToStr(gl.scaleV - 1.0) + " cm\n";
297
                                        m_pathBuffer += FToStr(qMax(gc.scaleH(), 0.1)) + " 0 0 " + FToStr(qMax(gc.scaleV(), 0.1)) + " 0 0 cm\n";
298
                                        m_pathBuffer += pdfFont.name + "_gl" + Pdf::toPdf(gl.glyph) + " Do\n";
299
                                        m_pathBuffer += "Q\n";
300
                                }
21563 jghali 301
 
23072 jghali 302
                                m_pathBuffer += "1.0 0 0 1.0 " + FToStr(x()) + " " + FToStr(fontSize() - y()) + " cm\n";
303
                                m_pathBuffer += "1.0 0 0 1.0 " + FToStr(gl.xoffset + current_x) + " " + FToStr(gc.scaleV() * fontSize() - fontSize() - gl.yoffset) + " cm\n";
21107 craig 304
 
305
                                FPointArray outline = font().glyphOutline(gl.glyph);
306
                                QTransform mat;
23072 jghali 307
                                mat.scale((fontSize() * gc.scaleH()) / 10.0, (fontSize() * gc.scaleV()) / 10.0);
21107 craig 308
                                outline.map(mat);
309
                                bool nPath = true;
310
                                FPoint np;
311
                                if (outline.size() > 3)
312
                                {
313
                                        for (int poi = 0; poi < outline.size() - 3; poi += 4)
314
                                        {
315
                                                if (outline.isMarker(poi))
316
                                                {
317
                                                        m_pathBuffer += "h\n";
318
                                                        nPath = true;
319
                                                        continue;
320
                                                }
321
 
322
                                                if (nPath)
323
                                                {
324
                                                        np = outline.point(poi);
325
                                                        m_pathBuffer += FToStr(np.x()) + " " + FToStr(-np.y()) + " m\n";
326
                                                        nPath = false;
327
                                                }
328
 
329
                                                np = outline.point(poi + 1);
330
                                                m_pathBuffer += FToStr(np.x()) + " " + FToStr(-np.y()) + " ";
331
                                                np = outline.point(poi + 3);
332
                                                m_pathBuffer += FToStr(np.x()) + " " + FToStr(-np.y()) + " ";
333
                                                np = outline.point(poi + 2);
334
                                                m_pathBuffer += FToStr(np.x()) + " " + FToStr(-np.y()) + " c\n";
335
                                        }
336
                                }
337
                                m_pathBuffer += "h s\n";
338
                                m_pathBuffer += "Q\n";
339
                        }
340
                        else
341
                        {
21563 jghali 342
                                if (!StrokeColor.isEmpty())
343
                                        m_pathBuffer += StrokeColor;
21107 craig 344
 
21563 jghali 345
                                uint gid = gl.glyph;
346
                                uint fontNr = 65535;
21107 craig 347
                                switch (pdfFont.encoding)
348
                                {
21563 jghali 349
                                case  Encode_256:
350
                                        gid = pdfFont.glyphmap[gid];
351
                                        fontNr = gid / 256;
352
                                        gid = gid % 256;
353
                                        break;
21107 craig 354
                                case Encode_224:
21563 jghali 355
                                        fontNr = gid / 224;
356
                                        gid = gid % 224 + 32;
21107 craig 357
                                        break;
21563 jghali 358
                                case Encode_Subset:
359
                                        gid = pdfFont.glyphmap[gid];
360
                                        break;
21107 craig 361
                                case Encode_IdentityH:
362
                                        break;
363
                                }
21563 jghali 364
 
22098 jghali 365
                                QByteArray pdfFontName = pdfFont.name;
366
                                if (fontNr != 65535)
367
                                {
368
                                        pdfFontName += "S";
369
                                        pdfFontName += Pdf::toPdf(fontNr);
370
                                }
21563 jghali 371
 
22099 jghali 372
                                if ((pdfFontName != m_prevFontName) || (fontSize() != m_prevFontSize))
22098 jghali 373
                                {
374
                                        m_glyphBuffer += pdfFontName + " " + FToStr(fontSize()) + " Tf\n";
375
                                        m_prevFontName = pdfFontName;
376
                                        m_prevFontSize = fontSize();
377
                                }
378
 
21563 jghali 379
                                if (!StrokeColor.isEmpty())
380
                                        m_glyphBuffer += StrokeColor;
381
 
382
                                if (!FillColor.isEmpty())
383
                                        m_glyphBuffer += FillColor;
384
 
385
                                if (pdfFont.method == Use_Type3 && !StrokeColor.isEmpty())
386
                                {
387
                                        m_pathBuffer += "q\n";
388
                                        m_pathBuffer += FToStr(strokeWidth()) + " w\n[] 0 d\n0 J\n0 j\n";
389
 
390
                                        m_pathBuffer += transformToStr(transform) + " cm\n";
23072 jghali 391
                                        m_pathBuffer += "1.0 0 0 1.0 " + FToStr(x()) + " " + FToStr(fontSize() - y()) + " cm\n";
392
                                        m_pathBuffer += "1.0 0 0 1.0 " + FToStr(gl.xoffset + current_x) + " " + FToStr(gc.scaleV() * fontSize() - fontSize() - gl.yoffset) + " cm\n";
21563 jghali 393
 
394
                                        /* paint outline */
395
                                        FPointArray outline = font().glyphOutline(gl.glyph);
396
                                        QTransform mat;
23072 jghali 397
                                        mat.scale((fontSize() * gc.scaleH()) / 10.0, (fontSize() * gc.scaleV()) / 10.0);
21563 jghali 398
                                        outline.map(mat);
399
                                        bool nPath = true;
400
                                        FPoint np;
401
                                        if (outline.size() > 3)
402
                                        {
403
                                                for (int poi = 0; poi < outline.size() - 3; poi += 4)
404
                                                {
405
                                                        if (outline.isMarker(poi))
406
                                                        {
407
                                                                m_pathBuffer += "h\n";
408
                                                                nPath = true;
409
                                                                continue;
410
                                                        }
411
 
412
                                                        if (nPath)
413
                                                        {
414
                                                                np = outline.point(poi);
415
                                                                m_pathBuffer += FToStr(np.x()) + " " + FToStr(-np.y()) + " m\n";
416
                                                                nPath = false;
417
                                                        }
418
 
419
                                                        np = outline.point(poi + 1);
420
                                                        m_pathBuffer += FToStr(np.x()) + " " + FToStr(-np.y()) + " ";
421
                                                        np = outline.point(poi + 3);
422
                                                        m_pathBuffer += FToStr(np.x()) + " " + FToStr(-np.y()) + " ";
423
                                                        np = outline.point(poi + 2);
424
                                                        m_pathBuffer += FToStr(np.x()) + " " + FToStr(-np.y()) + " c\n";
425
                                                }
426
                                        }
427
                                        m_pathBuffer += "h s\n";
428
                                        m_pathBuffer += "Q\n";
429
                                }
430
                                else
431
                                {
432
                                        m_glyphBuffer += FToStr(strokeWidth()) + " w ";
433
                                        if (fill)
434
                                                m_glyphBuffer += "2 Tr\n";
435
                                        else
436
                                                m_glyphBuffer += "1 Tr\n";
437
                                }
438
 
439
                                transform.translate(x() + gl.xoffset + current_x, y() + gl.yoffset);
440
                                transform.scale(qMax(gc.scaleH(), 0.1), qMax(gc.scaleV(), 0.1));
441
                                m_glyphBuffer += transformToStr(transform) + " Tm\n";
442
 
443
                                if (pdfFont.method != Use_Type3 || !FillColor.isEmpty())
444
                                {
445
                                        switch (pdfFont.encoding)
446
                                        {
447
                                        case Encode_224:
448
                                        case Encode_256:
449
                                                m_glyphBuffer += Pdf::toHexString8(gid) + " Tj\n";
450
                                                break;
451
                                        case Encode_IdentityH:
452
                                        default:
453
                                                m_glyphBuffer += Pdf::toHexString16(gid) + " Tj\n";
454
                                                break;
455
                                        }
456
                                }
21107 craig 457
                        }
23072 jghali 458
                        current_x += gl.xadvance * gl.scaleH;
21107 craig 459
                }
460
        }
461
 
23073 jghali 462
        void drawLine(QPointF start, QPointF end) override
21107 craig 463
        {
464
                QTransform transform = matrix();
465
                transform.translate(x(), y());
466
                m_pathBuffer += "q\n";
467
                m_pathBuffer += transformToStr(transform) + " cm\n";
468
                m_pathBuffer += m_pdf->putColor(strokeColor().color, strokeColor().shade, false);
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
 
23073 jghali 481
        void drawRect(QRectF rect) override
21107 craig 482
        {
483
                QTransform transform = matrix();
484
//              transform.translate(x(), y());
485
                double rectX = x() + rect.x();
486
                double rectY = -y() - rect.y();
22987 jghali 487
                m_backBuffer += "q\n";
488
                m_backBuffer += transformToStr(transform) + " cm\n";
489
                m_backBuffer += "n\n";
490
                m_backBuffer += m_pdf->putColor(fillColor().color, fillColor().shade, true);
491
                m_backBuffer += m_pdf->putColor(strokeColor().color, strokeColor().shade, false);
492
                m_backBuffer += FToStr(rectX) + " " + FToStr(rectY) + " m\n";
493
                m_backBuffer += FToStr(rectX + rect.width()) + " " + FToStr(rectY) + " l\n";
494
                m_backBuffer += FToStr(rectX + rect.width()) + " " + FToStr(rectY - rect.height()) + " l\n";
495
                m_backBuffer += FToStr(rectX) + " " + FToStr(rectY - rect.height()) + " l\n";
496
                m_backBuffer += "h\nf\n";
497
                m_backBuffer += "Q\n";
21107 craig 498
        }
499
 
23073 jghali 500
        void drawObject(PageItem* embedded) override
21107 craig 501
        {
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
{
23548 jghali 581
        if (Options.Version == PDFVersion::PDF_X1a)
19115 avox 582
                return true;
23548 jghali 583
        if (Options.Version == PDFVersion::PDF_X3)
19115 avox 584
                return true;
23548 jghali 585
        if (Options.Version == PDFVersion::PDF_X4)
19115 avox 586
                return true;
587
        return false;
588
}
589
 
23548 jghali 590
bool PDFLibCore::PDF_IsPDFX(const PDFVersion& ver)
19115 avox 591
{
23548 jghali 592
        if (ver == PDFVersion::PDF_X1a)
19115 avox 593
                return true;
23548 jghali 594
        if (ver == PDFVersion::PDF_X3)
19115 avox 595
                return true;
23548 jghali 596
        if (ver == PDFVersion::PDF_X4)
19115 avox 597
                return true;
598
        return false;
599
}
600
 
10054 craig 601
bool PDFLibCore::doExport(const QString& fn, const QString& nam, int Components,
22459 jghali 602
                                          const std::vector<int> & pageNs, const QMap<int, QImage> & thumbs)
3133 fschmid 603
{
22459 jghali 604
        QImage thumb;
11060 jghali 605
        bool ret = false, error = false;
606
        int  pc_exportpages=0;
607
        int  pc_exportmasterpages=0;
4224 craig 608
        if (usingGUI)
609
                progressDialog->show();
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
                {
23129 craig 626
                        pageNsMpa.insert(doc.MasterNames[doc.DocPages.at(pageNs[a]-1)->masterPageName()], 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);
20103 avox 1015
 
20112 jghali 1016
        PDF_Begin_Catalog();
1017
        PDF_Begin_MetadataAndEncrypt();
1018
        PDF_Begin_WriteUsedFonts(AllFonts, PDF_Begin_FindUsedFonts(AllFonts, DocFonts));
1019
        PDF_Begin_Colors();
1020
        PDF_Begin_Layers();
1021
 
1022
        return true;
20103 avox 1023
}
1024
 
1025
void PDFLibCore::PDF_Begin_Catalog()
1026
{
20112 jghali 1027
        writer.startObj(writer.CatalogObj);
22386 jghali 1028
        PutDoc("<<\n/Type /Catalog\n");
1029
        PutDoc("/Pages " + Pdf::toObjRef(writer.PagesObj) + "\n");
1030
        if (!PDF_IsPDFX())
1031
        {
1032
                if (doc.useAcroFormFields())
1033
                {
1034
                        writer.AcroFormObj = writer.newObject();
1035
                        PutDoc("/AcroForm " + Pdf::toObjRef(writer.AcroFormObj) + "\n");
1036
                }
1037
                if (doc.useAnnotations())
1038
                {
1039
                        writer.DestsObj = writer.newObject();
1040
                        PutDoc("/Dests " + Pdf::toObjRef(writer.DestsObj) + "\n");
1041
                }
1042
                if (doc.JavaScripts.count() > 0)
1043
                {
1044
                        writer.NamesObj = writer.newObject();
1045
                        PutDoc("/Names " + Pdf::toObjRef(writer.NamesObj) + "\n");
1046
                }
1047
        }
1048
        if (Options.Bookmarks)
1049
        {
1050
                writer.OutlinesObj = writer.newObject();
1051
                PutDoc("/Outlines " + Pdf::toObjRef(writer.OutlinesObj) + "\n");
1052
        }
1053
        if (Options.Articles)
1054
        {
1055
                writer.ThreadsObj = writer.newObject();
1056
                PutDoc("/Threads " + Pdf::toObjRef(writer.ThreadsObj) + "\n");
1057
        }
23548 jghali 1058
        if (Options.exportsLayers())
20112 jghali 1059
        {
1060
                writer.OCPropertiesObj = writer.newObject();
20103 avox 1061
                PutDoc("/OCProperties " + Pdf::toObjRef(writer.OCPropertiesObj) + "\n");
20112 jghali 1062
        }
1063
        if (PDF_IsPDFX())
19114 avox 1064
        {
20112 jghali 1065
                writer.OutputIntentObj = writer.newObject();
1066
                PutDoc("/OutputIntents [ " +  Pdf::toObjRef(writer.OutputIntentObj) + " ]\n");
1067
        }
23548 jghali 1068
        if ((Options.Version == PDFVersion::PDF_X4))
20112 jghali 1069
        {
1070
                writer.MetaDataObj = writer.newObject();
20103 avox 1071
                PutDoc("/Metadata "+ Pdf::toObjRef(writer.MetaDataObj) + "\n");
19114 avox 1072
        }
1073
        PutDoc("/PageLayout ");
1074
        switch (Options.PageLayout)
1075
        {
1076
                case PDFOptions::SinglePage:
1077
                        PutDoc("/SinglePage\n");
1078
                        break;
1079
                case PDFOptions::OneColumn:
1080
                        PutDoc("/OneColumn\n");
1081
                        break;
1082
                case PDFOptions::TwoColumnLeft:
1083
                        PutDoc("/TwoColumnLeft\n");
1084
                        break;
1085
                case PDFOptions::TwoColumnRight:
1086
                        PutDoc("/TwoColumnRight\n");
1087
                        break;
1088
        }
1089
        if (Options.displayBookmarks)
1090
                PutDoc("/PageMode /UseOutlines\n");
1091
        else if (Options.displayFullscreen)
1092
                PutDoc("/PageMode /FullScreen\n");
1093
        else if (Options.displayThumbs)
1094
                PutDoc("/PageMode /UseThumbs\n");
23548 jghali 1095
        else if ((Options.Version == PDFVersion::PDF_15 || Options.Version == PDFVersion::PDF_16) && (Options.displayLayers))
19114 avox 1096
                        PutDoc("/PageMode /UseOC\n");
1097
        if (!Options.openAction.isEmpty())
1098
        {
22389 jghali 1099
                writer.OpenActionObj = writer.newObject();
1100
                PutDoc("/OpenAction << /S /JavaScript /JS " + Pdf::toPdf(writer.OpenActionObj) + " 0 R >>\n");
19114 avox 1101
        }
21942 craig 1102
        QDateTime dt = QDateTime::currentDateTimeUtc();
20112 jghali 1103
        Datum = Pdf::toDateString(dt);
20103 avox 1104
//    "D:";
1105
//      tmp.sprintf("%4d", d.year());
1106
//      tmp.replace(QRegExp(" "), "0");
1107
//      Datum += tmp;
1108
//      tmp.sprintf("%2d", d.month());
1109
//      tmp.replace(QRegExp(" "), "0");
1110
//      Datum += tmp;
1111
//      tmp.sprintf("%2d", d.day());
1112
//      tmp.replace(QRegExp(" "), "0");
1113
//      Datum += tmp;
1114
//      tmp = dt.time().toString();
1115
//      tmp.replace(QRegExp(":"), "");
1116
//      Datum += tmp;
1117
//      Datum += "Z";
12429 fschmid 1118
 
19114 avox 1119
        // only include XMP to PDF/X-4 at the moment, could easily be extended to include it to any PDF
23548 jghali 1120
        if (Options.Version == PDFVersion::PDF_X4)
19114 avox 1121
                generateXMP(dt.toString("yyyy-MM-ddThh:mm:ssZ"));
1122
 
1123
/* The following code makes the resulting PDF "Reader enabled" in Acrobat Reader 8
1124
   but sadly it doesn't work with newer version, because its based on a bug in AR 8
1125
        PutDoc("/Perms\n");
1126
        PutDoc("<<\n");
1127
        PutDoc("/UR3\n");
1128
        PutDoc("<<\n");
1129
        PutDoc("/M ("+Datum+")\n");
1130
        PutDoc("/Name (Scribus "+QString(VERSION)+")\n");
1131
        PutDoc("/Reference [\n");
1132
        PutDoc("<<\n");
1133
        PutDoc("/TransformParams\n");
1134
        PutDoc("<<\n");
1135
        PutDoc("/Type /TransformParams\n");
1136
        PutDoc("/V /2.2\n");
1137
        PutDoc("/Document [/FullSave]\n");
1138
        PutDoc("/Annots [/Create/Delete/Modify/Copy/Import/Export]\n");
1139
        PutDoc("/Form [/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate]\n");
1140
        PutDoc("/Signature [/Modify]\n");
1141
        PutDoc(">>\n");
1142
        PutDoc("/TransformMethod /UR3\n");
1143
        PutDoc("/Type /SigRef\n");
1144
        PutDoc(">>\n");
1145
        PutDoc("]\n");
1146
        PutDoc("/Type /Sig\n");
1147
        PutDoc(">>\n");
1148
        PutDoc(">>\n");
1149
*/
1150
 
1151
        PutDoc("/ViewerPreferences\n<<\n/PageDirection ");
1152
        PutDoc( Options.Binding == 0 ? "/L2R\n" : "/R2L\n");
1153
        if (Options.hideToolBar)
1154
                PutDoc("/HideToolbar true\n");
1155
        if (Options.hideMenuBar)
1156
                PutDoc("/HideMenubar true\n");
1157
        if (Options.fitWindow)
1158
                PutDoc("/FitWindow true\n");
20112 jghali 1159
        PutDoc(" >>\n>>");
1160
        writer.endObj(writer.CatalogObj);
20103 avox 1161
}
1162
 
1163
void PDFLibCore::PDF_Begin_MetadataAndEncrypt()
1164
{
19114 avox 1165
        QString IDg(Datum);
1166
        IDg += Options.fileName;
1167
        IDg += "Scribus "+QString(VERSION);
1168
        IDg += "Scribus PDF Library "+QString(VERSION);
1169
        IDg += doc.documentInfo().title();
1170
        IDg += doc.documentInfo().author();
1171
        IDg += "/False";
20112 jghali 1172
        writer.setFileId(Pdf::toPdfDocEncoding(IDg));
19114 avox 1173
        if (Options.Encrypt)
1174
        {
23548 jghali 1175
                writer.setEncryption(Options.Version.supports128BitsEncryption(), Pdf::toPdfDocEncoding(Options.PassOwner), Pdf::toPdfDocEncoding(Options.PassUser), Options.Permissions);
19114 avox 1176
        }
20103 avox 1177
        writer.startObj(writer.InfoObj);
20112 jghali 1178
        PutDoc("<<\n/Creator " + EncString(QByteArray("Scribus ") + VERSION, writer.InfoObj) + "\n");
20103 avox 1179
        PutDoc("/Producer " + EncString(QByteArray("Scribus PDF Library ") + VERSION, writer.InfoObj) + "\n");
19114 avox 1180
        QString docTitle = doc.documentInfo().title();
19115 avox 1181
        if ((PDF_IsPDFX()) && (docTitle.isEmpty()))
22832 craig 1182
                PutDoc("/Title " + EncStringUTF16(doc.documentFileName(), writer.InfoObj) + "\n");
19114 avox 1183
        else
20103 avox 1184
                PutDoc("/Title " + EncStringUTF16(doc.documentInfo().title(), writer.InfoObj) + "\n");
1185
        PutDoc("/Author " + EncStringUTF16(doc.documentInfo().author(), writer.InfoObj) + "\n");
1186
        PutDoc("/Subject " + EncStringUTF16(doc.documentInfo().subject(), writer.InfoObj) + "\n");
1187
        PutDoc("/Keywords " + EncStringUTF16(doc.documentInfo().keywords(), writer.InfoObj) + "\n");
1188
        PutDoc("/CreationDate " + EncString(Datum, writer.InfoObj) + "\n");
1189
        PutDoc("/ModDate " + EncString(Datum, writer.InfoObj) + "\n");
23548 jghali 1190
        if (Options.Version == PDFVersion::PDF_X1a)
19114 avox 1191
        {
1192
                PutDoc("/GTS_PDFXVersion (PDF/X-1:2001)\n");
1193
                PutDoc("/GTS_PDFXConformance (PDF/X-1a:2001)\n");
1194
        }
23548 jghali 1195
        if (Options.Version == PDFVersion::PDF_X3)
19115 avox 1196
                PutDoc("/GTS_PDFXVersion (PDF/X-3:2002)\n");
23548 jghali 1197
        if (Options.Version == PDFVersion::PDF_X4)
19114 avox 1198
                PutDoc("/GTS_PDFXVersion (PDF/X-4)\n");
20112 jghali 1199
        PutDoc("/Trapped /False\n>>");
1200
        writer.endObj(writer.InfoObj);
20103 avox 1201
}
1202
 
1203
QMap<QString, QMap<uint, FPointArray> >
1204
PDFLibCore::PDF_Begin_FindUsedFonts(SCFonts &AllFonts, const QMap<QString, QMap<uint, FPointArray> >& DocFonts)
1205
{
5387 avox 1206
        QMap<QString, QMap<uint, FPointArray> > ReallyUsed;
3133 fschmid 1207
        ReallyUsed.clear();
1208
        PageItem* pgit;
20103 avox 1209
        QMap<int, QByteArray> ind2PDFabr;
1210
        const QByteArray tmpf[] = {"/Courier", "/Courier-Bold", "/Courier-Oblique", "/Courier-BoldOblique",
4017 fschmid 1211
                                                                                                "/Helvetica", "/Helvetica-Bold", "/Helvetica-Oblique", "/Helvetica-BoldOblique",
4546 subik 1212
                                                                                                "/Times-Roman", "/Times-Bold", "/Times-Italic", "/Times-BoldItalic",
4017 fschmid 1213
                                                                                                "/ZapfDingbats", "/Symbol"};
1214
        size_t ar = sizeof(tmpf) / sizeof(*tmpf);
1215
        for (uint ax = 0; ax < ar; ++ax)
1216
                ind2PDFabr[ax] = tmpf[ax];
21709 jghali 1217
 
1218
        QList<PageItem*> allItems = doc.FrameItems.values();
1219
        while (allItems.count() > 0)
3133 fschmid 1220
        {
21709 jghali 1221
                pgit = allItems.takeFirst();
1222
                if (pgit->isGroup() || pgit->isTable())
3133 fschmid 1223
                {
22224 jghali 1224
                        allItems = pgit->getChildren() + allItems;
21709 jghali 1225
                        continue;
1226
                }
1227
                if ((pgit->itemType() == PageItem::TextFrame) || (pgit->itemType() == PageItem::PathText))
1228
                {
1229
                        if (pgit->isAnnotation())
17312 fschmid 1230
                        {
21709 jghali 1231
                                int annotType  = pgit->annotation().Type();
1232
                                bool mustEmbed = ((annotType >= Annotation::Button) && (annotType <= Annotation::Listbox) && (annotType != Annotation::Checkbox));
1233
                                if ((pgit->annotation().Type() == Annotation::Checkbox) || (pgit->annotation().Type() == Annotation::RadioButton))
1234
                                        StdFonts.insert("/ZapfDingbats", "");
1235
                                if (pgit->itemText.length() > 0 || mustEmbed)
17312 fschmid 1236
                                {
23548 jghali 1237
                                        if (Options.Version < PDFVersion::PDF_14)
21709 jghali 1238
                                                StdFonts.insert(ind2PDFabr[pgit->annotation().Font()], "");
21744 jghali 1239
                                        QString replacementName = pgit->itemText.defaultStyle().charStyle().font().replacementName();
1240
                                        ReallyUsed.insert(replacementName, DocFonts[replacementName]);
17312 fschmid 1241
                                }
1242
                        }
21709 jghali 1243
                        uint start = pgit->isTextFrame() ? (uint) pgit->firstInFrame() : 0;
1244
                        uint stop  = pgit->isTextFrame() ? (uint) pgit->lastInFrame() + 1 : (uint) pgit->itemText.length();
1245
                        for (uint e = start; e < stop; ++e)
8092 fschmid 1246
                        {
21709 jghali 1247
                                QString replacementName = pgit->itemText.charStyle(e).font().replacementName();
1248
                                ReallyUsed.insert(replacementName, DocFonts[replacementName]);
8092 fschmid 1249
                        }
3133 fschmid 1250
                }
1251
        }
21709 jghali 1252
 
1253
        allItems = doc.MasterItems;
1254
        while (allItems.count() > 0)
3133 fschmid 1255
        {
21709 jghali 1256
                pgit = allItems.takeFirst();
1257
                if (pgit->isGroup() || pgit->isTable())
3133 fschmid 1258
                {
22224 jghali 1259
                        allItems = pgit->getChildren() + allItems;
21709 jghali 1260
                        continue;
1261
                }
1262
                if ((pgit->itemType() == PageItem::TextFrame) || (pgit->itemType() == PageItem::PathText))
1263
                {
1264
                        if (pgit->isAnnotation())
17312 fschmid 1265
                        {
21709 jghali 1266
                                int annotType  = pgit->annotation().Type();
1267
                                bool mustEmbed = ((annotType >= Annotation::Button) && (annotType <= Annotation::Listbox) && (annotType != Annotation::Checkbox));
1268
                                if ((pgit->annotation().Type() == Annotation::Checkbox) || (pgit->annotation().Type() == Annotation::RadioButton))
1269
                                        StdFonts.insert("/ZapfDingbats", "");
1270
                                if (pgit->itemText.length() > 0 || mustEmbed)
17312 fschmid 1271
                                {
23548 jghali 1272
                                        if (Options.Version < PDFVersion::PDF_14)
21709 jghali 1273
                                                StdFonts.insert(ind2PDFabr[pgit->annotation().Font()], "");
1274
                                        QString replacementName = pgit->itemText.defaultStyle().charStyle().font().replacementName();
1275
                                        ReallyUsed.insert(replacementName, DocFonts[replacementName]);
17312 fschmid 1276
                                }
1277
                        }
21709 jghali 1278
                        uint start = pgit->isTextFrame() ? (uint) pgit->firstInFrame() : 0;
1279
                        uint stop  = pgit->isTextFrame() ? (uint) pgit->lastInFrame() + 1 : (uint) pgit->itemText.length();
1280
                        for (uint e = start; e < stop; ++e)
8092 fschmid 1281
                        {
21709 jghali 1282
                                QString replacementName = pgit->itemText.charStyle(e).font().replacementName();
1283
                                ReallyUsed.insert(replacementName, DocFonts[replacementName]);
1284
                        }
1285
                }
1286
        }
1287
 
1288
        allItems = *(doc.Items);
1289
        while (allItems.count() > 0)
1290
        {
1291
                pgit = allItems.takeFirst();
1292
                if (pgit->isGroup() || pgit->isTable())
1293
                {
22224 jghali 1294
                        allItems = pgit->getChildren() + allItems;
21709 jghali 1295
                        continue;
1296
                }
1297
                if ((pgit->itemType() == PageItem::TextFrame) || (pgit->itemType() == PageItem::PathText))
1298
                {
1299
                        if (pgit->isAnnotation())
1300
                        {
1301
                                int annotType  = pgit->annotation().Type();
1302
                                bool mustEmbed = ((annotType >= Annotation::Button) && (annotType <= Annotation::Listbox) && (annotType != Annotation::Checkbox));
1303
                                if ((pgit->annotation().Type() == Annotation::Checkbox) || (pgit->annotation().Type() == Annotation::RadioButton))
1304
                                        StdFonts.insert("/ZapfDingbats", "");
1305
                                if (pgit->itemText.length() > 0 || mustEmbed)
12763 fschmid 1306
                                {
23548 jghali 1307
                                        if (Options.Version < PDFVersion::PDF_14)
21709 jghali 1308
                                                StdFonts.insert(ind2PDFabr[pgit->annotation().Font()], "");
1309
                                        QString replacementName = pgit->itemText.defaultStyle().charStyle().font().replacementName();
21707 jghali 1310
                                        ReallyUsed.insert(replacementName, DocFonts[replacementName]);
16953 fschmid 1311
                                }
8092 fschmid 1312
                        }
21709 jghali 1313
                        uint start = pgit->isTextFrame() ? (uint) pgit->firstInFrame() : 0;
1314
                        uint stop  = pgit->isTextFrame() ? (uint) pgit->lastInFrame() + 1 : (uint) pgit->itemText.length();
1315
                        for (uint e = start; e < stop; ++e)
1316
                        {
1317
                                QString replacementName = pgit->itemText.charStyle(e).font().replacementName();
1318
                                ReallyUsed.insert(replacementName, DocFonts[replacementName]);
1319
                        }
3133 fschmid 1320
                }
1321
        }
21709 jghali 1322
/*      if (Options.docInfoMarks)
3133 fschmid 1323
        {
21709 jghali 1324
                StdFonts.insert("/Helvetica", "");
1325
        } */
1326
        QStringList patterns = doc.getPatternDependencyList(doc.getUsedPatterns());
1327
        for (int c = 0; c < patterns.count(); ++c)
1328
        {
1329
                ScPattern pa = doc.docPatterns[patterns[c]];
1330
                allItems = pa.items;
1331
                while (allItems.count() > 0)
3133 fschmid 1332
                {
21709 jghali 1333
                        pgit = allItems.takeFirst();
1334
                        if (pgit->isGroup() || pgit->isTable())
17312 fschmid 1335
                        {
22224 jghali 1336
                                allItems = pgit->getChildren() + allItems;
21709 jghali 1337
                                continue;
17312 fschmid 1338
                        }
16953 fschmid 1339
                        if ((pgit->itemType() == PageItem::TextFrame) || (pgit->itemType() == PageItem::PathText))
8092 fschmid 1340
                        {
16953 fschmid 1341
                                if (pgit->isAnnotation())
12763 fschmid 1342
                                {
17952 fschmid 1343
                                        if ((pgit->annotation().Type() == Annotation::Checkbox) || (pgit->annotation().Type() == Annotation::RadioButton))
16953 fschmid 1344
                                                StdFonts.insert("/ZapfDingbats", "");
21709 jghali 1345
                                        if (pgit->itemText.length() > 0)
16953 fschmid 1346
                                        {
23548 jghali 1347
                                                if (Options.Version < PDFVersion::PDF_14)
16953 fschmid 1348
                                                        StdFonts.insert(ind2PDFabr[pgit->annotation().Font()], "");
21707 jghali 1349
                                                QString replacementName = pgit->itemText.defaultStyle().charStyle().font().replacementName();
1350
                                                ReallyUsed.insert(replacementName, DocFonts[replacementName]);
16953 fschmid 1351
                                        }
12763 fschmid 1352
                                }
17751 jghali 1353
                                uint start = pgit->isTextFrame() ? (uint) pgit->firstInFrame() : 0;
1354
                                uint stop  = pgit->isTextFrame() ? (uint) pgit->lastInFrame() + 1 : (uint) pgit->itemText.length();
1355
                                for (uint e = start; e < stop; ++e)
16953 fschmid 1356
                                {
21707 jghali 1357
                                        QString replacementName = pgit->itemText.charStyle(e).font().replacementName();
1358
                                        ReallyUsed.insert(replacementName, DocFonts[replacementName]);
16953 fschmid 1359
                                }
8092 fschmid 1360
                        }
3133 fschmid 1361
                }
1362
        }
20112 jghali 1363
        return ReallyUsed;
20103 avox 1364
}
17628 jghali 1365
 
20103 avox 1366
static QByteArray sanitizeFontName(QString fn)
1367
{
20112 jghali 1368
        return Pdf::toPdfDocEncoding(fn.replace( QRegExp("[\\s\\/\\{\\[\\]\\}\\<\\>\\(\\)\\%]"), "_" ));
20103 avox 1369
}
17628 jghali 1370
 
22507 jghali 1371
static QList<Pdf::Resource> asColorSpace(const QList<PdfICCD>& iccCSlist)
20103 avox 1372
{
20112 jghali 1373
        QList<Pdf::Resource> result;
22507 jghali 1374
        for (const Pdf::Resource& r : iccCSlist)
20112 jghali 1375
                result.append(r);
1376
        return result;
20103 avox 1377
}
1378
 
22507 jghali 1379
static QList<Pdf::Resource> asColorSpace(const QList<PdfSpotC>& spotMapValues)
20103 avox 1380
{
20112 jghali 1381
        QList<Pdf::Resource> result;
22507 jghali 1382
        for (const Pdf::Resource& r : spotMapValues)
20112 jghali 1383
                result.append(r);
1384
        return result;
20103 avox 1385
}
1386
 
1387
void PDFLibCore::PDF_WriteStandardFonts()
1388
{
20112 jghali 1389
        int a = 0;
1390
        QMap<QString, QString>::Iterator itStd;
1391
        for (itStd = StdFonts.begin(); itStd != StdFonts.end(); ++itStd)
1392
        {
1393
                PdfId fontObject = writer.newObject();
1394
                writer.startObj(fontObject);
1395
                PutDoc("<<\n/Type /Font\n/Subtype /Type1\n");
1396
                PutDoc("/Name /FoStd"+Pdf::toPdf(a)+"\n");
1397
                PutDoc("/BaseFont "+Pdf::toPdfDocEncoding(itStd.key())+"\n");
1398
                if (itStd.key() != "/ZapfDingbats")
1399
                {
1400
                        PutDoc("/Encoding << \n");
1401
                        PutDoc("/Differences [ \n");
1402
                        PutDoc("24 /breve /caron /circumflex /dotaccent /hungarumlaut /ogonek /ring /tilde\n");
1403
                        PutDoc("39 /quotesingle 96 /grave 128 /bullet /dagger /daggerdbl /ellipsis /emdash /endash /florin /fraction /guilsinglleft /guilsinglright\n");
1404
                        PutDoc("/minus /perthousand /quotedblbase /quotedblleft /quotedblright /quoteleft /quoteright /quotesinglbase /trademark /fi /fl /Lslash /OE /Scaron\n");
21744 jghali 1405
                        PutDoc("/Ydieresis /Zcaron /dotlessi /lslash /oe /scaron /zcaron 160 /Euro 164 /currency 166 /brokenbar 168 /dieresis /copyright /ordfeminine 172 /logicalnot\n");
20112 jghali 1406
                        PutDoc("/.notdef /registered /macron /degree /plusminus /twosuperior /threesuperior /acute /mu 183 /periodcentered /cedilla /onesuperior /ordmasculine\n");
1407
                        PutDoc("188 /onequarter /onehalf /threequarters 192 /Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla /Egrave /Eacute /Ecircumflex\n");
1408
                        PutDoc("/Edieresis /Igrave /Iacute /Icircumflex /Idieresis /Eth /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply /Oslash\n");
1409
                        PutDoc("/Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn /germandbls /agrave /aacute /acircumflex /atilde /adieresis /aring /ae /ccedilla\n");
1410
                        PutDoc("/egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex /idieresis /eth /ntilde /ograve /oacute /ocircumflex /otilde /odieresis\n");
1411
                        PutDoc("/divide /oslash /ugrave /uacute /ucircumflex /udieresis /yacute /thorn /ydieresis\n");
1412
                        PutDoc("] >>\n");
1413
                }
1414
                PutDoc(">>");
1415
                writer.endObj(fontObject);
1416
                pageData.FObjects["FoStd"+Pdf::toPdf(a)] = fontObject;
1417
                itStd.value() = "FoStd"+Pdf::toPdf(a);
1418
                a++;
1419
        }
20103 avox 1420
}
1421
 
1422
 
1423
PdfFont PDFLibCore::PDF_WriteType3Font(const QByteArray& name, ScFace& face, const QMap<uint,FPointArray>& RealGlyphs)
1424
{
20112 jghali 1425
        PdfFont result;
1426
        result.name = Pdf::toName(name);
1427
        result.method = Use_Type3;
1428
        result.encoding = Encode_256;
20103 avox 1429
 
22008 jghali 1430
        // TrueType font rasterizers use non-zero winding file rule,
1431
        // vs even-odd for current Postscript font rasterizers
1432
        // Note: OTF CFF2 will also use non-zero winding rule, so
1433
        // change code below when adding CCF2 support to Scribus
1434
        // ref: https://www.microsoft.com/typography/OTSpec/cff2.htm
1435
        bool useNonZeroRule = (face.type() == ScFace::TTF);
1436
 
20112 jghali 1437
        uint SubFonts = 0;
1438
        int glyphCount = 0;
1439
        double minx =  std::numeric_limits<double>::max();
1440
        double miny =  std::numeric_limits<double>::max();
1441
        double maxx = -std::numeric_limits<double>::max();
1442
        double maxy = -std::numeric_limits<double>::max();
22142 jghali 1443
 
20112 jghali 1444
        QList<uint> glyphWidths;
1445
        QList<QByteArray> charProcs;
22142 jghali 1446
        QStringList toUnicodeMaps;
1447
        QList<int> toUnicodeMapsCount;
22719 jghali 1448
        QString toUnicodeMap;
22142 jghali 1449
        int toUnicodeMapCounter = 0;
20112 jghali 1450
        QByteArray encoding = "<< /Type /Encoding\n/Differences [ 0\n";
1451
        QByteArray fon;
1452
        QMap<uint, uint> glyphMapping;
22142 jghali 1453
 
20112 jghali 1454
        ScFace::FaceEncoding gl;
1455
        face.glyphNames(gl);
22142 jghali 1456
 
20112 jghali 1457
        QMap<uint,FPointArray>::ConstIterator ig;
1458
        for (ig = RealGlyphs.cbegin(); ig != RealGlyphs.cend(); ++ig)
1459
        {
1460
                FPoint np, np1, np2;
1461
                bool nPath = true;
1462
                fon.resize(0);
1463
                if (ig.value().size() > 3)
1464
                {
1465
                        FPointArray gly = ig.value();
1466
                        QTransform mat;
1467
                        mat.scale(100.0, -100.0);
1468
                        gly.map(mat);
1469
                        gly.translate(0, 1000);
1470
                        for (int poi = 0; poi < gly.size()-3; poi += 4)
1471
                        {
1472
                                if (gly.isMarker(poi))
1473
                                {
1474
                                        fon += "h\n";
1475
                                        nPath = true;
1476
                                        continue;
1477
                                }
1478
                                if (nPath)
1479
                                {
1480
                                        np = gly.point(poi);
1481
                                        fon += FToStr(np.x())+" "+FToStr(np.y())+" m\n";
1482
                                        nPath = false;
1483
                                }
1484
                                np = gly.point(poi+1);
1485
                                np1 = gly.point(poi+3);
1486
                                np2 = gly.point(poi+2);
1487
                                fon += FToStr(np.x()) + " " + FToStr(np.y()) + " " + FToStr(np1.x()) + " " + FToStr(np1.y()) + " " + FToStr(np2.x()) + " " + FToStr(np2.y()) + " c\n";
1488
                        }
22008 jghali 1489
                        fon += useNonZeroRule? "h f\n" : "h f*\n";
20112 jghali 1490
                        np = getMinClipF(&gly);
1491
                        np1 = getMaxClipF(&gly);
1492
                }
1493
                else
1494
                {
20684 jghali 1495
                        // 13654: make evince emit warning about "Syntax Error: No current point in closepath"
1496
                        // PDF specification is not quite clear about that, but a single "h" operator might not
1497
                        // be a valid way to make an empty path
1498
                        // fon = "h";
20112 jghali 1499
                        np = FPoint(0, 0);
1500
                        np1 = FPoint(0, 0);
1501
                }
1502
                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");
1503
                minx = qMin(minx, np.x());
1504
                miny = qMin(miny, np.y());
1505
                maxx = qMax(maxx, np1.x());
1506
                maxy = qMax(maxy, np1.y());
1507
                glyphWidths.append(qRound(np1.x()));
22142 jghali 1508
 
20112 jghali 1509
                PdfId charProcObject = writer.newObject();
1510
                writer.startObj(charProcObject);
1511
                if (Options.Compress)
1512
                        fon = CompressArray(fon);
22717 jghali 1513
                PutDoc("<< /Length " + Pdf::toPdf(fon.length() + 1));
20112 jghali 1514
                if (Options.Compress)
1515
                        PutDoc("\n/Filter /FlateDecode");
22717 jghali 1516
                PutDoc("\n>>\nstream\n" + EncStream(fon, charProcObject) + "\nendstream");
20112 jghali 1517
                writer.endObj(charProcObject);
22142 jghali 1518
 
22717 jghali 1519
                // #15449 : in some cases we cannot retrieve glyph names for all glyphs we need
1520
                // using ScFace's glyphNames(), so generate custom glyph names using glyph index.
1521
                // With Type 3 fonts we have more flexibility than with other fonts.
1522
                QString glGlyphName, glToUnicode;
1523
                auto  glyphIt = gl.constFind(ig.key());
1524
                if ((glyphIt != gl.constEnd()) && (glyphIt->charcode > 0))
1525
                        glToUnicode = QString::asprintf("%04X", glyphIt->charcode);
1526
                else
1527
                        glToUnicode = QString("0000");
1528
                glGlyphName = "gly" + QString::asprintf("%04X", ig.key());
1529
 
1530
                charProcs.append(Pdf::toName(glGlyphName) + " " + Pdf::toPdf(charProcObject) + " 0 R\n");
1531
                encoding += Pdf::toName(glGlyphName) + " ";
1532
                glyphMapping.insert(ig.key(), glyphCount + SubFonts * 256);
1533
 
1534
                QString tmp = QString::asprintf("%02X", glyphCount);
1535
                toUnicodeMap += "<" + Pdf::toAscii(tmp) + "> <" + Pdf::toAscii(glToUnicode) + ">\n";
22142 jghali 1536
                toUnicodeMapCounter++;
1537
                if (toUnicodeMapCounter == 100)
1538
                {
1539
                        toUnicodeMaps.append(toUnicodeMap);
1540
                        toUnicodeMapsCount.append(toUnicodeMapCounter);
22717 jghali 1541
                        toUnicodeMap.clear();
22142 jghali 1542
                        toUnicodeMapCounter = 0;
1543
                }
1544
 
20112 jghali 1545
                glyphCount++;
1546
                int glyphsLeft = RealGlyphs.count() - SubFonts * 256;
1547
                if ((glyphCount > 255) || (glyphCount == glyphsLeft))
1548
                {
1549
                        PdfId fontWidths = writer.newObject();
1550
                        writer.startObj(fontWidths);
1551
                        PutDoc("[ ");
1552
                        for (int ww = 0; ww < glyphWidths.count(); ++ww)
1553
                        {
1554
                                PutDoc(Pdf::toPdf(glyphWidths[ww])+" ");
1555
                        }
1556
                        PutDoc("]");
1557
                        writer.endObj(fontWidths);
1558
                        PdfId fontCharProcs = writer.newObject();
1559
                        writer.startObj(fontCharProcs);
1560
                        PutDoc("<<\n");
1561
                        for (int ww = 0; ww < charProcs.count(); ++ww)
1562
                        {
1563
                                PutDoc(charProcs[ww]);
1564
                        }
1565
                        PutDoc(">>");
1566
                        writer.endObj(fontCharProcs);
1567
                        PdfId fontEncoding = writer.newObject();
1568
                        writer.startObj(fontEncoding);
1569
                        PutDoc(encoding);
1570
                        PutDoc("]\n");
1571
                        PutDoc(">>");
1572
                        writer.endObj(fontEncoding);
22142 jghali 1573
 
1574
                        if (toUnicodeMapCounter != 0)
1575
                        {
1576
                                toUnicodeMaps.append(toUnicodeMap);
1577
                                toUnicodeMapsCount.append(toUnicodeMapCounter);
1578
                        }
1579
 
22719 jghali 1580
                        QByteArray toUnicodeMapStream;
22142 jghali 1581
                        toUnicodeMapStream += "/CIDInit /ProcSet findresource begin\n";
1582
                        toUnicodeMapStream += "12 dict begin\n";
1583
                        toUnicodeMapStream += "begincmap\n";
1584
                        toUnicodeMapStream += "/CIDSystemInfo <<\n";
1585
                        toUnicodeMapStream += "/Registry (Adobe)\n";
1586
                        toUnicodeMapStream += "/Ordering (UCS)\n";
1587
                        toUnicodeMapStream += "/Supplement 0\n";
1588
                        toUnicodeMapStream += ">> def\n";
1589
                        toUnicodeMapStream += "/CMapName /Adobe-Identity-UCS def\n";
1590
                        toUnicodeMapStream += "/CMapType 2 def\n";
1591
                        toUnicodeMapStream += "1 begincodespacerange\n";
1592
                        toUnicodeMapStream += "<0000> <FFFF>\n";
1593
                        toUnicodeMapStream += "endcodespacerange\n";
1594
                        for (int uniC = 0; uniC < toUnicodeMaps.count(); uniC++)
1595
                        {
1596
                                toUnicodeMapStream += Pdf::toPdf(toUnicodeMapsCount[uniC]);
1597
                                toUnicodeMapStream += " beginbfchar\n";
1598
                                toUnicodeMapStream += toUnicodeMaps[uniC];
1599
                                toUnicodeMapStream += "endbfchar\n";
1600
                        }
1601
                        toUnicodeMapStream += "endcmap\n";
1602
                        toUnicodeMapStream += "CMapName currentdict /CMap defineresource pop\n";
1603
                        toUnicodeMapStream += "end\n";
1604
                        toUnicodeMapStream += "end\n";
1605
                        PdfId fontToUnicode = WritePDFStream(toUnicodeMapStream);
1606
 
20112 jghali 1607
                        PdfId font3Object = writer.newObject();
1608
                        writer.startObj(font3Object);
1609
                        PutDoc("<<\n/Type /Font\n/Subtype /Type3\n");
1610
                        PutDoc("/Name /"+name+"S"+Pdf::toPdf(SubFonts)+"\n");
1611
                        PutDoc("/FirstChar 0\n");
1612
                        PutDoc("/LastChar "+Pdf::toPdf(glyphCount-1)+"\n");
1613
                        PutDoc("/Widths "+Pdf::toPdf(fontWidths)+" 0 R\n");
1614
                        PutDoc("/CharProcs "+Pdf::toPdf(fontCharProcs)+" 0 R\n");
1615
                        PutDoc("/FontBBox ["+Pdf::toPdf(qRound(minx))+" "+Pdf::toPdf(qRound(miny))+" "+Pdf::toPdf(qRound(maxx))+ " "+Pdf::toPdf(qRound(maxy))+"]\n");
1616
                        PutDoc("/FontMatrix [0.001 0 0 0.001 0 0]\n");
1617
                        PutDoc("/Encoding "+Pdf::toPdf(fontEncoding)+" 0 R\n");
22142 jghali 1618
                        PutDoc("/ToUnicode " + Pdf::toPdf(fontToUnicode) + " 0 R\n");
20112 jghali 1619
                        PutDoc(">>");
1620
                        writer.endObj(font3Object);
1621
                        pageData.FObjects[name+"S"+Pdf::toPdf(SubFonts)] = font3Object;
1622
                        charProcs.clear();
1623
                        glyphWidths.clear();
22142 jghali 1624
                        toUnicodeMaps.clear();
1625
                        toUnicodeMapsCount.clear();
1626
                        toUnicodeMap.clear();
1627
                        toUnicodeMapCounter = 0;
20112 jghali 1628
                        glyphCount = 0;
1629
                        ++SubFonts;
1630
                        minx =  std::numeric_limits<double>::max();
1631
                        miny =  std::numeric_limits<double>::max();
1632
                        maxx = -std::numeric_limits<double>::max();
1633
                        maxy = -std::numeric_limits<double>::max();
1634
                        encoding = "<< /Type /Encoding\n/Differences [ 0\n";
1635
                }
1636
        }
1637
        result.glyphmap = glyphMapping;
1638
        return result;
20103 avox 1639
}
1640
 
1641
 
1642
PdfFont PDFLibCore::PDF_WriteGlyphsAsXForms(const QByteArray& fontName, ScFace& face, const QMap<uint,FPointArray>& RealGlyphs)
1643
{
20112 jghali 1644
        PdfFont result;
1645
        result.name = Pdf::toName(fontName);
1646
        result.method = Use_XForm;
1647
        result.encoding = Encode_224;
20103 avox 1648
 
22008 jghali 1649
        // TrueType font rasterizers use non-zero winding file rule,
1650
        // vs even-odd for current Postscript font rasterizers
1651
        // Note: OTF CFF2 will also use non-zero winding rule, so
1652
        // change code below when adding CCF2 support to Scribus
1653
        // ref: https://www.microsoft.com/typography/OTSpec/cff2.htm
1654
        bool useNonZeroRule = (face.type() == ScFace::TTF);
1655
 
20112 jghali 1656
        QByteArray fon;
1657
        QMap<uint,FPointArray>::ConstIterator ig;
1658
        for (ig = RealGlyphs.cbegin(); ig != RealGlyphs.cend(); ++ig)
1659
        {
1660
                FPoint np, np1, np2;
1661
                bool nPath = true;
1662
                fon.resize(0);
1663
                if (ig.value().size() > 3)
1664
                {
1665
                        FPointArray gly = ig.value();
1666
                        QTransform mat;
1667
                        mat.scale(0.1, 0.1);
1668
                        gly.map(mat);
1669
                        for (int poi = 0; poi < gly.size()-3; poi += 4)
1670
                        {
1671
                                if (gly.isMarker(poi))
1672
                                {
1673
                                        fon += "h\n";
1674
                                        nPath = true;
1675
                                        continue;
1676
                                }
1677
                                if (nPath)
1678
                                {
1679
                                        np = gly.point(poi);
1680
                                        fon += FToStr(np.x())+" "+FToStr(-np.y())+" m\n";
1681
                                        nPath = false;
1682
                                }
1683
                                np = gly.point(poi+1);
1684
                                np1 = gly.point(poi+3);
1685
                                np2 = gly.point(poi+2);
1686
                                fon += FToStr(np.x()) + " " + FToStr(-np.y()) + " " +
1687
                                FToStr(np1.x()) + " " + FToStr(-np1.y()) + " " +
1688
                                FToStr(np2.x()) + " " + FToStr(-np2.y()) + " c\n";
1689
                        }
22008 jghali 1690
                        fon += useNonZeroRule? "h f\n" : "h f*\n";
20112 jghali 1691
                        np = getMinClipF(&gly);
1692
                        np1 = getMaxClipF(&gly);
1693
                }
1694
                else
1695
                {
20684 jghali 1696
                        // 13654: make evince emit warning about "Syntax Error: No current point in closepath"
1697
                        // PDF specification is not quite clear about that, but a single "h" operator might not
1698
                        // be a valid way to make an empty path
1699
                        fon = " " /*"h"*/;
20112 jghali 1700
                        np = FPoint(0, 0);
1701
                        np1 = FPoint(0, 0);
1702
                }
1703
                PdfId fontGlyphXForm = writer.newObject();
1704
                writer.startObj(fontGlyphXForm);
1705
                PutDoc("<<\n/Type /XObject\n/Subtype /Form\n/FormType 1\n");
1706
                PutDoc("/BBox [ "+FToStr(np.x())+" "+FToStr(-np.y())+" "+FToStr(np1.x())+ " "+FToStr(-np1.y())+" ]\n");
1707
                PutDoc("/Resources << /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]\n");
1708
                PutDoc(">>\n");
1709
                if (Options.Compress)
1710
                        fon = CompressArray(fon);
1711
                PutDoc("/Length "+Pdf::toPdf(fon.length()+1));
1712
                if (Options.Compress)
1713
                        PutDoc("\n/Filter /FlateDecode");
1714
                PutDoc(" >>\nstream\n"+EncStream(fon, fontGlyphXForm)+"\nendstream");
1715
                writer.endObj(fontGlyphXForm);
21894 jghali 1716
                pageData.XObjects[fontName + "_gl" + Pdf::toPdf(ig.key())] = fontGlyphXForm;
20112 jghali 1717
        }
1718
        return result;
20103 avox 1719
}
1720
 
1721
PdfId PDFLibCore::PDF_EmbedFontObject(const QByteArray& font, const QByteArray& subtype)
1722
{
20112 jghali 1723
        PdfId embeddedFontObject = writer.newObject();
1724
        writer.startObj(embeddedFontObject);
1725
        int len = font.length();
1726
        QByteArray ttf = (Options.Compress? CompressArray(font) : font);
1727
        //qDebug() << QString("sfnt data: size=%1 compressed=%2").arg(len).arg(bb.length());
1728
        PutDoc("<<\n/Length " + Pdf::toPdf(ttf.length() + 1) + "\n");
1729
        PutDoc("/Length1 " + Pdf::toPdf(len) + "\n");
1730
        if (subtype.size() > 0)
1731
                PutDoc("/Subtype " + subtype);
1732
        if (Options.Compress)
1733
                PutDoc("/Filter /FlateDecode\n");
1734
        PutDoc(">>\nstream\n");
1735
        EncodeArrayToStream(ttf, embeddedFontObject);
1736
        PutDoc("\nendstream");
1737
        writer.endObj(embeddedFontObject);
1738
        return embeddedFontObject;
20103 avox 1739
}
1740
 
22600 craig 1741
QByteArray PDFLibCore::PDF_GenerateSubsetTag(const QByteArray& fontName, const QList<uint>& usedGlyphs)
21775 jghali 1742
{
1743
        uint hash, mod;
1744
        QVector<uint> glyphVec = usedGlyphs.toVector();
1745
 
1746
        hash = qHashBits(fontName.constData(), fontName.size(), 0);
1747
        hash = qHashBits(glyphVec.constData(), glyphVec.size() * sizeof(uint), hash);
1748
 
1749
        QByteArray subsetTag(6, (char) 0);
1750
        for (int i = 0; i < 6; ++i)
1751
        {
1752
                mod = hash % 26;
1753
                subsetTag[i] = 'A' + mod;
1754
                hash = (hash - mod) / 26;
1755
        }
1756
 
1757
        return subsetTag;
1758
}
1759
 
20103 avox 1760
PdfId PDFLibCore::PDF_WriteFontDescriptor(const QByteArray& fontName, ScFace& face, ScFace::FontFormat fformat, PdfId embeddedFontObject)
1761
{
20112 jghali 1762
        PdfId fontDescriptor = writer.newObject();
1763
        writer.startObj(fontDescriptor);
1764
        // TODO: think about QByteArray ScFace::getFontDescriptor() -- AV
1765
        PutDoc("<<\n/Type /FontDescriptor\n");
1766
        PutDoc("/FontName "  + Pdf::toName(fontName) + "\n");
1767
        PutDoc("/FontBBox [ " + Pdf::toAscii(face.pdfFontBBoxAsString()) + " ]\n");
1768
        PutDoc("/Flags ");
1769
        //FIXME: isItalic() should be queried from ScFace, not from Qt -- AV
1770
        //QFontInfo fo = QFontInfo(it.data());
1771
        int pfl = 0;
1772
        if (face.isFixedPitch())
1773
                pfl = pfl ^ 1;
1774
        //if (fo.italic())
1775
        if (face.italicAngleAsString() != "0")
1776
                pfl = pfl ^ 64;
1777
        //                      pfl = pfl ^ 4;
1778
        pfl = pfl ^ 32;
1779
        PutDoc(Pdf::toPdf(pfl)+"\n");
1780
        PutDoc("/Ascent "      + Pdf::toAscii(face.pdfAscentAsString())+"\n");
1781
        PutDoc("/Descent "     + Pdf::toAscii(face.pdfDescentAsString())+"\n");
1782
        PutDoc("/CapHeight "   + Pdf::toAscii(face.pdfCapHeightAsString())+"\n");
1783
        PutDoc("/ItalicAngle " + Pdf::toAscii(face.italicAngleAsString())+"\n");
1784
        PutDoc("/StemV 1\n");
1785
        if (embeddedFontObject != 0)
1786
        {
1787
                if (fformat == ScFace::SFNT || fformat == ScFace::TTCF)
1788
                {
1789
                        if (face.type() == ScFace::OTF)
1790
                        {
23548 jghali 1791
                                PutDoc("/FontFile3 " + Pdf::toPdf(embeddedFontObject) + " 0 R\n");
20112 jghali 1792
                        }
1793
                        else
1794
                        {
23548 jghali 1795
                                PutDoc("/FontFile2 " + Pdf::toPdf(embeddedFontObject) + " 0 R\n");
20112 jghali 1796
                        }
1797
                }
1798
                else if (fformat == ScFace::PFB)
1799
                        PutDoc("/FontFile "+Pdf::toPdf(embeddedFontObject)+" 0 R\n");
1800
                else if (fformat == ScFace::PFA)
1801
                        PutDoc("/FontFile "+Pdf::toPdf(embeddedFontObject)+" 0 R\n");
1802
        }
1803
        PutDoc(">>");
1804
        writer.endObj(fontDescriptor);
1805
        return fontDescriptor;
20103 avox 1806
}
1807
 
22600 craig 1808
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 1809
{
20112 jghali 1810
        PdfFont result;
1811
        result.name = Pdf::toName(fontName);
1812
        result.method = glyphmap.isEmpty()? Use_Embedded : Use_Subset;
1813
        result.encoding = glyphmap.isEmpty()? Encode_IdentityH : Encode_Subset;
1814
        result.glyphmap = glyphmap;
1815
 
1816
        PdfId fontWidths2 = writer.newObject();
1817
        writer.startObj(fontWidths2);
1818
        QList<QByteArray> toUnicodeMaps;
1819
        QList<int> toUnicodeMapsCount;
22719 jghali 1820
        QByteArray toUnicodeMap;
20112 jghali 1821
        int toUnicodeMapCounter = 0;
1822
 
1823
        PutDoc("[ ");
1824
        QList<uint> keys = gl.uniqueKeys();
1825
        bool seenNotDef = false;
23483 jghali 1826
        for (auto git = keys.begin(); git != keys.end(); ++git)
20112 jghali 1827
        {
1828
                uint gid = result.encoding == Encode_Subset? glyphmap[*git] : *git;
1829
                if (gid > 0 || !seenNotDef)
1830
                {
1831
                        seenNotDef |= (gid == 0);
23483 jghali 1832
                        PutDoc(Pdf::toPdf(gid) + " [" + Pdf::toPdf(static_cast<int>(face.glyphWidth(*git) * 1000)) + "] " );
1833
                        QString tmp = QString::asprintf("%04X", gid);
1834
                        QString tmp2 = gl.value(*git).toUnicode;
20112 jghali 1835
                        toUnicodeMap += "<" + Pdf::toAscii(tmp)+ "> <" + Pdf::toAscii(tmp2) + ">\n";
1836
                        toUnicodeMapCounter++;
1837
                        if (toUnicodeMapCounter == 100)
1838
                        {
1839
                                toUnicodeMaps.append(toUnicodeMap);
1840
                                toUnicodeMapsCount.append(toUnicodeMapCounter);
22719 jghali 1841
                                toUnicodeMap.clear();
20112 jghali 1842
                                toUnicodeMapCounter = 0;
1843
                        }
1844
                }
1845
        }
1846
        PutDoc("]");
1847
        writer.endObj(fontWidths2);
1848
        if (toUnicodeMapCounter != 0)
1849
        {
1850
                toUnicodeMaps.append(toUnicodeMap);
1851
                toUnicodeMapsCount.append(toUnicodeMapCounter);
1852
        }
22719 jghali 1853
        QByteArray toUnicodeMapStream;
20112 jghali 1854
        toUnicodeMapStream += "/CIDInit /ProcSet findresource begin\n";
1855
        toUnicodeMapStream += "12 dict begin\n";
1856
        toUnicodeMapStream += "begincmap\n";
1857
        toUnicodeMapStream += "/CIDSystemInfo <<\n";
1858
        toUnicodeMapStream += "/Registry (Adobe)\n";
1859
        toUnicodeMapStream += "/Ordering (UCS)\n";
1860
        toUnicodeMapStream += "/Supplement 0\n";
1861
        toUnicodeMapStream += ">> def\n";
1862
        toUnicodeMapStream += "/CMapName /Adobe-Identity-UCS def\n";
1863
        toUnicodeMapStream += "/CMapType 2 def\n";
1864
        toUnicodeMapStream += "1 begincodespacerange\n";
1865
        toUnicodeMapStream += "<0000> <FFFF>\n";
1866
        toUnicodeMapStream += "endcodespacerange\n";
1867
        for (int uniC = 0; uniC < toUnicodeMaps.count(); uniC++)
1868
        {
1869
                toUnicodeMapStream += Pdf::toPdf(toUnicodeMapsCount[uniC]);
1870
                toUnicodeMapStream += " beginbfchar\n";
1871
                toUnicodeMapStream += toUnicodeMaps[uniC];
1872
                toUnicodeMapStream += "endbfchar\n";
1873
        }
1874
        toUnicodeMapStream += "endcmap\n";
1875
        toUnicodeMapStream += "CMapName currentdict /CMap defineresource pop\n";
1876
        toUnicodeMapStream += "end\n";
1877
        toUnicodeMapStream += "end\n";
1878
        PdfId fontToUnicode2 = WritePDFStream(toUnicodeMapStream);
1879
        PdfId fontObject2 = writer.newObject();
1880
        writer.startObj(fontObject2);
1881
        PutDoc("<<\n/Type /Font\n/Subtype /Type0\n");
1882
        PutDoc("/Name " + Pdf::toName(fontName) + "\n");
21759 jghali 1883
        PutDoc("/BaseFont "+ Pdf::toName(baseFont) +"\n");
20112 jghali 1884
        PutDoc("/Encoding /Identity-H\n");
1885
        PutDoc("/ToUnicode "+Pdf::toPdf(fontToUnicode2)+" 0 R\n");
1886
        PutDoc("/DescendantFonts [");
1887
        PutDoc("<</Type /Font");
1888
        if (face.type() == ScFace::OTF)
1889
                PutDoc("/Subtype /CIDFontType0");
1890
        else
1891
                PutDoc("/Subtype /CIDFontType2");
21759 jghali 1892
        PutDoc("/BaseFont " + Pdf::toName(baseFont));
20112 jghali 1893
        PutDoc("/FontDescriptor " + Pdf::toPdf(fontDes)+ " 0 R");
1894
        PutDoc("/CIDSystemInfo <</Ordering(Identity)/Registry(Adobe)/Supplement 0>>");
1895
        PutDoc("/DW 1000");
1896
        PutDoc("/W "+Pdf::toPdf(fontWidths2)+" 0 R");
1897
        PutDoc("/CIDToGIDMap /Identity");
1898
        PutDoc(">>"); // close CIDFont dictionary
1899
        PutDoc("]\n"); // close DescendantFonts array
1900
        PutDoc(">>");
1901
        writer.endObj(fontObject2);
1902
        pageData.FObjects[fontName] = fontObject2;
1903
        return result;
20103 avox 1904
}
1905
 
1906
 
1907
PdfFont PDFLibCore::PDF_EncodeSimpleFont(const QByteArray& fontName, ScFace& face, const QByteArray& baseFont, const QByteArray& subtype, bool isEmbedded, PdfId fontDes, const ScFace::FaceEncoding& gl)
1908
{
20112 jghali 1909
        PdfFont result;
1910
        result.name = Pdf::toName(fontName);
1911
        result.method = isEmbedded? Use_Embedded : Use_System;
1912
        result.encoding = Encode_224;
1913
 
1914
        int nglyphs = 0;
22717 jghali 1915
        for (auto gli = gl.cbegin(); gli != gl.cend(); ++gli)
20112 jghali 1916
        {
1917
                if (gli.key() > static_cast<uint>(nglyphs))
1918
                        nglyphs = gli.key();
1919
        }
1920
        ++nglyphs;
1921
        //                      qDebug() << QString("pdflib: nglyphs %1 max %2").arg(nglyphs).arg(face.maxGlyph());
1922
 
1923
        uint Fcc = nglyphs / 224;
1924
        if ((nglyphs % 224) != 0)
1925
                Fcc += 1;
1926
        for (uint Fc = 0; Fc < Fcc; ++Fc)
1927
        {
1928
                PdfId fontWidths2 = writer.newObject();
1929
                writer.startObj(fontWidths2);
1930
                int chCount = 32;
1931
                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 ");
1932
                for (int ww = 32; ww < 256; ++ww)
1933
                {
1934
                        uint glyph = 224 * Fc + ww - 32;
1935
                        if (gl.contains(glyph))
1936
                                PutDoc(Pdf::toPdf(static_cast<int>(face.glyphWidth(glyph)* 1000))+" ");
1937
                        else
1938
                                PutDoc("0 ");
1939
                        chCount++;
1940
                        if (signed(glyph) == nglyphs-1)
1941
                                break;
1942
                }
1943
                PutDoc("]");
1944
                writer.endObj(fontWidths2);
22717 jghali 1945
 
20112 jghali 1946
                QStringList toUnicodeMaps;
22719 jghali 1947
                QList<int>  toUnicodeMapsCount;
1948
                QString toUnicodeMap;
20112 jghali 1949
                int toUnicodeMapCounter = 0;
22717 jghali 1950
                int crc = 0;
1951
                bool startOfSeq = true;
1952
 
1953
                PdfId fontEncoding2 = writer.newObject();
1954
                writer.startObj(fontEncoding2);
20112 jghali 1955
                PutDoc("<< /Type /Encoding\n");
1956
                PutDoc("/Differences [ \n");
1957
                for (int ww2 = 32; ww2 < 256; ++ww2)
1958
                {
1959
                        uint glyph = 224 * Fc + ww2 - 32;
1960
                        ScFace::FaceEncoding::ConstIterator glIt = gl.find(glyph);
22143 jghali 1961
                        if (glIt != gl.cend() && !glIt.value().glyphName.isEmpty())
20112 jghali 1962
                        {
22143 jghali 1963
                                const ScFace::GlyphEncoding& glEncoding = glIt.value();
20112 jghali 1964
                                if (startOfSeq)
1965
                                {
1966
                                        PutDoc(Pdf::toPdf(ww2)+" ");
1967
                                        startOfSeq = false;
1968
                                }
22143 jghali 1969
                                PutDoc(Pdf::toName(glEncoding.glyphName)+" ");
23483 jghali 1970
                                QString tmp = QString::asprintf("%02X", ww2);
1971
                                QString tmp2 = glEncoding.toUnicode;
20112 jghali 1972
                                toUnicodeMap += "<" + Pdf::toAscii(tmp) + "> <" + Pdf::toAscii(tmp2) + ">\n";
1973
                                //QString("<%1> <%2>\n").arg(tmp).arg((tmp2));
1974
                                toUnicodeMapCounter++;
1975
                                if (toUnicodeMapCounter == 100)
1976
                                {
1977
                                        toUnicodeMaps.append(toUnicodeMap);
1978
                                        toUnicodeMapsCount.append(toUnicodeMapCounter);
22717 jghali 1979
                                        toUnicodeMap.clear();
20112 jghali 1980
                                        toUnicodeMapCounter = 0;
1981
                                }
1982
                                crc++;
1983
                        }
1984
                        else
1985
                        {
1986
                                startOfSeq = true;
1987
                        }
1988
                        if (signed(glyph) == nglyphs-1)
1989
                                break;
1990
                        if (crc > 8)
1991
                        {
1992
                                PutDoc("\n");
1993
                                crc = 0;
1994
                        }
1995
                }
1996
                if (toUnicodeMapCounter != 0)
1997
                {
1998
                        toUnicodeMaps.append(toUnicodeMap);
1999
                        toUnicodeMapsCount.append(toUnicodeMapCounter);
2000
                }
2001
                PutDoc("]\n");
2002
                PutDoc(">>");
2003
                writer.endObj(fontEncoding2);
22719 jghali 2004
                QByteArray toUnicodeMapStream;
20112 jghali 2005
                toUnicodeMapStream += "/CIDInit /ProcSet findresource begin\n";
2006
                toUnicodeMapStream += "12 dict begin\n";
2007
                toUnicodeMapStream += "begincmap\n";
2008
                toUnicodeMapStream += "/CIDSystemInfo <<\n";
2009
                toUnicodeMapStream += "/Registry (Adobe)\n";
2010
                toUnicodeMapStream += "/Ordering (UCS)\n";
2011
                toUnicodeMapStream += "/Supplement 0\n";
2012
                toUnicodeMapStream += ">> def\n";
2013
                toUnicodeMapStream += "/CMapName /Adobe-Identity-UCS def\n";
2014
                toUnicodeMapStream += "/CMapType 2 def\n";
2015
                toUnicodeMapStream += "1 begincodespacerange\n";
2016
                toUnicodeMapStream += "<0000> <FFFF>\n";
2017
                toUnicodeMapStream += "endcodespacerange\n";
2018
                for (int uniC = 0; uniC < toUnicodeMaps.count(); uniC++)
2019
                {
2020
                        toUnicodeMapStream += Pdf::toPdf(toUnicodeMapsCount[uniC]);
2021
                        toUnicodeMapStream += " beginbfchar\n";
2022
                        toUnicodeMapStream += toUnicodeMaps[uniC];
2023
                        toUnicodeMapStream += "endbfchar\n";
2024
                }
2025
                toUnicodeMapStream += "endcmap\n";
2026
                toUnicodeMapStream += "CMapName currentdict /CMap defineresource pop\n";
2027
                toUnicodeMapStream += "end\n";
2028
                toUnicodeMapStream += "end\n";
2029
                PdfId fontToUnicode2 = WritePDFStream(toUnicodeMapStream);
2030
                PdfId fontObject2 = writer.newObject();
2031
                writer.startObj(fontObject2);
2032
                PutDoc("<<\n/Type /Font\n/Subtype ");
2033
                PutDoc(subtype + "\n");
2034
                PutDoc("/Name "+Pdf::toName(fontName)+"S"+Pdf::toPdf(Fc)+"\n");
21759 jghali 2035
                PutDoc("/BaseFont "+ Pdf::toName(baseFont) + "\n");
20112 jghali 2036
                PutDoc("/FirstChar 0\n");
2037
                PutDoc("/LastChar "+Pdf::toPdf(chCount-1)+"\n");
2038
                PutDoc("/Widths "+Pdf::toPdf(fontWidths2)+" 0 R\n");
2039
                PutDoc("/Encoding "+Pdf::toPdf(fontEncoding2)+" 0 R\n");
2040
                PutDoc("/ToUnicode "+Pdf::toPdf(fontToUnicode2)+" 0 R\n");
2041
                PutDoc("/FontDescriptor "+Pdf::toPdf(fontDes)+" 0 R\n");
2042
                PutDoc(">>");
2043
                writer.endObj(fontObject2);
2044
                pageData.FObjects[fontName + "S"+Pdf::toPdf(Fc)] = fontObject2;
22720 jghali 2045
        } // for (Fc)
21744 jghali 2046
 
2047
        return result;
2048
}
2049
 
2050
PdfFont PDFLibCore::PDF_EncodeFormFont(const QByteArray& fontName, ScFace& face, const QByteArray& baseFont, const QByteArray& subtype, PdfId fontDes)
2051
{
2052
        PdfFont formFont;
2053
        formFont.name = Pdf::toName(fontName) + "Form";
2054
        formFont.usage = Used_in_Forms;
2055
 
20112 jghali 2056
        PdfId fontWidthsForm = writer.newObject();
2057
        writer.startObj(fontWidthsForm);
2058
        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 2059
        for (uint ww = 32; ww < 256; ++ww)
20112 jghali 2060
        {
21744 jghali 2061
                int unicode = Pdf::fromPDFDocEncoding(ww);
2062
                uint glyph  = face.char2CMap(unicode);
2063
                if (glyph > 0)
20112 jghali 2064
                        PutDoc(Pdf::toPdf(static_cast<int>(face.glyphWidth(glyph)* 1000))+" ");
2065
                else
2066
                        PutDoc("0 ");
2067
        }
2068
        PutDoc("]");
2069
        writer.endObj(fontWidthsForm);
21744 jghali 2070
 
20112 jghali 2071
        PdfId fontObjectForm = writer.newObject();
2072
        writer.startObj(fontObjectForm);
2073
        PutDoc("<<\n/Type /Font\n/Subtype ");
2074
        PutDoc(subtype + "\n");
21759 jghali 2075
        PutDoc("/Name " + formFont.name + "\n");
2076
        PutDoc("/BaseFont "+ Pdf::toName(baseFont) +"\n");
20112 jghali 2077
        PutDoc("/Encoding << \n");
2078
        PutDoc("/Differences [ \n");
2079
        PutDoc("24 /breve /caron /circumflex /dotaccent /hungarumlaut /ogonek /ring /tilde\n");