Subversion Repositories Scribus

Rev

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