Subversion Repositories Scribus

Rev

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

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