Subversion Repositories Scribus

Rev

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