Subversion Repositories Scribus

Rev

Rev 24464 | Rev 24655 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
For general Scribus (>=1.3.2) copyright and licensing information please refer
to the COPYING file provided with the program. Following this notice may exist
a copyright and/or license notice that predates the release of Scribus 1.3.2
for which a new license (GPL+exception) is in place.
*/

/***************************************************************************
                                                  pdflib_core.cpp  -  description
                                                         -------------------
        begin                : Sat Jan 19 2002
        copyright            : (C) 2002 by Franz Schmid
        email                : Franz.Schmid@altmuehlnet.de
 ***************************************************************************/


/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/


#include "pdflib_core.h"

#include "scconfig.h"

#if defined(_MSC_VER) && !defined(_USE_MATH_DEFINES)
#define _USE_MATH_DEFINES
#endif
#include <cmath>
#include <cstdlib>
#include <string>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

#include "rc4.h"

#include <QByteArray>
#include <QCryptographicHash>
#include <QDateTime>
#include <QDataStream>
#include <QDebug>
#include <QDir>
#include <QFileInfo>
#include <QImage>
#include <QList>
#include <QPainterPath>
#include <QRect>
#include <QRegExp>
#include <QScopedPointer>
#include <QStack>
#include <QString>
#include <QTemporaryFile>
#include <QTextCodec>
#include <QtXml>
#include <QUuid>

#include "cmsettings.h"
#include "commonstrings.h"
#include "pageitem.h"
#include "pageitem_textframe.h"
#include "pageitem_group.h"
#include "pageitem_table.h"
#include "pdfoptions.h"
#include "prefscontext.h"
#include "prefsmanager.h"
#include "sccolor.h"
#include "sccolorengine.h"
#include "scfonts.h"
#include "text/textlayoutpainter.h"
#include "fonts/cff.h"
#include "fonts/sfnt.h"
#include "scpage.h"
#include "scpaths.h"
#include "scpattern.h"

#include "scribuscore.h"
#include "scribusdoc.h"
#include "scstreamfilter_flate.h"
#include "scstreamfilter_rc4.h"
#include "tableutils.h"
#include "util.h"
#include "util_file.h"
#include "util_formats.h"
#include "util_math.h"
#include "util_ghostscript.h"
#include "text/boxes.h"

#ifdef HAVE_OSG
        #include "third_party/prc/exportPRC.h"
#endif

#include "ui/bookmwin.h"
#include "ui/bookmarkpalette.h"
#include "ui/multiprogressdialog.h"

using namespace std;
using namespace TableUtils;

#if defined(_WIN32)
#undef GetObject
#endif

static inline QByteArray FToStr(double c)
{
        double v = c;
        if (fabs(c) < 0.0000001)
                v = 0.0;
        return QByteArray::number(v, 'f', 5);
}

static inline QByteArray TransformToStr(const QTransform& tr)
{
        return FToStr(tr.m11()) + " " + FToStr(tr.m12()) + " " + FToStr(tr.m21()) + " " + FToStr(tr.m22()) + " " + FToStr(tr.dx()) + " " + FToStr(tr.dy());
}

class PdfPainter: public TextLayoutPainter
{
        QByteArray m_backBuffer;
        QByteArray m_glyphBuffer;
        QByteArray m_pathBuffer;
//      PageItem* m_item;
        QMap<QString, PdfFont>  m_UsedFontsP;
        PDFLibCore *m_pdf;
        uint m_PNr;
        const ScPage* m_page;

        QByteArray m_prevFontName;
        int        m_prevFontSize;

        QByteArray transformToStr(const QTransform& tr)
        {
                return FToStr(tr.m11()) + " " + FToStr(-tr.m12()) + " " + FToStr(-tr.m21()) + " " + FToStr(tr.m22()) + " " + FToStr(tr.dx()) + " " + FToStr(-tr.dy());
        }

public:
        PdfPainter(PageItem *ite, PDFLibCore *pdf, uint num, const ScPage* pag) :
                m_backBuffer(),
                m_glyphBuffer(),
                m_pathBuffer(),
//              m_item(ite),
                m_pdf(pdf),
                m_PNr(num),
                m_page(pag),
                m_prevFontSize(-1)
        {}

        ~PdfPainter() {}

        void drawGlyph(const GlyphCluster& gc) override
        {
                if (gc.isControlGlyphs() || gc.isEmpty())
                        return;
                       
                double current_x = 0.0;
                for (const GlyphLayout& gl : gc.glyphs())
                {
                        if (gl.glyph >= ScFace::CONTROL_GLYPHS)
                        {
                                current_x += gl.xadvance * gl.scaleH;
                                continue;
                        }

                        PdfFont pdfFont = m_pdf->UsedFontsP[font().replacementName()];
                        QByteArray StrokeColor;
                        QByteArray FillColor;

                        if (strokeColor().color != CommonStrings::None)
                                StrokeColor = m_pdf->putColor(strokeColor().color, strokeColor().shade, false);
                        if (fillColor().color != CommonStrings::None)
                                FillColor = m_pdf->putColor(fillColor().color, fillColor().shade, true);

                        QTransform transform = matrix();
                        if (pdfFont.method == Use_XForm)
                        {
                                if (!FillColor.isEmpty())
                                        m_pathBuffer += FillColor;

                                m_pathBuffer += "q\n";
                                m_pathBuffer += transformToStr(transform) + " cm\n";
                                m_pathBuffer += FToStr(fontSize()) + " 0 0 " + FToStr(fontSize()) + " " + FToStr(x() + gl.xoffset + current_x) + " " + FToStr(-y() + fontSize() - gl.yoffset) + " cm\n";

                                if (gl.scaleV != 1.0)
                                        m_pathBuffer += "1 0 0 1 0 " + FToStr(gl.scaleV - 1.0) + " cm\n";
                                m_pathBuffer += FToStr(qMax(gl.scaleH, 0.1)) + " 0 0 " + FToStr(qMax(gl.scaleV, 0.1)) + " 0 0 cm\n";

                                if (!FillColor.isEmpty())
                                        m_pathBuffer += pdfFont.name + "_gl" + Pdf::toPdf(gl.glyph) + " Do\n";
                                m_pathBuffer += "Q\n";
                        }
                        else
                        {
                                uint gid = gl.glyph;
                                uint fontNr = 65535;

                                switch (pdfFont.encoding)
                                {
                                case  Encode_256:
                                        gid = pdfFont.glyphmap[gid];
                                        fontNr = gid / 256;
                                        gid = gid % 256;
                                        break;
                                case Encode_224:
                                        fontNr = gid / 224;
                                        gid = gid % 224 + 32;
                                        break;
                                case Encode_Subset:
                                        gid = pdfFont.glyphmap[gid];
                                        break;
                                case Encode_IdentityH:
                                        break;
                                }

                                QByteArray pdfFontName = pdfFont.name;
                                if (fontNr != 65535)
                                {
                                        pdfFontName += "S";
                                        pdfFontName += Pdf::toPdf(fontNr);
                                }

                                if ((pdfFontName != m_prevFontName) || (fontSize() != m_prevFontSize))
                                {
                                        m_glyphBuffer += pdfFontName + " " + FToStr(fontSize()) + " Tf\n";
                                        m_prevFontName = pdfFontName;
                                        m_prevFontSize = fontSize();
                                }

                                if (!StrokeColor.isEmpty())
                                        m_glyphBuffer += StrokeColor;

                                if (!FillColor.isEmpty())
                                        m_glyphBuffer += FillColor;

                                m_glyphBuffer += "0 Tr\n";

                                transform.translate(x() + gl.xoffset + current_x , y() + gl.yoffset);
                                transform.scale(qMax(gl.scaleH, 0.1), qMax(gl.scaleV, 0.1));
                                m_glyphBuffer += transformToStr(transform) + " Tm\n";

                                if (pdfFont.method != Use_Type3 || !FillColor.isEmpty())
                                {
                                        switch (pdfFont.encoding)
                                        {
                                        case Encode_224:
                                        case Encode_256:
                                                m_glyphBuffer += Pdf::toHexString8(gid) + " Tj\n";
                                                break;
                                        case Encode_IdentityH:
                                        default:
                                                m_glyphBuffer += Pdf::toHexString16(gid) + " Tj\n";
                                                break;
                                        }
                                }
                        }
                        current_x += gl.xadvance * gl.scaleH;
                }
        }

        void drawGlyphOutline(const GlyphCluster& gc, bool fill) override
        {
                if (gc.isControlGlyphs() || gc.isEmpty())
                        return;

                double current_x = 0.0;
                for (const GlyphLayout& gl : gc.glyphs())
                {
                        if (gl.glyph >= ScFace::CONTROL_GLYPHS)
                        {
                                current_x += gl.xadvance * gl.scaleH;
                                continue;
                        }

                        PdfFont pdfFont = m_pdf->UsedFontsP[font().replacementName()];
                        QByteArray StrokeColor;
                        QByteArray FillColor;
                        QTransform transform = matrix();

                        if (strokeColor().color != CommonStrings::None)
                                StrokeColor = m_pdf->putColor(strokeColor().color, strokeColor().shade, false);
                        if (fill && fillColor().color != CommonStrings::None)
                                FillColor = m_pdf->putColor(fillColor().color, fillColor().shade, true);

                        if (pdfFont.method == Use_XForm)
                        {
                                m_pathBuffer += "q\n";
                                if (!StrokeColor.isEmpty())
                                {
                                        m_pathBuffer += FToStr(strokeWidth()) + " w\n[] 0 d\n0 J\n0 j\n";
                                        m_pathBuffer += StrokeColor;
                                }
                                m_pathBuffer += transformToStr(transform) + " cm\n";

                                if (!FillColor.isEmpty())
                                {
                                        m_pathBuffer += "q\n";
                                        m_pathBuffer += FillColor;
                                        m_pathBuffer += FToStr(fontSize()) + " 0 0 " + FToStr(fontSize()) + " " + FToStr(x() + gl.xoffset + current_x) + " " + FToStr(-y() + fontSize() - gl.yoffset) + " cm\n";
                                        if (gc.scaleV() != 1.0)
                                                m_pathBuffer += "1 0 0 1 0 " + FToStr(gl.scaleV - 1.0) + " cm\n";
                                        m_pathBuffer += FToStr(qMax(gc.scaleH(), 0.1)) + " 0 0 " + FToStr(qMax(gc.scaleV(), 0.1)) + " 0 0 cm\n";
                                        m_pathBuffer += pdfFont.name + "_gl" + Pdf::toPdf(gl.glyph) + " Do\n";
                                        m_pathBuffer += "Q\n";
                                }

                                m_pathBuffer += "1.0 0 0 1.0 " + FToStr(x()) + " " + FToStr(fontSize() - y()) + " cm\n";
                                m_pathBuffer += "1.0 0 0 1.0 " + FToStr(gl.xoffset + current_x) + " " + FToStr(gc.scaleV() * fontSize() - fontSize() - gl.yoffset) + " cm\n";

                                FPointArray outline = font().glyphOutline(gl.glyph);
                                QTransform mat;
                                mat.scale((fontSize() * gc.scaleH()) / 10.0, (fontSize() * gc.scaleV()) / 10.0);
                                outline.map(mat);
                                bool nPath = true;
                                FPoint np;
                                if (outline.size() > 3)
                                {
                                        for (int poi = 0; poi < outline.size() - 3; poi += 4)
                                        {
                                                if (outline.isMarker(poi))
                                                {
                                                        m_pathBuffer += "h\n";
                                                        nPath = true;
                                                        continue;
                                                }

                                                if (nPath)
                                                {
                                                        np = outline.point(poi);
                                                        m_pathBuffer += FToStr(np.x()) + " " + FToStr(-np.y()) + " m\n";
                                                        nPath = false;
                                                }

                                                np = outline.point(poi + 1);
                                                m_pathBuffer += FToStr(np.x()) + " " + FToStr(-np.y()) + " ";
                                                np = outline.point(poi + 3);
                                                m_pathBuffer += FToStr(np.x()) + " " + FToStr(-np.y()) + " ";
                                                np = outline.point(poi + 2);
                                                m_pathBuffer += FToStr(np.x()) + " " + FToStr(-np.y()) + " c\n";
                                        }
                                }
                                m_pathBuffer += "h s\n";
                                m_pathBuffer += "Q\n";
                        }
                        else
                        {
                                if (!StrokeColor.isEmpty())
                                        m_pathBuffer += StrokeColor;

                                uint gid = gl.glyph;
                                uint fontNr = 65535;
                                switch (pdfFont.encoding)
                                {
                                case  Encode_256:
                                        gid = pdfFont.glyphmap[gid];
                                        fontNr = gid / 256;
                                        gid = gid % 256;
                                        break;
                                case Encode_224:
                                        fontNr = gid / 224;
                                        gid = gid % 224 + 32;
                                        break;
                                case Encode_Subset:
                                        gid = pdfFont.glyphmap[gid];
                                        break;
                                case Encode_IdentityH:
                                        break;
                                }

                                QByteArray pdfFontName = pdfFont.name;
                                if (fontNr != 65535)
                                {
                                        pdfFontName += "S";
                                        pdfFontName += Pdf::toPdf(fontNr);
                                }

                                if ((pdfFontName != m_prevFontName) || (fontSize() != m_prevFontSize))
                                {
                                        m_glyphBuffer += pdfFontName + " " + FToStr(fontSize()) + " Tf\n";
                                        m_prevFontName = pdfFontName;
                                        m_prevFontSize = fontSize();
                                }

                                if (!StrokeColor.isEmpty())
                                        m_glyphBuffer += StrokeColor;

                                if (!FillColor.isEmpty())
                                        m_glyphBuffer += FillColor;

                                if (pdfFont.method == Use_Type3 && !StrokeColor.isEmpty())
                                {
                                        m_pathBuffer += "q\n";
                                        m_pathBuffer += FToStr(strokeWidth()) + " w\n[] 0 d\n0 J\n0 j\n";

                                        m_pathBuffer += transformToStr(transform) + " cm\n";
                                        m_pathBuffer += "1.0 0 0 1.0 " + FToStr(x()) + " " + FToStr(fontSize() - y()) + " cm\n";
                                        m_pathBuffer += "1.0 0 0 1.0 " + FToStr(gl.xoffset + current_x) + " " + FToStr(gc.scaleV() * fontSize() - fontSize() - gl.yoffset) + " cm\n";

                                        /* paint outline */
                                        FPointArray outline = font().glyphOutline(gl.glyph);
                                        QTransform mat;
                                        mat.scale((fontSize() * gc.scaleH()) / 10.0, (fontSize() * gc.scaleV()) / 10.0);
                                        outline.map(mat);
                                        bool nPath = true;
                                        FPoint np;
                                        if (outline.size() > 3)
                                        {
                                                for (int poi = 0; poi < outline.size() - 3; poi += 4)
                                                {
                                                        if (outline.isMarker(poi))
                                                        {
                                                                m_pathBuffer += "h\n";
                                                                nPath = true;
                                                                continue;
                                                        }

                                                        if (nPath)
                                                        {
                                                                np = outline.point(poi);
                                                                m_pathBuffer += FToStr(np.x()) + " " + FToStr(-np.y()) + " m\n";
                                                                nPath = false;
                                                        }

                                                        np = outline.point(poi + 1);
                                                        m_pathBuffer += FToStr(np.x()) + " " + FToStr(-np.y()) + " ";
                                                        np = outline.point(poi + 3);
                                                        m_pathBuffer += FToStr(np.x()) + " " + FToStr(-np.y()) + " ";
                                                        np = outline.point(poi + 2);
                                                        m_pathBuffer += FToStr(np.x()) + " " + FToStr(-np.y()) + " c\n";
                                                }
                                        }
                                        m_pathBuffer += "h s\n";
                                        m_pathBuffer += "Q\n";
                                }
                                else
                                {
                                        m_glyphBuffer += FToStr(strokeWidth()) + " w ";
                                        if (fill)
                                                m_glyphBuffer += "2 Tr\n";
                                        else
                                                m_glyphBuffer += "1 Tr\n";
                                }

                                transform.translate(x() + gl.xoffset + current_x, y() + gl.yoffset);
                                transform.scale(qMax(gc.scaleH(), 0.1), qMax(gc.scaleV(), 0.1));
                                m_glyphBuffer += transformToStr(transform) + " Tm\n";

                                if (pdfFont.method != Use_Type3 || !FillColor.isEmpty())
                                {
                                        switch (pdfFont.encoding)
                                        {
                                        case Encode_224:
                                        case Encode_256:
                                                m_glyphBuffer += Pdf::toHexString8(gid) + " Tj\n";
                                                break;
                                        case Encode_IdentityH:
                                        default:
                                                m_glyphBuffer += Pdf::toHexString16(gid) + " Tj\n";
                                                break;
                                        }
                                }
                        }
                        current_x += gl.xadvance * gl.scaleH;
                }
        }

        void drawLine(QPointF start, QPointF end) override
        {
                QTransform transform = matrix();
                transform.translate(x(), y());
                m_pathBuffer += "q\n";
                m_pathBuffer += transformToStr(transform) + " cm\n";
                m_pathBuffer += m_pdf->putColor(strokeColor().color, strokeColor().shade, false);
                m_pathBuffer += FToStr(strokeWidth()) + " w\n";
                m_pathBuffer += FToStr(start.x()) + " " + FToStr(-start.y()) + " m\n";
                m_pathBuffer += FToStr(end.x()) + " " + FToStr(-end.y()) + " l\n";
                m_pathBuffer += "S\n";
                m_pathBuffer += "Q\n";
        }

        QByteArray getBuffer()
        {
                return m_backBuffer + "BT\n" + m_glyphBuffer + "ET\n" + m_pathBuffer;
        }

        void drawRect(QRectF rect) override
        {
                QTransform transform = matrix();
//              transform.translate(x(), y());
                double rectX = x() + rect.x();
                double rectY = -y() - rect.y();
                m_backBuffer += "q\n";
                m_backBuffer += transformToStr(transform) + " cm\n";
                m_backBuffer += "n\n";
                m_backBuffer += m_pdf->putColor(fillColor().color, fillColor().shade, true);
                m_backBuffer += m_pdf->putColor(strokeColor().color, strokeColor().shade, false);
                m_backBuffer += FToStr(rectX) + " " + FToStr(rectY) + " m\n";
                m_backBuffer += FToStr(rectX + rect.width()) + " " + FToStr(rectY) + " l\n";
                m_backBuffer += FToStr(rectX + rect.width()) + " " + FToStr(rectY - rect.height()) + " l\n";
                m_backBuffer += FToStr(rectX) + " " + FToStr(rectY - rect.height()) + " l\n";
                m_backBuffer += "h\nf\n";
                m_backBuffer += "Q\n";
        }

        void drawObject(PageItem* embedded) override
        {
                m_glyphBuffer += "ET\n" + m_pathBuffer;
                m_pathBuffer.clear();

                m_pathBuffer += "q\n";
                m_pathBuffer += FToStr(scaleH()) + " 0 0 " + FToStr(scaleV()) + " " + FToStr(x() + embedded->gXpos) + " " + FToStr(-(y() + embedded->gYpos)) + " cm\n";

                QByteArray output;
                if (!m_pdf->PDF_ProcessItem(output, embedded, m_page, m_PNr, true))
                        return;
                m_pathBuffer += output;
                m_pathBuffer += "Q\n";
                m_glyphBuffer += m_pathBuffer + "\n";
                m_pathBuffer.clear();

                m_prevFontName.clear();
                m_prevFontSize = -1;
                m_glyphBuffer += "BT\n";
        }
};

PDFLibCore::PDFLibCore(ScribusDoc & docu)
        : PDFLibCore(docu, docu.pdfOptions())
{
       
}

PDFLibCore::PDFLibCore(ScribusDoc & docu, const PDFOptions& options)
        : QObject(&docu),
        doc(docu),
        ActPageP(nullptr),
        Options(options),
        Bvie(nullptr),
        ucs2Codec(nullptr),
        ResNam("RE"),
        ResCount(0),
        NDnam("LI"),
        NDnum(0),
        spotNam("Spot"),
        spotCount(0),
        progressDialog(nullptr),
        abortExport(false),
        usingGUI(ScCore->usingGUI()),
        bleedDisplacementX(0),
        bleedDisplacementY(0)
{
        Catalog.Outlines = 2;
        Catalog.PageTree = 3;
        Catalog.Dest = 4;
        Outlines.First = 0;
        Outlines.Last = 0;
        Outlines.Count = 0;
        pageData.ObjNum = 0;
        pageData.Thumb = 0;

        if (usingGUI)
        {
                progressDialog = new MultiProgressDialog( tr("Saving PDF"), CommonStrings::tr_Cancel, doc.scMW());
                Q_CHECK_PTR(progressDialog);
                QStringList barNames, barTexts;
                barNames << "EMP" << "EP" << "ECPI";
                barTexts << tr("Exporting Master Page:") << tr("Exporting Page:") << tr("Exporting Items on Current Page:");
                QList<bool> barsNumeric;
                barsNumeric << true << true << false;
                progressDialog->addExtraProgressBars(barNames, barTexts, barsNumeric);
                connect(progressDialog, SIGNAL(canceled()), this, SLOT(cancelRequested()));
        }
}

PDFLibCore::~PDFLibCore()
{
        delete progressDialog;
}


bool PDFLibCore::PDF_IsPDFX()
{
        if (Options.Version == PDFVersion::PDF_X1a)
                return true;
        if (Options.Version == PDFVersion::PDF_X3)
                return true;
        if (Options.Version == PDFVersion::PDF_X4)
                return true;
        return false;
}

bool PDFLibCore::PDF_IsPDFX(const PDFVersion& ver)
{
        if (ver == PDFVersion::PDF_X1a)
                return true;
        if (ver == PDFVersion::PDF_X3)
                return true;
        if (ver == PDFVersion::PDF_X4)
                return true;
        return false;
}

bool PDFLibCore::doExport(const QString& fn, const std::vector<int> & pageNs, const QMap<int, QImage> & thumbs)
{
        QImage thumb;
        bool ret = false, error = false;
        int  pc_exportpages=0;
        int  pc_exportmasterpages=0;
        if (usingGUI)
                progressDialog->show();

        ucs2Codec = QTextCodec::codecForName("ISO-10646-UCS-2");
        if (!ucs2Codec)
                ucs2Codec = QTextCodec::codecForName("UTF-16");
        if (!ucs2Codec)
        {
                PDF_Error( tr("Qt build miss both \"UTF-16\" and \"ISO-10646-UCS-2\" text codecs, pdf export is not possible") );
                return false;
        }

        QMap<QString, QMap<uint, QString> > usedFonts;
        doc.getUsedFonts(usedFonts);

        if (PDF_Begin_Doc(fn, PrefsManager::instance().appPrefs.fontPrefs.AvailFonts, usedFonts, doc.scMW()->bookmarkPalette->BView))
        {
                QMap<int, int> pageNsMpa;
                for (uint a = 0; a < pageNs.size(); ++a)
                {
                        pageNsMpa.insert(doc.MasterNames[doc.DocPages.at(pageNs[a] - 1)->masterPageName()], 0);
                }
                if (usingGUI)
                {
                        progressDialog->setOverallTotalSteps(pageNsMpa.count() + pageNs.size());
                        progressDialog->setTotalSteps("EMP", pageNsMpa.count());
                        progressDialog->setTotalSteps("EP", pageNs.size());
                        progressDialog->setOverallProgress(0);
                        progressDialog->setProgress("EMP", 0);
                        progressDialog->setProgress("EP", 0);
                }
                for (int ap = 0; ap < doc.MasterPages.count() && !abortExport; ++ap)
                {
                        if (doc.MasterItems.count() != 0)
                        {
                                if (pageNsMpa.contains(ap))
                                {
                                        qApp->processEvents();
                                        if (!PDF_TemplatePage(doc.MasterPages.at(ap)))
                                                error = abortExport = true;
                                        ++pc_exportmasterpages;
                                }
                        }
                        if (usingGUI)
                        {
                                progressDialog->setProgress("EMP", pc_exportmasterpages);
                                progressDialog->setOverallProgress(pc_exportmasterpages+pc_exportpages);
                        }
                }
                for (uint a = 0; a < pageNs.size() && !abortExport; ++a)
                {
                        if (Options.Thumbnails)
                                thumb = thumbs[pageNs[a]];
                        qApp->processEvents();
                        if (abortExport) break;

                        PDF_Begin_Page(doc.DocPages.at(pageNs[a]-1), thumb);
                        qApp->processEvents();
                        if (abortExport) break;

                        if (!PDF_ProcessPage(doc.DocPages.at(pageNs[a]-1), pageNs[a]-1, Options.doClip))
                                error = abortExport = true;
                        qApp->processEvents();
                        if (abortExport) break;

                        PDF_End_Page();
                        pc_exportpages++;
                        if (usingGUI)
                        {
                                progressDialog->setProgress("EP", pc_exportpages);
                                progressDialog->setOverallProgress(pc_exportmasterpages+pc_exportpages);
                        }
                }
                ret = true;//Even when aborting we return true. Don't want that "couldnt write msg"
                if (!abortExport)
                {
                        if (PDF_IsPDFX(Options.Version))
                                ret = PDF_End_Doc(ScCore->PrinterProfiles[Options.PrintProf]);
                        else
                                ret = PDF_End_Doc();
                }
                else
                        closeAndCleanup();
        }
        if (usingGUI)
                progressDialog->close();
        return (ret && !error);
}

const QString& PDFLibCore::errorMessage() const
{
        return ErrorMessage;
}

bool PDFLibCore::exportAborted() const
{
        return abortExport;
}

//#define StartObj(n) writer.startObj((n))
#define PutDoc(s) writer.write(s)
//#define newObject() writer.newObject()


//void PDFLibCore::StartObj(PdfId nr)
//{
//      for (int i=XRef.size(); i < nr; ++i)
//              XRef.append(0);
//      XRef[nr-1] = bytesWritten();
//      PutDoc(Pdf::toPdf(nr) + " 0 obj\n");
//}

//// Encode a string for inclusion in a
//// PDF (literal) .
//static QByteArray PDFEncode(const QString & in)
//{
//      QByteArray tmp("");
//      for (int d = 0; d < in.length(); ++d)
//      {
//              QChar cc(in.at(d));
//              if ((cc == '(') || (cc == ')') || (cc == '\\'))
//                      tmp += '\\';
//              else if ((cc == '\r') || (cc == '\n'))
//              {
//                      tmp += (cc == '\r') ? "\\r" : "\\n";
//                      continue;
//              }
//              tmp += cc;
//      }
//      return tmp;
//}

static QByteArray blendMode(int code)
{
        switch (code)
        {
                case 0:
                        return("Normal");
                        break;
                case 1:
                        return("Darken");
                        break;
                case 2:
                        return("Lighten");
                        break;
                case 3:
                        return("Multiply");
                        break;
                case 4:
                        return("Screen");
                        break;
                case 5:
                        return("Overlay");
                        break;
                case 6:
                        return("HardLight");
                        break;
                case 7:
                        return("SoftLight");
                        break;
                case 8:
                        return("Difference");
                        break;
                case 9:
                        return("Exclusion");
                        break;
                case 10:
                        return("ColorDodge");
                        break;
                case 11:
                        return("ColorBurn");
                        break;
                case 12:
                        return("Hue");
                        break;
                case 13:
                        return("Saturation");
                        break;
                case 14:
                        return("Color");
                        break;
                case 15:
                        return("Luminosity");
                        break;
                default:
                        return "";
        }
}
//
//QByteArray PDFLibCore::EncodeUTF16(const QString &in)
//{
//      QString tmp = in;
//      QByteArray cres = ucs2Codec->fromUnicode( tmp );
//#ifndef WORDS_BIGENDIAN
//      // on little endian systems we need to swap bytes:
//      uchar sw;
//      for (int d = 0; d < cres.size() - 1; d += 2)
//      {
//              sw = cres[d];
//              cres[d] = cres[d+1];
//              cres[d+1] = sw;
//      }
//#endif
//      return cres;
//}

QByteArray PDFLibCore::EncStream(const QByteArray & in, PdfId ObjNum)
{
        if (in.length() < 1)
                return QByteArray();
        if (!Options.Encrypt)
                return in;
        return writer.encryptBytes(in, ObjNum);
//      rc4_context_t rc4;
//      QByteArray tmp(in);
//      QByteArray us(tmp.length(), ' ');
//      QByteArray ou(tmp.length(), ' ');
//      for (int a = 0; a < tmp.length(); ++a)
//              us[a] = (tmp.at(a));
//      QByteArray step1 = writer.ComputeRC4Key(ObjNum);
//      rc4_init(&rc4, reinterpret_cast<uchar*>(step1.data()), qMin(KeyLen+5, 16));
//      rc4_encrypt(&rc4, reinterpret_cast<uchar*>(us.data()), reinterpret_cast<uchar*>(ou.data()), tmp.length());
//      QString uk = "";
//      for (int cl = 0; cl < tmp.length(); ++cl)
//              uk += QChar(ou[cl]);
//      return ou;
}

QByteArray PDFLibCore::EncString(const QByteArray & in, PdfId ObjNum)
{
        if (in.length() < 1)
                return "<>";
        if (!Options.Encrypt)
        {
                //tmp = "(" + PDFEncode(in) + ")";
                return Pdf::toLiteralString(in);
        }
       
//      rc4_context_t rc4;
//      QByteArray us(in.length(), ' ');
//      QByteArray ou(in.length(), ' ');
//      for (int a = 0; a < in.length(); ++a)
//              us[a] = static_cast<uchar>(QChar(in.at(a)).cell());
//      QByteArray step1 = writer.ComputeRC4Key(ObjNum);
//      rc4_init(&rc4, reinterpret_cast<uchar*>(step1.data()), qMin(KeyLen+5, 16));
//      rc4_encrypt(&rc4, reinterpret_cast<uchar*>(us.data()), reinterpret_cast<uchar*>(ou.data()), in.length());
//      QString uk = "";
//      for (int cl = 0; cl < in.length(); ++cl)
//              uk += QChar(ou[cl]);
       
        return Pdf::toHexString(writer.encryptBytes(in, ObjNum));
}

QByteArray PDFLibCore::EncStringUTF16(const QString & in, PdfId ObjNum)
{
        if (in.length() < 1)
                return "<>";
        if (!Options.Encrypt)
        {
                QByteArray us = Pdf::toUTF16(in);
//              QString uk = "";
//              for (int cl = 0; cl < us.size(); ++cl)
//                      uk += QChar(us[cl]);
                return Pdf::toHexString(us);
        }
//      rc4_context_t rc4;
        QByteArray us = Pdf::toUTF16(in);
//      QByteArray ou(us.size(), ' ');
//      QByteArray step1 = writer.ComputeRC4Key(ObjNum);
//      rc4_init(&rc4, reinterpret_cast<uchar*>(step1.data()), qMin(KeyLen+5, 16));
//      rc4_encrypt(&rc4, reinterpret_cast<uchar*>(us.data()), reinterpret_cast<uchar*>(ou.data()), ou.size());
//      QString uk = "";
//      for (int cl = 0; cl < ou.size(); ++cl)
//              uk += QChar(ou[cl]);
        QByteArray tmp = Pdf::toHexString(writer.encryptBytes(us, ObjNum));
        return tmp;
}

bool PDFLibCore::EncodeArrayToStream(const QByteArray& in, PdfId ObjNum)
{
        if (in.size() < 1)
                return true;
        bool succeed = false;
        if (Options.Encrypt)
        {
                ScStreamFilter* rc4Encode = writer.openStreamFilter(true, ObjNum);
                if (rc4Encode->openFilter())
                {
                        succeed  = rc4Encode->writeData(in.data(), in.size());
                        succeed &= rc4Encode->closeFilter();
                }
                delete rc4Encode;
        }
        else
                writer.write(in);
        return (writer.getOutStream().status() == QDataStream::Ok);
}

int PDFLibCore::WriteImageToStream(ScImage& image, PdfId ObjNum, ColorSpaceEnum format, bool precal)
{
        bool fromCmyk, succeed = false;
        int  bytesWritten = 0;

        ScStreamFilter* rc4Encode = writer.openStreamFilter(Options.Encrypt, ObjNum);
        if (rc4Encode->openFilter())
        {
                switch (format)
                {
                        case ColorSpaceMonochrome :
                                fromCmyk = !Options.UseRGB && !Options.isGrayscale && !(doc.HasCMS && Options.UseProfiles2);
                                succeed = image.writeMonochromeDataToFilter(rc4Encode, fromCmyk); break;
                        case ColorSpaceGray :
                                succeed = image.writeGrayDataToFilter(rc4Encode, precal); break;
                        case ColorSpaceCMYK :
                                succeed = image.writeCMYKDataToFilter(rc4Encode); break;
                        default :
                                succeed = image.writeRGBDataToFilter(rc4Encode); break;
                }
                succeed &= rc4Encode->closeFilter();
                bytesWritten = rc4Encode->writtenToStream();
                delete rc4Encode;
        }
        return (succeed ? bytesWritten : 0);
}

int PDFLibCore::WriteJPEGImageToStream(ScImage& image, const QString& fn, PdfId ObjNum, int quality, ColorSpaceEnum format,
                                                                                 bool sameFile, bool precal)
{
        bool succeed = true;
        int  bytesWritten = 0;
        QFileInfo fInfo(fn);
        QString   ext = fInfo.suffix().toLower();
        QString   jpgFileName, tmpFile;
        if (extensionIndicatesJPEG(ext) && sameFile)
                jpgFileName = fn;
        else
        {
                tmpFile  = QDir::toNativeSeparators(ScPaths::tempFileDir() + "sc.jpg");
                if (format == ColorSpaceGray && (!precal))
                        image.convertToGray();
                if (image.convert2JPG(tmpFile, quality, format == ColorSpaceCMYK, format == ColorSpaceGray))
                        jpgFileName = tmpFile;
        }
        if (jpgFileName.isEmpty())
                return 0;
        if (Options.Encrypt)
        {
                succeed = false;
                ScStreamFilter* rc4Encode = writer.openStreamFilter(true, ObjNum);
                if (rc4Encode->openFilter())
                {
                        succeed  = copyFileToFilter(jpgFileName, *rc4Encode);
                        succeed &= rc4Encode->closeFilter();
                        bytesWritten = rc4Encode->writtenToStream();
                }
                delete rc4Encode;
        }
        else
        {
                succeed &= copyFileToStream(jpgFileName, writer.getOutStream());
                QFileInfo jpgInfo(jpgFileName);
                bytesWritten = jpgInfo.size();
        }
        if (!tmpFile.isEmpty() && QFile::exists(tmpFile))
                QFile::remove(tmpFile);
        return (succeed ? bytesWritten : 0);
}

int PDFLibCore::WriteFlateImageToStream(ScImage& image, PdfId ObjNum, ColorSpaceEnum format, bool precal)
{
        bool fromCmyk, succeed = false;
        int  bytesWritten = 0;
        ScStreamFilter* rc4Encode = writer.openStreamFilter(Options.Encrypt, ObjNum);
        ScFlateEncodeFilter flateEncode(rc4Encode);
        if (flateEncode.openFilter())
        {
                switch (format)
                {
                        case ColorSpaceMonochrome :
                                fromCmyk = !Options.UseRGB && !Options.isGrayscale && !(doc.HasCMS && Options.UseProfiles2);
                                succeed = image.writeMonochromeDataToFilter(&flateEncode, fromCmyk); break;
                        case ColorSpaceGray :
                                succeed = image.writeGrayDataToFilter(&flateEncode, precal); break;
                        case ColorSpaceCMYK :
                                succeed = image.writeCMYKDataToFilter(&flateEncode); break;
                        default :
                                succeed = image.writeRGBDataToFilter(&flateEncode); break;
                }
                succeed &= flateEncode.closeFilter();
                bytesWritten = flateEncode.writtenToStream();
        }
        delete rc4Encode;
        return (succeed ? bytesWritten : 0);
}

bool PDFLibCore::PDF_Begin_Doc(const QString& fn, SCFonts &AllFonts, const QMap<QString, QMap<uint, QString> >& DocFonts, BookmarkView* vi)
{
        if (!writer.open(fn))
                return false;
       
        inPattern = 0;
        Bvie = vi;
        BookMinUse = false;
        UsedFontsP.clear();
        UsedFontsF.clear();
       
        writer.writeHeader(Options.Version);

        PDF_Begin_Catalog();
        PDF_Begin_MetadataAndEncrypt();
        PDF_Begin_WriteUsedFonts(AllFonts, PDF_Begin_FindUsedFonts(AllFonts, DocFonts));
        PDF_Begin_Colors();
        PDF_Begin_Layers();
       
        return true;
}

void PDFLibCore::PDF_Begin_Catalog()
{
        writer.startObj(writer.CatalogObj);
        PutDoc("<<\n/Type /Catalog\n");
        PutDoc("/Pages " + Pdf::toObjRef(writer.PagesObj) + "\n");
        if (!PDF_IsPDFX())
        {
                if (doc.useAcroFormFields())
                {
                        writer.AcroFormObj = writer.newObject();
                        PutDoc("/AcroForm " + Pdf::toObjRef(writer.AcroFormObj) + "\n");
                }
                if (doc.useAnnotations())
                {
                        writer.DestsObj = writer.newObject();
                        PutDoc("/Dests " + Pdf::toObjRef(writer.DestsObj) + "\n");
                }
                if (doc.JavaScripts.count() > 0)
                {
                        writer.NamesObj = writer.newObject();
                        PutDoc("/Names " + Pdf::toObjRef(writer.NamesObj) + "\n");
                }
        }
        if (Options.Bookmarks)
        {
                writer.OutlinesObj = writer.newObject();
                PutDoc("/Outlines " + Pdf::toObjRef(writer.OutlinesObj) + "\n");
        }
        if (Options.Articles)
        {
                writer.ThreadsObj = writer.newObject();
                PutDoc("/Threads " + Pdf::toObjRef(writer.ThreadsObj) + "\n");
        }
        if (Options.exportsLayers())
        {
                writer.OCPropertiesObj = writer.newObject();
                PutDoc("/OCProperties " + Pdf::toObjRef(writer.OCPropertiesObj) + "\n");
        }
        if (PDF_IsPDFX())
        {
                writer.OutputIntentObj = writer.newObject();
                PutDoc("/OutputIntents [ " + Pdf::toObjRef(writer.OutputIntentObj) + " ]\n");
        }
        if ((Options.Version == PDFVersion::PDF_X4))
        {
                writer.MetaDataObj = writer.newObject();
                PutDoc("/Metadata " + Pdf::toObjRef(writer.MetaDataObj) + "\n");
        }
        PutDoc("/PageLayout ");
        switch (Options.PageLayout)
        {
                case PDFOptions::SinglePage:
                        PutDoc("/SinglePage\n");
                        break;
                case PDFOptions::OneColumn:
                        PutDoc("/OneColumn\n");
                        break;
                case PDFOptions::TwoColumnLeft:
                        PutDoc("/TwoColumnLeft\n");
                        break;
                case PDFOptions::TwoColumnRight:
                        PutDoc("/TwoColumnRight\n");
                        break;
        }
        if (Options.displayBookmarks)
                PutDoc("/PageMode /UseOutlines\n");
        else if (Options.displayFullscreen)
                PutDoc("/PageMode /FullScreen\n");
        else if (Options.displayThumbs)
                PutDoc("/PageMode /UseThumbs\n");
        else if ((Options.Version == PDFVersion::PDF_15 || Options.Version == PDFVersion::PDF_16) && (Options.displayLayers))
                PutDoc("/PageMode /UseOC\n");
        if (!Options.openAction.isEmpty())
        {
                writer.OpenActionObj = writer.newObject();
                PutDoc("/OpenAction << /S /JavaScript /JS " + Pdf::toPdf(writer.OpenActionObj) + " 0 R >>\n");
        }
        QDateTime dt = QDateTime::currentDateTimeUtc();
        Datum = Pdf::toDateString(dt);
//    "D:";
//      tmp.sprintf("%4d", d.year());
//      tmp.replace(QRegExp(" "), "0");
//      Datum += tmp;
//      tmp.sprintf("%2d", d.month());
//      tmp.replace(QRegExp(" "), "0");
//      Datum += tmp;
//      tmp.sprintf("%2d", d.day());
//      tmp.replace(QRegExp(" "), "0");
//      Datum += tmp;
//      tmp = dt.time().toString();
//      tmp.replace(QRegExp(":"), "");
//      Datum += tmp;
//      Datum += "Z";

        // only include XMP to PDF/X-4 at the moment, could easily be extended to include it to any PDF
        if (Options.Version == PDFVersion::PDF_X4)
                generateXMP(dt.toString("yyyy-MM-ddThh:mm:ssZ"));

/* The following code makes the resulting PDF "Reader enabled" in Acrobat Reader 8
   but sadly it doesn't work with newer version, because its based on a bug in AR 8
        PutDoc("/Perms\n");
        PutDoc("<<\n");
        PutDoc("/UR3\n");
        PutDoc("<<\n");
        PutDoc("/M (" + Datum + ")\n");
        PutDoc("/Name (Scribus " + QString(VERSION) + ")\n");
        PutDoc("/Reference [\n");
        PutDoc("<<\n");
        PutDoc("/TransformParams\n");
        PutDoc("<<\n");
        PutDoc("/Type /TransformParams\n");
        PutDoc("/V /2.2\n");
        PutDoc("/Document [/FullSave]\n");
        PutDoc("/Annots [/Create/Delete/Modify/Copy/Import/Export]\n");
        PutDoc("/Form [/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate]\n");
        PutDoc("/Signature [/Modify]\n");
        PutDoc(">>\n");
        PutDoc("/TransformMethod /UR3\n");
        PutDoc("/Type /SigRef\n");
        PutDoc(">>\n");
        PutDoc("]\n");
        PutDoc("/Type /Sig\n");
        PutDoc(">>\n");
        PutDoc(">>\n");
*/


        PutDoc("/ViewerPreferences\n<<\n/PageDirection ");
        PutDoc( Options.Binding == 0 ? "/L2R\n" : "/R2L\n");
        if (Options.hideToolBar)
                PutDoc("/HideToolbar true\n");
        if (Options.hideMenuBar)
                PutDoc("/HideMenubar true\n");
        if (Options.fitWindow)
                PutDoc("/FitWindow true\n");
        PutDoc(" >>\n>>");
        writer.endObj(writer.CatalogObj);
}

void PDFLibCore::PDF_Begin_MetadataAndEncrypt()
{
        QString IDg(Datum);
        IDg += Options.fileName;
        IDg += "Scribus " + QString(VERSION);
        IDg += "Scribus PDF Library " + QString(VERSION);
        IDg += doc.documentInfo().title();
        IDg += doc.documentInfo().author();
        IDg += "/False";
        writer.setFileId(Pdf::toPdfDocEncoding(IDg));
        if (Options.Encrypt)
        {
                writer.setEncryption(Options.Version.supports128BitsEncryption(), Pdf::toPdfDocEncoding(Options.PassOwner), Pdf::toPdfDocEncoding(Options.PassUser), Options.Permissions);
        }
        writer.startObj(writer.InfoObj);
        PutDoc("<<\n/Creator " + EncString(QByteArray("Scribus ") + VERSION, writer.InfoObj) + "\n");
        PutDoc("/Producer " + EncString(QByteArray("Scribus PDF Library ") + VERSION, writer.InfoObj) + "\n");
        QString docTitle = doc.documentInfo().title();
        if ((PDF_IsPDFX()) && (docTitle.isEmpty()))
                PutDoc("/Title " + EncStringUTF16(doc.documentFileName(), writer.InfoObj) + "\n");
        else
                PutDoc("/Title " + EncStringUTF16(doc.documentInfo().title(), writer.InfoObj) + "\n");
        PutDoc("/Author " + EncStringUTF16(doc.documentInfo().author(), writer.InfoObj) + "\n");
        PutDoc("/Subject " + EncStringUTF16(doc.documentInfo().subject(), writer.InfoObj) + "\n");
        PutDoc("/Keywords " + EncStringUTF16(doc.documentInfo().keywords(), writer.InfoObj) + "\n");
        PutDoc("/CreationDate " + EncString(Datum, writer.InfoObj) + "\n");
        PutDoc("/ModDate " + EncString(Datum, writer.InfoObj) + "\n");
        if (Options.Version == PDFVersion::PDF_X1a)
        {
                PutDoc("/GTS_PDFXVersion (PDF/X-1:2001)\n");
                PutDoc("/GTS_PDFXConformance (PDF/X-1a:2001)\n");
        }
        if (Options.Version == PDFVersion::PDF_X3)
                PutDoc("/GTS_PDFXVersion (PDF/X-3:2002)\n");
        if (Options.Version == PDFVersion::PDF_X4)
                PutDoc("/GTS_PDFXVersion (PDF/X-4)\n");
        PutDoc("/Trapped /False\n>>");
        writer.endObj(writer.InfoObj);
}

QMap<QString, QMap<uint, QString> >
PDFLibCore::PDF_Begin_FindUsedFonts(SCFonts &AllFonts, const QMap<QString, QMap<uint, QString> >& DocFonts)
{
        QMap<QString, QMap<uint, QString> > usedFonts;
        PageItem* pgit;
        QMap<int, QByteArray> ind2PDFabr;
        const QByteArray tmpf[] = {"/Courier", "/Courier-Bold", "/Courier-Oblique", "/Courier-BoldOblique",
                                                                                                "/Helvetica", "/Helvetica-Bold", "/Helvetica-Oblique", "/Helvetica-BoldOblique",
                                                                                                "/Times-Roman", "/Times-Bold", "/Times-Italic", "/Times-BoldItalic",
                                                                                                "/ZapfDingbats", "/Symbol"};
        size_t ar = sizeof(tmpf) / sizeof(*tmpf);
        for (uint ax = 0; ax < ar; ++ax)
                ind2PDFabr[ax] = tmpf[ax];

        QList<PageItem*> allItems = doc.FrameItems.values();
        while (allItems.count() > 0)
        {
                pgit = allItems.takeFirst();
                if (pgit->isGroup() || pgit->isTable())
                {
                        allItems = pgit->getChildren() + allItems;
                        continue;
                }
                if ((pgit->itemType() == PageItem::TextFrame) || (pgit->itemType() == PageItem::PathText))
                {
                        if (pgit->isAnnotation())
                        {
                                int annotType  = pgit->annotation().Type();
                                bool mustEmbed = ((annotType >= Annotation::Button) && (annotType <= Annotation::Listbox) && (annotType != Annotation::Checkbox));
                                if ((pgit->annotation().Type() == Annotation::Checkbox) || (pgit->annotation().Type() == Annotation::RadioButton))
                                        StdFonts.insert("/ZapfDingbats", "");
                                if (pgit->itemText.length() > 0 || mustEmbed)
                                {
                                        if (Options.Version < PDFVersion::PDF_14)
                                                StdFonts.insert(ind2PDFabr[pgit->annotation().Font()], "");
                                        QString replacementName = pgit->itemText.defaultStyle().charStyle().font().replacementName();
                                        usedFonts.insert(replacementName, DocFonts[replacementName]);
                                }
                        }
                        uint start = pgit->isTextFrame() ? (uint) pgit->firstInFrame() : 0;
                        uint stop  = pgit->isTextFrame() ? (uint) pgit->lastInFrame() + 1 : (uint) pgit->itemText.length();
                        for (uint e = start; e < stop; ++e)
                        {
                                QString replacementName = pgit->itemText.charStyle(e).font().replacementName();
                                usedFonts.insert(replacementName, DocFonts[replacementName]);
                        }
                }
        }

        allItems = doc.MasterItems;
        while (allItems.count() > 0)
        {
                pgit = allItems.takeFirst();
                if (pgit->isGroup() || pgit->isTable())
                {
                        allItems = pgit->getChildren() + allItems;
                        continue;
                }
                if ((pgit->itemType() == PageItem::TextFrame) || (pgit->itemType() == PageItem::PathText))
                {
                        if (pgit->isAnnotation())
                        {
                                int annotType  = pgit->annotation().Type();
                                bool mustEmbed = ((annotType >= Annotation::Button) && (annotType <= Annotation::Listbox) && (annotType != Annotation::Checkbox));
                                if ((pgit->annotation().Type() == Annotation::Checkbox) || (pgit->annotation().Type() == Annotation::RadioButton))
                                        StdFonts.insert("/ZapfDingbats", "");
                                if (pgit->itemText.length() > 0 || mustEmbed)
                                {
                                        if (Options.Version < PDFVersion::PDF_14)
                                                StdFonts.insert(ind2PDFabr[pgit->annotation().Font()], "");
                                        QString replacementName = pgit->itemText.defaultStyle().charStyle().font().replacementName();
                                        usedFonts.insert(replacementName, DocFonts[replacementName]);
                                }
                        }
                        uint start = pgit->isTextFrame() ? (uint) pgit->firstInFrame() : 0;
                        uint stop  = pgit->isTextFrame() ? (uint) pgit->lastInFrame() + 1 : (uint) pgit->itemText.length();
                        for (uint e = start; e < stop; ++e)
                        {
                                QString replacementName = pgit->itemText.charStyle(e).font().replacementName();
                                usedFonts.insert(replacementName, DocFonts[replacementName]);
                        }
                }
        }

        allItems = *(doc.Items);
        while (allItems.count() > 0)
        {
                pgit = allItems.takeFirst();
                if (pgit->isGroup() || pgit->isTable())
                {
                        allItems = pgit->getChildren() + allItems;
                        continue;
                }
                if ((pgit->itemType() == PageItem::TextFrame) || (pgit->itemType() == PageItem::PathText))
                {
                        if (pgit->isAnnotation())
                        {
                                int annotType  = pgit->annotation().Type();
                                bool mustEmbed = ((annotType >= Annotation::Button) && (annotType <= Annotation::Listbox) && (annotType != Annotation::Checkbox));
                                if ((pgit->annotation().Type() == Annotation::Checkbox) || (pgit->annotation().Type() == Annotation::RadioButton))
                                        StdFonts.insert("/ZapfDingbats", "");
                                if (pgit->itemText.length() > 0 || mustEmbed)
                                {
                                        if (Options.Version < PDFVersion::PDF_14)
                                                StdFonts.insert(ind2PDFabr[pgit->annotation().Font()], "");
                                        QString replacementName = pgit->itemText.defaultStyle().charStyle().font().replacementName();
                                        usedFonts.insert(replacementName, DocFonts[replacementName]);
                                }
                        }
                        uint start = pgit->isTextFrame() ? (uint) pgit->firstInFrame() : 0;
                        uint stop  = pgit->isTextFrame() ? (uint) pgit->lastInFrame() + 1 : (uint) pgit->itemText.length();
                        for (uint e = start; e < stop; ++e)
                        {
                                QString replacementName = pgit->itemText.charStyle(e).font().replacementName();
                                usedFonts.insert(replacementName, DocFonts[replacementName]);
                        }
                }
        }
/*      if (Options.docInfoMarks)
        {
                StdFonts.insert("/Helvetica", "");
        } */

        QStringList patterns = doc.getPatternDependencyList(doc.getUsedPatterns());
        for (int c = 0; c < patterns.count(); ++c)
        {
                ScPattern pa = doc.docPatterns[patterns[c]];
                allItems = pa.items;
                while (allItems.count() > 0)
                {
                        pgit = allItems.takeFirst();
                        if (pgit->isGroup() || pgit->isTable())
                        {
                                allItems = pgit->getChildren() + allItems;
                                continue;
                        }
                        if ((pgit->itemType() == PageItem::TextFrame) || (pgit->itemType() == PageItem::PathText))
                        {
                                if (pgit->isAnnotation())
                                {
                                        if ((pgit->annotation().Type() == Annotation::Checkbox) || (pgit->annotation().Type() == Annotation::RadioButton))
                                                StdFonts.insert("/ZapfDingbats", "");
                                        if (pgit->itemText.length() > 0)
                                        {
                                                if (Options.Version < PDFVersion::PDF_14)
                                                        StdFonts.insert(ind2PDFabr[pgit->annotation().Font()], "");
                                                QString replacementName = pgit->itemText.defaultStyle().charStyle().font().replacementName();
                                                usedFonts.insert(replacementName, DocFonts[replacementName]);
                                        }
                                }
                                uint start = pgit->isTextFrame() ? (uint) pgit->firstInFrame() : 0;
                                uint stop  = pgit->isTextFrame() ? (uint) pgit->lastInFrame() + 1 : (uint) pgit->itemText.length();
                                for (uint e = start; e < stop; ++e)
                                {
                                        QString replacementName = pgit->itemText.charStyle(e).font().replacementName();
                                        usedFonts.insert(replacementName, DocFonts[replacementName]);
                                }
                        }
                }
        }
        return usedFonts;
}

static QByteArray sanitizeFontName(QString fn)
{
        return Pdf::toPdfDocEncoding(fn.replace( QRegExp("[\\s\\/\\{\\[\\]\\}\\<\\>\\(\\)\\%]"), "_"));
}

static QList<Pdf::Resource> asColorSpace(const QList<PdfICCD>& iccCSlist)
{
        QList<Pdf::Resource> result;
        for (const Pdf::Resource& r : iccCSlist)
                result.append(r);
        return result;
}

static QList<Pdf::Resource> asColorSpace(const QList<PdfSpotC>& spotMapValues)
{
        QList<Pdf::Resource> result;
        for (const Pdf::Resource& r : spotMapValues)
                result.append(r);
        return result;
}

void PDFLibCore::PDF_WriteStandardFonts()
{
        int a = 0;
        QMap<QString, QString>::Iterator itStd;
        for (itStd = StdFonts.begin(); itStd != StdFonts.end(); ++itStd)
        {
                PdfId fontObject = writer.newObject();
                writer.startObj(fontObject);
                PutDoc("<<\n/Type /Font\n/Subtype /Type1\n");
                PutDoc("/Name /FoStd" + Pdf::toPdf(a) + "\n");
                PutDoc("/BaseFont " + Pdf::toPdfDocEncoding(itStd.key()) + "\n");
                if (itStd.key() != "/ZapfDingbats")
                {
                        PutDoc("/Encoding << \n");
                        PutDoc("/Differences [ \n");
                        PutDoc("24 /breve /caron /circumflex /dotaccent /hungarumlaut /ogonek /ring /tilde\n");
                        PutDoc("39 /quotesingle 96 /grave 128 /bullet /dagger /daggerdbl /ellipsis /emdash /endash /florin /fraction /guilsinglleft /guilsinglright\n");
                        PutDoc("/minus /perthousand /quotedblbase /quotedblleft /quotedblright /quoteleft /quoteright /quotesinglbase /trademark /fi /fl /Lslash /OE /Scaron\n");
                        PutDoc("/Ydieresis /Zcaron /dotlessi /lslash /oe /scaron /zcaron 160 /Euro 164 /currency 166 /brokenbar 168 /dieresis /copyright /ordfeminine 172 /logicalnot\n");
                        PutDoc("/.notdef /registered /macron /degree /plusminus /twosuperior /threesuperior /acute /mu 183 /periodcentered /cedilla /onesuperior /ordmasculine\n");
                        PutDoc("188 /onequarter /onehalf /threequarters 192 /Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla /Egrave /Eacute /Ecircumflex\n");
                        PutDoc("/Edieresis /Igrave /Iacute /Icircumflex /Idieresis /Eth /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply /Oslash\n");
                        PutDoc("/Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn /germandbls /agrave /aacute /acircumflex /atilde /adieresis /aring /ae /ccedilla\n");
                        PutDoc("/egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex /idieresis /eth /ntilde /ograve /oacute /ocircumflex /otilde /odieresis\n");
                        PutDoc("/divide /oslash /ugrave /uacute /ucircumflex /udieresis /yacute /thorn /ydieresis\n");
                        PutDoc("] >>\n");
                }
                PutDoc(">>");
                writer.endObj(fontObject);
                pageData.FObjects["FoStd" + Pdf::toPdf(a)] = fontObject;
                itStd.value() = "FoStd" + Pdf::toPdf(a);
                a++;
        }
}


PdfFont PDFLibCore::PDF_WriteType3Font(const QByteArray& name, ScFace& face, const QMap<uint, QString>& usedGlyphs)
{
        PdfFont result;
        result.name = Pdf::toName(name);
        result.usage = Used_in_Content;
        result.method = Use_Type3;
        result.encoding = Encode_256;

        // TrueType font rasterizers use non-zero winding file rule,
        // vs even-odd for current Postscript font rasterizers
        // Note: OTF CFF2 will also use non-zero winding rule, so
        // change code below when adding CCF2 support to Scribus
        // ref: https://www.microsoft.com/typography/OTSpec/cff2.htm
        bool useNonZeroRule = (face.type() == ScFace::TTF);

        uint SubFonts = 0;
        int glyphCount = 0;
        double minx =  std::numeric_limits<double>::max();
        double miny =  std::numeric_limits<double>::max();
        double maxx = -std::numeric_limits<double>::max();
        double maxy = -std::numeric_limits<double>::max();

        QList<uint> glyphWidths;
        QList<QByteArray> charProcs;
        QStringList toUnicodeMaps;
        QList<int> toUnicodeMapsCount;
        QString toUnicodeMap;
        int toUnicodeMapCounter = 0;
        QByteArray encoding = "<< /Type /Encoding\n/Differences [ 0\n";
        QByteArray fon;
        QMap<uint, uint> glyphMapping;

        ScFace::FaceEncoding gl;
        face.glyphNames(gl);

        for (auto ig = usedGlyphs.cbegin(); ig != usedGlyphs.cend(); ++ig)
        {
                FPoint np, np1, np2;
                bool nPath = true;
                fon.resize(0);

                uint gid = ig.key();
                FPointArray glyphOutline = face.glyphOutline(gid);
                if (glyphOutline.size() > 3)
                {
                        FPointArray gly = glyphOutline;
                        QTransform mat;
                        mat.scale(100.0, -100.0);
                        gly.map(mat);
                        gly.translate(0, 1000);
                        for (int poi = 0; poi < gly.size() - 3; poi += 4)
                        {
                                if (gly.isMarker(poi))
                                {
                                        fon += "h\n";
                                        nPath = true;
                                        continue;
                                }
                                if (nPath)
                                {
                                        np = gly.point(poi);
                                        fon += FToStr(np.x()) + " " + FToStr(np.y()) + " m\n";
                                        nPath = false;
                                }
                                np = gly.point(poi + 1);
                                np1 = gly.point(poi + 3);
                                np2 = gly.point(poi + 2);
                                fon += FToStr(np.x()) + " " + FToStr(np.y()) + " " + FToStr(np1.x()) + " " + FToStr(np1.y()) + " " + FToStr(np2.x()) + " " + FToStr(np2.y()) + " c\n";
                        }
                        fon += useNonZeroRule? "h f\n" : "h f*\n";
                        np = getMinClipF(&gly);
                        np1 = getMaxClipF(&gly);
                }
                else
                {
                        // 13654: make evince emit warning about "Syntax Error: No current point in closepath"
                        // PDF specification is not quite clear about that, but a single "h" operator might not
                        // be a valid way to make an empty path
                        // fon = "h";
                        np = FPoint(0, 0);
                        np1 = FPoint(0, 0);
                }
                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");
                minx = qMin(minx, np.x());
                miny = qMin(miny, np.y());
                maxx = qMax(maxx, np1.x());
                maxy = qMax(maxy, np1.y());
                glyphWidths.append(qRound(np1.x()));

                PdfId charProcObject = writer.newObject();
                writer.startObj(charProcObject);
                if (Options.Compress)
                        fon = CompressArray(fon);
                PutDoc("<< /Length " + Pdf::toPdf(fon.length() + 1));
                if (Options.Compress)
                        PutDoc("\n/Filter /FlateDecode");
                PutDoc("\n>>\nstream\n" + EncStream(fon, charProcObject) + "\nendstream");
                writer.endObj(charProcObject);

                // #15449 : in some cases we cannot retrieve glyph names for all glyphs we need
                // using ScFace's glyphNames(), so generate custom glyph names using glyph index.
                // With Type 3 fonts we have more flexibility than with other fonts.
                QString glGlyphName, glToUnicode;
                auto  glyphIt = gl.constFind(gid);
                if ((glyphIt != gl.constEnd()) && (glyphIt->charcode > 0))
                        glToUnicode = QString::asprintf("%04X", glyphIt->charcode);
                if (glToUnicode.isEmpty() && usedGlyphs.contains(gid))
                {
                        QString uniStr = usedGlyphs.value(gid);
                        for (int i = 0; i < uniStr.length(); ++i)
                                glToUnicode += QString::asprintf("%04X", uniStr.at(i).unicode());
                }
                if (glToUnicode.isEmpty())
                        glToUnicode = QString("0000");
                glGlyphName = "gly" + QString::asprintf("%04X", gid);

                charProcs.append(Pdf::toName(glGlyphName) + " " + Pdf::toPdf(charProcObject) + " 0 R\n");
                encoding += Pdf::toName(glGlyphName) + " ";
                glyphMapping.insert(gid, glyphCount + SubFonts * 256);

                QString tmp = QString::asprintf("%02X", glyphCount);
                toUnicodeMap += "<" + Pdf::toAscii(tmp) + "> <" + Pdf::toAscii(glToUnicode) + ">\n";
                toUnicodeMapCounter++;
                if (toUnicodeMapCounter == 100)
                {
                        toUnicodeMaps.append(toUnicodeMap);
                        toUnicodeMapsCount.append(toUnicodeMapCounter);
                        toUnicodeMap.clear();
                        toUnicodeMapCounter = 0;
                }

                glyphCount++;
                int glyphsLeft = usedGlyphs.count() - SubFonts * 256;
                if ((glyphCount > 255) || (glyphCount == glyphsLeft))
                {
                        PdfId fontWidths = writer.newObject();
                        writer.startObj(fontWidths);
                        PutDoc("[ ");
                        for (int ww = 0; ww < glyphWidths.count(); ++ww)
                        {
                                PutDoc(Pdf::toPdf(glyphWidths[ww]) + " ");
                        }
                        PutDoc("]");
                        writer.endObj(fontWidths);
                        PdfId fontCharProcs = writer.newObject();
                        writer.startObj(fontCharProcs);
                        PutDoc("<<\n");
                        for (int ww = 0; ww < charProcs.count(); ++ww)
                        {
                                PutDoc(charProcs[ww]);
                        }
                        PutDoc(">>");
                        writer.endObj(fontCharProcs);
                        PdfId fontEncoding = writer.newObject();
                        writer.startObj(fontEncoding);
                        PutDoc(encoding);
                        PutDoc("]\n");
                        PutDoc(">>");
                        writer.endObj(fontEncoding);

                        if (toUnicodeMapCounter != 0)
                        {
                                toUnicodeMaps.append(toUnicodeMap);
                                toUnicodeMapsCount.append(toUnicodeMapCounter);
                        }

                        QByteArray toUnicodeMapStream;
                        toUnicodeMapStream += "/CIDInit /ProcSet findresource begin\n";
                        toUnicodeMapStream += "12 dict begin\n";
                        toUnicodeMapStream += "begincmap\n";
                        toUnicodeMapStream += "/CIDSystemInfo <<\n";
                        toUnicodeMapStream += "/Registry (Adobe)\n";
                        toUnicodeMapStream += "/Ordering (UCS)\n";
                        toUnicodeMapStream += "/Supplement 0\n";
                        toUnicodeMapStream += ">> def\n";
                        toUnicodeMapStream += "/CMapName /Adobe-Identity-UCS def\n";
                        toUnicodeMapStream += "/CMapType 2 def\n";
                        toUnicodeMapStream += "1 begincodespacerange\n";
                        toUnicodeMapStream += "<0000> <FFFF>\n";
                        toUnicodeMapStream += "endcodespacerange\n";
                        for (int uniC = 0; uniC < toUnicodeMaps.count(); uniC++)
                        {
                                toUnicodeMapStream += Pdf::toPdf(toUnicodeMapsCount[uniC]);
                                toUnicodeMapStream += " beginbfchar\n";
                                toUnicodeMapStream += toUnicodeMaps[uniC];
                                toUnicodeMapStream += "endbfchar\n";
                        }
                        toUnicodeMapStream += "endcmap\n";
                        toUnicodeMapStream += "CMapName currentdict /CMap defineresource pop\n";
                        toUnicodeMapStream += "end\n";
                        toUnicodeMapStream += "end\n";
                        PdfId fontToUnicode = WritePDFStream(toUnicodeMapStream);

                        PdfId font3Object = writer.newObject();
                        writer.startObj(font3Object);
                        PutDoc("<<\n/Type /Font\n/Subtype /Type3\n");
                        PutDoc("/Name /" + name + "S" + Pdf::toPdf(SubFonts) + "\n");
                        PutDoc("/FirstChar 0\n");
                        PutDoc("/LastChar " + Pdf::toPdf(glyphCount-1) + "\n");
                        PutDoc("/Widths " + Pdf::toPdf(fontWidths) + " 0 R\n");
                        PutDoc("/CharProcs " + Pdf::toPdf(fontCharProcs) + " 0 R\n");
                        PutDoc("/FontBBox [" + Pdf::toPdf(qRound(minx)) + " " + Pdf::toPdf(qRound(miny)) + " " + Pdf::toPdf(qRound(maxx)) + " " + Pdf::toPdf(qRound(maxy)) + "]\n");
                        PutDoc("/FontMatrix [0.001 0 0 0.001 0 0]\n");
                        PutDoc("/Encoding " + Pdf::toPdf(fontEncoding) + " 0 R\n");
                        PutDoc("/ToUnicode " + Pdf::toPdf(fontToUnicode) + " 0 R\n");
                        PutDoc(">>");
                        writer.endObj(font3Object);
                        pageData.FObjects[name + "S" + Pdf::toPdf(SubFonts)] = font3Object;
                        charProcs.clear();
                        glyphWidths.clear();
                        toUnicodeMaps.clear();
                        toUnicodeMapsCount.clear();
                        toUnicodeMap.clear();
                        toUnicodeMapCounter = 0;
                        glyphCount = 0;
                        ++SubFonts;
                        minx =  std::numeric_limits<double>::max();
                        miny =  std::numeric_limits<double>::max();
                        maxx = -std::numeric_limits<double>::max();
                        maxy = -std::numeric_limits<double>::max();
                        encoding = "<< /Type /Encoding\n/Differences [ 0\n";
                }
        }
        result.glyphmap = glyphMapping;
        return result;
}


PdfFont PDFLibCore::PDF_WriteGlyphsAsXForms(const QByteArray& fontName, ScFace& face, const QMap<uint, QString>& usedGlyphs)
{
        PdfFont result;
        result.name = Pdf::toName(fontName);
        result.usage = Used_in_Content;
        result.method = Use_XForm;
        result.encoding = Encode_224;

        // TrueType font rasterizers use non-zero winding file rule,
        // vs even-odd for current Postscript font rasterizers
        // Note: OTF CFF2 will also use non-zero winding rule, so
        // change code below when adding CCF2 support to Scribus
        // ref: https://www.microsoft.com/typography/OTSpec/cff2.htm
        bool useNonZeroRule = (face.type() == ScFace::TTF);

        QByteArray fon;
        for (auto ig = usedGlyphs.cbegin(); ig != usedGlyphs.cend(); ++ig)
        {
                FPoint np, np1, np2;
                bool nPath = true;
                fon.resize(0);

                uint gid = ig.key();
                FPointArray glyphOutline = face.glyphOutline(gid);
                if (glyphOutline.size() > 3)
                {
                        FPointArray gly = glyphOutline;
                        QTransform mat;
                        mat.scale(0.1, 0.1);
                        gly.map(mat);
                        for (int poi = 0; poi < gly.size() - 3; poi += 4)
                        {
                                if (gly.isMarker(poi))
                                {
                                        fon += "h\n";
                                        nPath = true;
                                        continue;
                                }
                                if (nPath)
                                {
                                        np = gly.point(poi);
                                        fon += FToStr(np.x()) + " " + FToStr(-np.y()) + " m\n";
                                        nPath = false;
                                }
                                np = gly.point(poi + 1);
                                np1 = gly.point(poi + 3);
                                np2 = gly.point(poi + 2);
                                fon += FToStr(np.x()) + " " + FToStr(-np.y()) + " " +
                                FToStr(np1.x()) + " " + FToStr(-np1.y()) + " " +
                                FToStr(np2.x()) + " " + FToStr(-np2.y()) + " c\n";
                        }
                        fon += useNonZeroRule? "h f\n" : "h f*\n";
                        np = getMinClipF(&gly);
                        np1 = getMaxClipF(&gly);
                }
                else
                {
                        // 13654: make evince emit warning about "Syntax Error: No current point in closepath"
                        // PDF specification is not quite clear about that, but a single "h" operator might not
                        // be a valid way to make an empty path
                        fon = " " /*"h"*/;
                        np = FPoint(0, 0);
                        np1 = FPoint(0, 0);
                }
                PdfId fontGlyphXForm = writer.newObject();
                writer.startObj(fontGlyphXForm);
                PutDoc("<<\n/Type /XObject\n/Subtype /Form\n/FormType 1\n");
                PutDoc("/BBox [ " + FToStr(np.x()) + " " + FToStr(-np.y()) + " " + FToStr(np1.x()) + " " + FToStr(-np1.y()) + " ]\n");
                PutDoc("/Resources << /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]\n");
                PutDoc(">>\n");
                if (Options.Compress)
                        fon = CompressArray(fon);
                PutDoc("/Length " + Pdf::toPdf(fon.length() + 1));
                if (Options.Compress)
                        PutDoc("\n/Filter /FlateDecode");
                PutDoc(" >>\nstream\n" + EncStream(fon, fontGlyphXForm) + "\nendstream");
                writer.endObj(fontGlyphXForm);
                pageData.XObjects[fontName + "_gl" + Pdf::toPdf(gid)] = fontGlyphXForm;
        }
        return result;
}

PdfId PDFLibCore::PDF_EmbedFontObject(const QByteArray& font, const QByteArray& subtype)
{
        PdfId embeddedFontObject = writer.newObject();
        writer.startObj(embeddedFontObject);
        int len = font.length();
        QByteArray ttf = (Options.Compress? CompressArray(font) : font);
        //qDebug() << QString("sfnt data: size=%1 compressed=%2").arg(len).arg(bb.length());
        PutDoc("<<\n/Length " + Pdf::toPdf(ttf.length() + 1) + "\n");
        PutDoc("/Length1 " + Pdf::toPdf(len) + "\n");
        if (subtype.size() > 0)
                PutDoc("/Subtype " + subtype);
        if (Options.Compress)
                PutDoc("/Filter /FlateDecode\n");
        PutDoc(">>\nstream\n");
        EncodeArrayToStream(ttf, embeddedFontObject);
        PutDoc("\nendstream");
        writer.endObj(embeddedFontObject);
        return embeddedFontObject;
}

QByteArray PDFLibCore::PDF_GenerateSubsetTag(const QByteArray& fontName, const QList<uint>& usedGlyphs)
{
        uint hash, mod;
        QVector<uint> glyphVec = usedGlyphs.toVector();

        hash = qHashBits(fontName.constData(), fontName.size(), 0);
        hash = qHashBits(glyphVec.constData(), glyphVec.size() * sizeof(uint), hash);

        QByteArray subsetTag(6, (char) 0);
        for (int i = 0; i < 6; ++i)
        {
                mod = hash % 26;
                subsetTag[i] = 'A' + mod;
                hash = (hash - mod) / 26;
        }

        return subsetTag;
}

PdfId PDFLibCore::PDF_WriteFontDescriptor(const QByteArray& fontName, ScFace& face, ScFace::FontFormat fformat, PdfId embeddedFontObject)
{
        PdfId fontDescriptor = writer.newObject();
        writer.startObj(fontDescriptor);
        // TODO: think about QByteArray ScFace::getFontDescriptor() -- AV
        PutDoc("<<\n/Type /FontDescriptor\n");
        PutDoc("/FontName "  + Pdf::toName(fontName) + "\n");
        PutDoc("/FontBBox [ " + Pdf::toAscii(face.pdfFontBBoxAsString()) + " ]\n");
        PutDoc("/Flags ");
        //FIXME: isItalic() should be queried from ScFace, not from Qt -- AV
        //QFontInfo fo = QFontInfo(it.data());
        int pfl = 0;
        if (face.isFixedPitch())
                pfl = pfl ^ 1;
        //if (fo.italic())
        if (face.italicAngleAsString() != "0")
                pfl = pfl ^ 64;
        //                      pfl = pfl ^ 4;
        pfl = pfl ^ 32;
        PutDoc(Pdf::toPdf(pfl) + "\n");
        PutDoc("/Ascent "      + Pdf::toAscii(face.pdfAscentAsString()) + "\n");
        PutDoc("/Descent "     + Pdf::toAscii(face.pdfDescentAsString()) + "\n");
        PutDoc("/CapHeight "   + Pdf::toAscii(face.pdfCapHeightAsString()) + "\n");
        PutDoc("/ItalicAngle " + Pdf::toAscii(face.italicAngleAsString()) + "\n");
        PutDoc("/StemV 1\n");
        if (embeddedFontObject != 0)
        {
                if (fformat == ScFace::SFNT || fformat == ScFace::TTCF)
                {
                        if (face.type() == ScFace::OTF)
                        {
                                PutDoc("/FontFile3 " + Pdf::toPdf(embeddedFontObject) + " 0 R\n");
                        }
                        else
                        {
                                PutDoc("/FontFile2 " + Pdf::toPdf(embeddedFontObject) + " 0 R\n");
                        }
                }
                else if (fformat == ScFace::PFB)
                        PutDoc("/FontFile " + Pdf::toPdf(embeddedFontObject) + " 0 R\n");
                else if (fformat == ScFace::PFA)
                        PutDoc("/FontFile " + Pdf::toPdf(embeddedFontObject) + " 0 R\n");
        }
        PutDoc(">>");
        writer.endObj(fontDescriptor);
        return fontDescriptor;
}

PdfFont PDFLibCore::PDF_EncodeCidFont(const QByteArray& fontName, ScFace& face, const QByteArray& baseFont, PdfId fontDes, const QMap<uint, QString>& usedGlyphs, const QMap<uint,uint>& glyphmap)
{
        PdfFont result;
        result.name = Pdf::toName(fontName);
        result.usage = Used_in_Content;
        result.method = glyphmap.isEmpty()? Use_Embedded : Use_Subset;
        result.encoding = glyphmap.isEmpty() ? Encode_IdentityH : Encode_Subset;
        result.glyphmap = glyphmap;
       
        PdfId fontWidths2 = writer.newObject();
        writer.startObj(fontWidths2);
        QList<QByteArray> toUnicodeMaps;
        QList<int> toUnicodeMapsCount;
        QByteArray toUnicodeMap;
        int toUnicodeMapCounter = 0;

        ScFace::FaceEncoding encoding;
        face.glyphNames(encoding);
        QList<uint> keys = encoding.uniqueKeys();
        bool seenNotDef = false;
       
        PutDoc("[ ");
        for (auto git = keys.begin(); git != keys.end(); ++git)
        {
                uint gid = (result.encoding == Encode_Subset) ? glyphmap.value(*git, 0) : *git;
                if (gid == 0 && seenNotDef)
                        continue;
                seenNotDef |= (gid == 0);

                PutDoc(Pdf::toPdf(gid) + " [" + Pdf::toPdf(static_cast<int>(face.glyphWidth(*git) * 1000)) + "] ");
                QString tmp = QString::asprintf("%04X", gid);
                QString tmp2;
                auto  glyphIt = encoding.constFind(*git);
                if ((glyphIt != encoding.constEnd()) && (glyphIt->charcode > 0))
                        tmp2 = glyphIt->toUnicode;
                if (tmp2.isEmpty() && usedGlyphs.contains(*git))
                {
                        QString uniStr = usedGlyphs.value(*git);
                        for (int i = 0; i < uniStr.length(); ++i)
                                tmp2 += QString::asprintf("%04X", uniStr.at(i).unicode());
                }
                if (tmp2.isEmpty())
                        tmp2 = "0000";
                toUnicodeMap += "<" + Pdf::toAscii(tmp) + "> <" + Pdf::toAscii(tmp2) + ">\n";
                toUnicodeMapCounter++;
                if (toUnicodeMapCounter == 100)
                {
                        toUnicodeMaps.append(toUnicodeMap);
                        toUnicodeMapsCount.append(toUnicodeMapCounter);
                        toUnicodeMap.clear();
                        toUnicodeMapCounter = 0;
                }
        }
        PutDoc("]");
        writer.endObj(fontWidths2);
        if (toUnicodeMapCounter != 0)
        {
                toUnicodeMaps.append(toUnicodeMap);
                toUnicodeMapsCount.append(toUnicodeMapCounter);
        }
        QByteArray toUnicodeMapStream;
        toUnicodeMapStream += "/CIDInit /ProcSet findresource begin\n";
        toUnicodeMapStream += "12 dict begin\n";
        toUnicodeMapStream += "begincmap\n";
        toUnicodeMapStream += "/CIDSystemInfo <<\n";
        toUnicodeMapStream += "/Registry (Adobe)\n";
        toUnicodeMapStream += "/Ordering (UCS)\n";
        toUnicodeMapStream += "/Supplement 0\n";
        toUnicodeMapStream += ">> def\n";
        toUnicodeMapStream += "/CMapName /Adobe-Identity-UCS def\n";
        toUnicodeMapStream += "/CMapType 2 def\n";
        toUnicodeMapStream += "1 begincodespacerange\n";
        toUnicodeMapStream += "<0000> <FFFF>\n";
        toUnicodeMapStream += "endcodespacerange\n";
        for (int uniC = 0; uniC < toUnicodeMaps.count(); uniC++)
        {
                toUnicodeMapStream += Pdf::toPdf(toUnicodeMapsCount[uniC]);
                toUnicodeMapStream += " beginbfchar\n";
                toUnicodeMapStream += toUnicodeMaps[uniC];
                toUnicodeMapStream += "endbfchar\n";
        }
        toUnicodeMapStream += "endcmap\n";
        toUnicodeMapStream += "CMapName currentdict /CMap defineresource pop\n";
        toUnicodeMapStream += "end\n";
        toUnicodeMapStream += "end\n";
        PdfId fontToUnicode2 = WritePDFStream(toUnicodeMapStream);
        PdfId fontObject2 = writer.newObject();
        writer.startObj(fontObject2);
        PutDoc("<<\n/Type /Font\n/Subtype /Type0\n");
        PutDoc("/Name " + Pdf::toName(fontName) + "\n");
        PutDoc("/BaseFont " + Pdf::toName(baseFont) + "\n");
        PutDoc("/Encoding /Identity-H\n");
        PutDoc("/ToUnicode " + Pdf::toPdf(fontToUnicode2) + " 0 R\n");
        PutDoc("/DescendantFonts [");
        PutDoc("<</Type /Font");
        if (face.type() == ScFace::OTF)
                PutDoc("/Subtype /CIDFontType0");
        else
                PutDoc("/Subtype /CIDFontType2");
        PutDoc("/BaseFont " + Pdf::toName(baseFont));
        PutDoc("/FontDescriptor " + Pdf::toPdf(fontDes) + " 0 R");
        PutDoc("/CIDSystemInfo <</Ordering(Identity)/Registry(Adobe)/Supplement 0>>");
        PutDoc("/DW 1000");
        PutDoc("/W " + Pdf::toPdf(fontWidths2) + " 0 R");
        PutDoc("/CIDToGIDMap /Identity");
        PutDoc(">>"); // close CIDFont dictionary
        PutDoc("]\n"); // close DescendantFonts array
        PutDoc(">>");
        writer.endObj(fontObject2);
        pageData.FObjects[fontName] = fontObject2;
        return result;
}


PdfFont PDFLibCore::PDF_EncodeSimpleFont(const QByteArray& fontName, ScFace& face, const QByteArray& baseFont, const QByteArray& subtype, bool isEmbedded, PdfId fontDes, const QMap<uint, QString>& usedGlyphs)
{
        PdfFont result;
        result.name = Pdf::toName(fontName);
        result.usage = Used_in_Content;
        result.method = isEmbedded? Use_Embedded : Use_System;
        result.encoding = Encode_224;

        ScFace::FaceEncoding gl;
        face.glyphNames(gl);
       
        int nglyphs = 0;
        for (auto gli = gl.cbegin(); gli != gl.cend(); ++gli)
        {
                if (gli.key() > static_cast<uint>(nglyphs))
                        nglyphs = gli.key();
        }
        ++nglyphs;
        //                      qDebug() << QString("pdflib: nglyphs %1 max %2").arg(nglyphs).arg(face.maxGlyph());
       
        uint Fcc = nglyphs / 224;
        if ((nglyphs % 224) != 0)
                Fcc += 1;
        for (uint Fc = 0; Fc < Fcc; ++Fc)
        {
                PdfId fontWidths2 = writer.newObject();
                writer.startObj(fontWidths2);
                int chCount = 32;
                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 ");
                for (int ww = 32; ww < 256; ++ww)
                {
                        uint glyph = 224 * Fc + ww - 32;
                        if (gl.contains(glyph))
                                PutDoc(Pdf::toPdf(static_cast<int>(face.glyphWidth(glyph)* 1000)) + " ");
                        else
                                PutDoc("0 ");
                        chCount++;
                        if (signed(glyph) == nglyphs-1)
                                break;
                }
                PutDoc("]");
                writer.endObj(fontWidths2);

                QStringList toUnicodeMaps;
                QList<int>  toUnicodeMapsCount;
                QString toUnicodeMap;
                int toUnicodeMapCounter = 0;
                int crc = 0;
                bool startOfSeq = true;

                PdfId fontEncoding2 = writer.newObject();
                writer.startObj(fontEncoding2);
                PutDoc("<< /Type /Encoding\n");
                PutDoc("/Differences [ \n");
                for (int ww2 = 32; ww2 < 256; ++ww2)
                {
                        uint glyph = 224 * Fc + ww2 - 32;
                        ScFace::FaceEncoding::ConstIterator glIt = gl.find(glyph);
                        if (glIt != gl.cend() && !glIt.value().glyphName.isEmpty())
                        {
                                const ScFace::GlyphEncoding& glEncoding = glIt.value();
                                if (startOfSeq)
                                {
                                        PutDoc(Pdf::toPdf(ww2) + " ");
                                        startOfSeq = false;
                                }
                                PutDoc(Pdf::toName(glEncoding.glyphName) + " ");
                                QString tmp = QString::asprintf("%02X", ww2);
                                QString tmp2 = (glEncoding.charcode > 0) ? glEncoding.toUnicode : QString();
                                if (tmp2.isEmpty() && usedGlyphs.contains(glyph))
                                {
                                        QString uniStr = usedGlyphs.value(glyph);
                                        for (int i = 0; i < uniStr.length(); ++i)
                                                tmp2 += QString::asprintf("%04X", uniStr.at(i).unicode());
                                }
                                if (tmp2.isEmpty())
                                        tmp2 = "0000";
                                toUnicodeMap += "<" + Pdf::toAscii(tmp) + "> <" + Pdf::toAscii(tmp2) + ">\n";
                                toUnicodeMapCounter++;
                                if (toUnicodeMapCounter == 100)
                                {
                                        toUnicodeMaps.append(toUnicodeMap);
                                        toUnicodeMapsCount.append(toUnicodeMapCounter);
                                        toUnicodeMap.clear();
                                        toUnicodeMapCounter = 0;
                                }
                                crc++;
                        }
                        else
                        {
                                startOfSeq = true;
                        }
                        if (signed(glyph) == nglyphs-1)
                                break;
                        if (crc > 8)
                        {
                                PutDoc("\n");
                                crc = 0;
                        }
                }
                if (toUnicodeMapCounter != 0)
                {
                        toUnicodeMaps.append(toUnicodeMap);
                        toUnicodeMapsCount.append(toUnicodeMapCounter);
                }
                PutDoc("]\n");
                PutDoc(">>");
                writer.endObj(fontEncoding2);
                QByteArray toUnicodeMapStream;
                toUnicodeMapStream += "/CIDInit /ProcSet findresource begin\n";
                toUnicodeMapStream += "12 dict begin\n";
                toUnicodeMapStream += "begincmap\n";
                toUnicodeMapStream += "/CIDSystemInfo <<\n";
                toUnicodeMapStream += "/Registry (Adobe)\n";
                toUnicodeMapStream += "/Ordering (UCS)\n";
                toUnicodeMapStream += "/Supplement 0\n";
                toUnicodeMapStream += ">> def\n";
                toUnicodeMapStream += "/CMapName /Adobe-Identity-UCS def\n";
                toUnicodeMapStream += "/CMapType 2 def\n";
                toUnicodeMapStream += "1 begincodespacerange\n";
                toUnicodeMapStream += "<0000> <FFFF>\n";
                toUnicodeMapStream += "endcodespacerange\n";
                for (int uniC = 0; uniC < toUnicodeMaps.count(); uniC++)
                {
                        toUnicodeMapStream += Pdf::toPdf(toUnicodeMapsCount[uniC]);
                        toUnicodeMapStream += " beginbfchar\n";
                        toUnicodeMapStream += toUnicodeMaps[uniC];
                        toUnicodeMapStream += "endbfchar\n";
                }
                toUnicodeMapStream += "endcmap\n";
                toUnicodeMapStream += "CMapName currentdict /CMap defineresource pop\n";
                toUnicodeMapStream += "end\n";
                toUnicodeMapStream += "end\n";
                PdfId fontToUnicode2 = WritePDFStream(toUnicodeMapStream);
                PdfId fontObject2 = writer.newObject();
                writer.startObj(fontObject2);
                PutDoc("<<\n/Type /Font\n/Subtype ");
                PutDoc(subtype + "\n");
                PutDoc("/Name " + Pdf::toName(fontName) + "S" + Pdf::toPdf(Fc) + "\n");
                PutDoc("/BaseFont " + Pdf::toName(baseFont) + "\n");
                PutDoc("/FirstChar 0\n");
                PutDoc("/LastChar " + Pdf::toPdf(chCount-1) + "\n");
                PutDoc("/Widths " + Pdf::toPdf(fontWidths2) + " 0 R\n");
                PutDoc("/Encoding " + Pdf::toPdf(fontEncoding2) + " 0 R\n");
                PutDoc("/ToUnicode " + Pdf::toPdf(fontToUnicode2) + " 0 R\n");
                PutDoc("/FontDescriptor " + Pdf::toPdf(fontDes) + " 0 R\n");
                PutDoc(">>");
                writer.endObj(fontObject2);
                pageData.FObjects[fontName + "S" + Pdf::toPdf(Fc)] = fontObject2;
        } // for (Fc)

        return result;
}

PdfFont PDFLibCore::PDF_EncodeFormFont(const QByteArray& fontName, ScFace& face, const QByteArray& baseFont, const QByteArray& subtype, PdfId fontDes)
{
        PdfFont formFont;
        formFont.name = Pdf::toName(fontName) + "Form";
        formFont.usage = Used_in_Forms;
       
        PdfId fontWidthsForm = writer.newObject();
        writer.startObj(fontWidthsForm);
        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 ");
        for (uint ww = 32; ww < 256; ++ww)
        {
                int unicode = Pdf::fromPDFDocEncoding(ww);
                uint glyph  = face.char2CMap(unicode);
                if (glyph > 0)
                        PutDoc(Pdf::toPdf(static_cast<int>(face.glyphWidth(glyph)* 1000)) + " ");
                else
                        PutDoc("0 ");
        }
        PutDoc("]");
        writer.endObj(fontWidthsForm);

        PdfId fontObjectForm = writer.newObject();
        writer.startObj(fontObjectForm);
        PutDoc("<<\n/Type /Font\n/Subtype ");
        PutDoc(subtype + "\n");
        PutDoc("/Name " + formFont.name + "\n");
        PutDoc("/BaseFont " + Pdf::toName(baseFont) + "\n");
        PutDoc("/Encoding << \n");
        PutDoc("/Differences [ \n");
        PutDoc("24 /breve /caron /circumflex /dotaccent /hungarumlaut /ogonek /ring /tilde\n");
        PutDoc("39 /quotesingle 96 /grave 128 /bullet /dagger /daggerdbl /ellipsis /emdash /endash /florin /fraction /guilsinglleft /guilsinglright\n");
        PutDoc("/minus /perthousand /quotedblbase /quotedblleft /quotedblright /quoteleft /quoteright /quotesinglbase /trademark /fi /fl /Lslash /OE /Scaron\n");
        PutDoc("/Ydieresis /Zcaron /dotlessi /lslash /oe /scaron /zcaron 160 /Euro 164 /currency 166 /brokenbar 168 /dieresis /copyright /ordfeminine 172 /logicalnot\n");
        PutDoc("/.notdef /registered /macron /degree /plusminus /twosuperior /threesuperior /acute /mu 183 /periodcentered /cedilla /onesuperior /ordmasculine\n");
        PutDoc("188 /onequarter /onehalf /threequarters 192 /Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla /Egrave /Eacute /Ecircumflex\n");
        PutDoc("/Edieresis /Igrave /Iacute /Icircumflex /Idieresis /Eth /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply /Oslash\n");
        PutDoc("/Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn /germandbls /agrave /aacute /acircumflex /atilde /adieresis /aring /ae /ccedilla\n");
        PutDoc("/egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex /idieresis /eth /ntilde /ograve /oacute /ocircumflex /otilde /odieresis\n");
        PutDoc("/divide /oslash /ugrave /uacute /ucircumflex /udieresis /yacute /thorn /ydieresis\n");
        PutDoc("] >>\n");
        PutDoc("/FirstChar 0\n");
        PutDoc("/LastChar 255\n");
        PutDoc("/Widths " + Pdf::toPdf(fontWidthsForm) + " 0 R\n");
        PutDoc("/FontDescriptor " + Pdf::toPdf(fontDes) + " 0 R\n");
        PutDoc(">>");
        writer.endObj(fontObjectForm);

        pageData.FObjects[fontName + "Form"] = fontObjectForm;
        UsedFontsF.insert(face.replacementName(), formFont);

        return formFont;
}

/*
static void dumpFont(QString name, QByteArray data)
{
        QFile file(name);
        if (file.open(QIODevice::WriteOnly))
        {
                QDataStream out(&file);
                out.writeRawData(data.data(), data.length());
                file.close();
        }
}

static void dumpCFF(QString name, cff::CFF font)
{
        QFile file(name);
        if (file.open(QIODevice::WriteOnly))
        {
                QDataStream out(&file);
                font.dump(out);
                file.close();
        }
}
*/


PdfFont PDFLibCore::PDF_WriteTtfSubsetFont(const QByteArray& fontName, ScFace& face, const QMap<uint, QString>& usedGlyphs)
{
        QByteArray font;
        face.rawData(font);
        /*dumpFont(face.psName() + ".ttf", font);*/
        QList<ScFace::gid_type> glyphs = usedGlyphs.uniqueKeys();
        glyphs.removeAll(0);
        glyphs.prepend(0);

        QMap<uint, uint> glyphMap;
        QByteArray subset = sfnt::subsetFace(font, glyphs, glyphMap);

        /*dumpFont(face.psName() + "subs.ttf", subset);*/
        QByteArray baseFont   = sanitizeFontName(face.psName());
        QByteArray subsetTag  = PDF_GenerateSubsetTag(baseFont, glyphs);
        QByteArray subsetName = subsetTag + '+' + baseFont;
        PdfId embeddedFontObj = PDF_EmbedFontObject(subset, QByteArray());
        PdfId fontDes = PDF_WriteFontDescriptor(subsetName, face, face.format(), embeddedFontObj);
       
        PdfFont result = PDF_EncodeCidFont(fontName, face, subsetName, fontDes, usedGlyphs, glyphMap);
        return result;
}

PdfFont PDFLibCore::PDF_WriteCffSubsetFont(const QByteArray& fontName, ScFace& face, const QMap<uint, QString>& usedGlyphs)
{
//      QByteArray sfnt; //TEST
//      face.rawData(sfnt);
//      QByteArray cff = sfnt::getTable(sfnt, "CFF ");
//      dumpFont(fontName, cff);
//      cff::CFF cfffont(cff);
//      cfffont.dump();
//      QByteArray subsetfont = cff::subsetFace(cff, it.value().keys());
//      dumpFont(it.key() + "subs.cff", subsetfont);
//      cff::CFF subset(subsetfont);
//      subset.dump();
//      PDF_WriteFontDescriptor(fontName, face, fformat, 0);
//      // END
       
        QByteArray font, data;
        face.rawData(data);
        font = sfnt::getTable(data, "CFF ");
        /*dumpFont(face.psName() + ".cff", font);*/
        QList<ScFace::gid_type> glyphs = usedGlyphs.uniqueKeys();
        glyphs.removeAll(0);
        glyphs.prepend(0);

        QMap<uint, uint> glyphMap;
        QByteArray subset = cff::subsetFace(font, glyphs, glyphMap);
        if (subset.isEmpty())
        {
                PdfFont result = PDF_WriteType3Font(fontName, face, usedGlyphs);
                return result;
        }

        /*dumpFont(face.psName() + "subs.cff", subset);*/
        QByteArray baseFont   = sanitizeFontName(face.psName());
        QByteArray subsetTag  = PDF_GenerateSubsetTag(baseFont, glyphs);
        QByteArray subsetName = subsetTag + '+' + baseFont;
        PdfId embeddedFontObj = PDF_EmbedFontObject(subset, "/CIDFontType0C");
        PdfId fontDes = PDF_WriteFontDescriptor(subsetName, face, face.format(), embeddedFontObj);
       
        PdfFont result = PDF_EncodeCidFont(fontName, face, subsetName, fontDes, usedGlyphs, glyphMap);
        return result;
}

PdfFont PDFLibCore::PDF_WriteOpenTypeSubsetFont(const QByteArray& fontName, ScFace& face, const QMap<uint, QString>& usedGlyphs)
{
        QByteArray data;
        face.rawData(data);
        QList<ScFace::gid_type> glyphs = usedGlyphs.uniqueKeys();
        glyphs.removeAll(0);
        glyphs.prepend(0);

        QMap<uint, uint> glyphMap;
        QByteArray subset = sfnt::subsetFaceWithHB(data, glyphs, face.faceIndex(), glyphMap);
        if (subset.isEmpty())
        {
                PdfFont result = PDF_WriteType3Font(fontName, face, usedGlyphs);
                return result;
        }

        QByteArray subType = "/OpenType";
        if (!Options.supportsEmbeddedOpenTypeFonts())
        {
                QByteArray cffData = sfnt::getTable(subset, "CFF ");
                QByteArray cffSubset(cffData.data(), cffData.length());
                if (cffSubset.isEmpty())
                {
                        PdfFont result = PDF_WriteType3Font(fontName, face, usedGlyphs);
                        return result;
                }
                subset = cffSubset;
                subType = "/CIDFontType0C";
        }

        /*dumpFont(face.psName() + "subs.otf", subset);*/
        QByteArray baseFont   = sanitizeFontName(face.psName());
        QByteArray subsetTag  = PDF_GenerateSubsetTag(baseFont, glyphs);
        QByteArray subsetName = subsetTag + '+' + baseFont;
        PdfId embeddedFontObj = PDF_EmbedFontObject(subset, subType);
        PdfId fontDes = PDF_WriteFontDescriptor(subsetName, face, face.format(), embeddedFontObj);
       
        PdfFont result = PDF_EncodeCidFont(fontName, face, subsetName, fontDes, usedGlyphs, glyphMap);
        return result;
}

PdfId PDFLibCore::PDF_EmbedType1AsciiFontObject(const QByteArray& fon)
{
        QByteArray fon2;
        PdfId embeddedFontObject = writer.newObject();
        writer.startObj(embeddedFontObject);
        int len1 = fon.indexOf("eexec") + 5;
        fon2 = fon.left(len1) + "\n";
        int len2 = fon.indexOf("0000000000000000000000000");
        if (len2 == -1)
                len2 = fon.length() + 1;
        //                        QString tm;
        //                        uint value;
        //                        bool ok = true;
        //                              int count = 0;
        //                              for (int xx = len1; xx < len2-1; ++xx)
        //                              {
        //                                      tm = fon.at(xx);
        //                                      if ((tm == QChar(13)) || (tm == QChar(10)))
        //                                              continue;
        //                                      xx++;
        //                                      count++;
        //                                      tm += fon.at(xx);
        //                                      value = tm.toUInt(&ok, 16);
        //                                      fon2 += char(value);
        //                              }
        QByteArray hexData = QByteArray::fromHex(fon.mid(len1, len2-len1));
        fon2 += hexData;
        fon2 += fon.mid(len2);
        if (Options.Compress)
                fon2 = CompressArray(fon2);
        PutDoc("<<\n/Length " + Pdf::toPdf(fon2.length() + 1) + "\n");
        PutDoc("/Length1 " + Pdf::toPdf(len1+1) + "\n");
        PutDoc("/Length2 " + Pdf::toPdf(hexData.length()) + "\n");
        if (static_cast<int>(fon.length() - len2) == -1)
                PutDoc("/Length3 0\n");
        else
                PutDoc("/Length3 " + Pdf::toPdf(fon.length() - len2) + "\n");
        if (Options.Compress)
                PutDoc("/Filter /FlateDecode\n");
        PutDoc(">>\nstream\n" + EncStream(fon2, embeddedFontObject) + "\nendstream");
        writer.endObj(embeddedFontObject);
        return embeddedFontObject;
}


PdfId PDFLibCore::PDF_EmbedType1BinaryFontObject(const QByteArray& bb)
{
        PdfId embeddedFontObject = writer.newObject();
        QByteArray fon;
        writer.startObj(embeddedFontObject);
        int posi;
        for (posi = 6; posi < bb.size(); ++posi)
        {
                if ((bb[posi] == static_cast<char>(0x80)) && (static_cast<int>(bb[posi+1]) == 2))
                        break;
                fon += bb[posi];
        }
        int len1 = fon.length();
        int ulen;
        ulen = bb[posi+2] & 0xff;
        ulen |= (bb[posi+3] << 8) & 0xff00;
        ulen |= (bb[posi+4] << 16) & 0xff0000;
        ulen |= (bb[posi+5] << 24) & 0xff000000;
        if (ulen > bb.size())
                ulen = bb.size() - 7;
        posi += 6;
        for (int j = 0; j < ulen; ++j)
                fon += bb[posi++];
        posi += 6;
        int len2 = fon.length() - len1;
        for (int j = posi; j < bb.size(); ++j)
        {
                if ((bb[j] == static_cast<char>(0x80)) && (static_cast<int>(bb[j+1]) == 3))
                        break;
                if (bb[j] == '\r')
                        fon += "\n";
                else
                        fon += bb[j];
        }
        int len3 = fon.length() - len2 - len1;
        if (Options.Compress)
                fon = CompressArray(fon);
        PutDoc("<<\n/Length " + Pdf::toPdf(fon.length() + 1) + "\n");
        PutDoc("/Length1 " + Pdf::toPdf(len1) + "\n");
        PutDoc("/Length2 " + Pdf::toPdf(len2) + "\n");
        PutDoc("/Length3 " + Pdf::toPdf(len3) + "\n");
        if (Options.Compress)
                PutDoc("/Filter /FlateDecode\n");
        PutDoc(">>\nstream\n" + EncStream(fon,embeddedFontObject) + "\nendstream");
        writer.endObj(embeddedFontObject);
        return embeddedFontObject;
}


PdfId PDFLibCore::PDF_EmbedFontObject(const QString& name, ScFace& face)
{
        PdfId embeddedFontObject = 0;
        if (Options.EmbedList.contains(name))
        {
                ScFace::FontFormat fformat = face.format();
                QByteArray bb;
                face.rawData(bb);
                if (fformat == ScFace::PFB)
                {
                        embeddedFontObject = PDF_EmbedType1BinaryFontObject(bb);
                }
                if (fformat == ScFace::PFA)
                {
                        embeddedFontObject = PDF_EmbedType1AsciiFontObject(bb);
                }
                if (fformat == ScFace::SFNT || fformat == ScFace::TTCF)
                {
                        QByteArray subtype;
                        if (face.type() == ScFace::OTF && Options.supportsEmbeddedOpenTypeFonts())
                                subtype = "/OpenType";
                        else if (face.type() == ScFace::OTF)
                        {
                                subtype = "/CIDFontType0C";
                                QByteArray cff_bb = sfnt::getTable(bb, "CFF ");
                                //dumpCFF(face.psName() + ".cff", cff::CFF(cff_bb));
                                bb = cff::extractFace(cff_bb, 0);
                                //dumpCFF(face.psName() + "full.cff", cff::CFF(bb));
                        }
                        embeddedFontObject = PDF_EmbedFontObject(bb, subtype);
                }
        }
        return embeddedFontObject;
}


void PDFLibCore::PDF_Begin_WriteUsedFonts(SCFonts &AllFonts, const QMap<QString, QMap<uint, QString> >& usedFonts)
{
        qDebug() << "embed list:" << QStringList(Options.EmbedList).join(", ");
        qDebug() << "subset list:" << QStringList(Options.SubsetList).join(", ");
        qDebug() << "outline list:" << QStringList(Options.OutlineList).join(", ");

        int a = 0;
        for (auto it = usedFonts.cbegin(); it != usedFonts.cend(); ++it)
        {
                ScFace& face(AllFonts[it.key()]);
                ScFace::FontFormat fformat = face.format();
                PdfFont pdfFont;
                QByteArray fontName = QByteArray("Fo") + Pdf::toPdf(a);
               
                QMap<uint, QString> usedGlyphs = it.value();
                if (usedGlyphs.count() <= 0)
                        continue;
               
                qDebug() << "pdf font" << it.key();
                if (Options.OutlineList.contains(it.key()))
                {
                        pdfFont = PDF_WriteGlyphsAsXForms(fontName, face, usedGlyphs);
                }
                else
                {
                        if (Options.SubsetList.contains(it.key()))
                        {
                                if (fformat == ScFace::SFNT || fformat == ScFace::TTCF)
                                {
                                        if (face.type() == ScFace::TTF)
                                        {
                                                pdfFont = PDF_WriteTtfSubsetFont(fontName, face, usedGlyphs);
                                        }
                                        else if (face.type() == ScFace::OTF && face.isCIDKeyed() && sfnt::canSubsetOpenTypeFonts())
                                        {
                                                pdfFont = PDF_WriteOpenTypeSubsetFont(fontName, face, usedGlyphs);
                                        }
                                        else if (face.isCIDKeyed())
                                        {
                                                pdfFont = PDF_WriteType3Font(fontName, face, usedGlyphs);
                                        }
                                        else
                                        {
                                                pdfFont = PDF_WriteCffSubsetFont(fontName, face, usedGlyphs);
                                        }
                                }
                                else
                                {
                                        pdfFont = PDF_WriteType3Font(fontName, face, usedGlyphs);
                                }
                        }
                        else
                        {
                                PdfId embeddedFontObject = PDF_EmbedFontObject(it.key(), face);
                               
                                QByteArray baseFont  = sanitizeFontName(face.psName());
                                PdfId fontDescriptor = PDF_WriteFontDescriptor(baseFont, face, fformat, embeddedFontObject);
                               
                                ScFace::FaceEncoding gl;
                                face.glyphNames(gl);

                                // #14550 : for TTF fonts, we already avoid using the Postscript glyph name table
                                // as it is notoriously unreliable. We hence retrieve glyph names using unicode cmap
                                // and adobe glyph names uniXXXX convention. Unfortunately we may still not get all
                                // required glyph names and some glyphs may not have a name anyway. So check we have
                                // ps names for all glyphs we need and if not, then use CID encoding
                                bool hasNeededGlyphNames = face.hasNames() && (gl.count() >= usedGlyphs.count());
                                if ((fformat == ScFace::SFNT || fformat == ScFace::TTCF))
                                {
                                        for (auto it = usedGlyphs.constBegin(); it != usedGlyphs.constEnd(); ++it)
                                        {
                                                int glyphIndex = it.key();
                                                hasNeededGlyphNames &= gl.contains(glyphIndex);
                                                if (!hasNeededGlyphNames)
                                                        break;
                                        }
                                }
                               
                                QByteArray subtype = (fformat == ScFace::SFNT || fformat == ScFace::TTCF) ? "/TrueType" : "/Type1";
                               
                                if ((face.isSymbolic() || !hasNeededGlyphNames || Options.Version == PDFVersion::PDF_X4 || face.type() == ScFace::OTF) &&
                                        (fformat == ScFace::SFNT || fformat == ScFace::TTCF))
                                {
                                        pdfFont = PDF_EncodeCidFont(fontName, face, baseFont, fontDescriptor, usedGlyphs, QMap<uint,uint>());
                                }
                                else
                                {
                                        pdfFont = PDF_EncodeSimpleFont(fontName, face, baseFont, subtype, embeddedFontObject != 0, fontDescriptor, usedGlyphs);
                                }

                                if ((Options.Version != PDFVersion::PDF_X4) && (face.type() != ScFace::OTF))
                                        PDF_EncodeFormFont(fontName, face, baseFont, subtype, fontDescriptor);
                        }
                        pdfFont.usage = Used_in_Content;
                }
                a++;
                QString meth;
                switch (pdfFont.method)
                {
                        case Use_System:
                                meth = "Systemfont (no embedding)"; break;
                        case Use_Embedded:
                                meth = "Embed"; break;
                        case Use_Subset:
                                meth = "Subset"; break;
                        case Use_Type3:
                                meth = "Subset as Type3 font"; break;
                        case Use_XForm:
                                meth = "Outline (PDF XForm)"; break;
                        default:
                                meth = "?"; break;
                }
                qDebug() << pdfFont.name << "uses method" << meth << "and encoding" << pdfFont.encoding;
                UsedFontsP.insert(it.key(), pdfFont);
        }
}

void PDFLibCore::PDF_Begin_Colors()
{
        if (Options.UseLPI)
        {
                uint halftones = writer.newObject();
                writer.startObj(halftones);
                PutDoc("<<\n/Type /Halftone\n/HalftoneType 5\n");
                QMap<QString,LPIData>::const_iterator itlp;
                for (itlp = Options.LPISettings.constBegin(); itlp != Options.LPISettings.constEnd(); ++itlp)
                {
                        PutDoc(Pdf::toName(itlp.key()) + "\n<<\n/Type /Halftone\n/HalftoneType 1\n/Frequency ");
                        PutDoc(Pdf::toPdf(itlp.value().Frequency) + "\n/Angle " + Pdf::toPdf(itlp.value().Angle) + "\n/SpotFunction ");
                        QByteArray func ("");
                        switch (itlp.value().SpotFunc)
                        {
                                case 0:
                                        func = "/SimpleDot";
                                        break;
                                case 1:
                                        func = "/Line";
                                        break;
                                case 2:
                                        func = "/Round";
                                        break;
                                case 3:
                                        func = "/Ellipse";
                                        break;
                                default:
                                        func = "/SimpleDot";
                                        break;
                        }
                        PutDoc(func + "\n>>\n");
                }
                PutDoc("/Default\n<<\n/Type /Halftone\n/HalftoneType 1\n/Frequency 50\n/Angle 45\n/SpotFunction /Round\n>>\n");
                PutDoc(">>");
                writer.endObj(halftones);
                HTName = ResNam + Pdf::toPdf(ResCount);
                Transpar[HTName] = writeGState("/HT " + Pdf::toPdf(halftones) + " 0 R\n");
                ResCount++;
        }
        if ((doc.HasCMS) && (Options.UseProfiles) && (Options.Version != PDFVersion::PDF_X1a))
        {
                PdfId iccProfileObject = writer.newObject();
                writer.startObj(iccProfileObject);
                QByteArray dataP;
                PdfICCD dataD;
                loadRawBytes(ScCore->InputProfiles[Options.SolidProf], dataP);
                PutDoc("<<\n");
                if (Options.Compress)
                {
                        QByteArray compData = CompressArray(dataP);
                        if (compData.size() > 0)
                        {
                                PutDoc("/Filter /FlateDecode\n");
                                dataP = compData;
                        }
                }
                PutDoc("/Length " + Pdf::toPdf(dataP.size() + 1) + "\n");
                PutDoc("/N " + Pdf::toPdf(Options.SComp) + "\n");
                PutDoc(">>\nstream\n");
                EncodeArrayToStream(dataP, iccProfileObject);
                PutDoc("\nendstream");
                writer.endObj(iccProfileObject);
                PdfId iccColorspace = writer.newObject();
                writer.startObj(iccColorspace);
                dataD.ResName = ResNam + Pdf::toPdf(ResCount);
                dataD.ICCArray = "[ /ICCBased " + Pdf::toPdf(iccProfileObject) + " 0 R ]";
                dataD.ResNum = iccColorspace;
                dataD.components = Options.SComp;
                ICCProfiles[Options.SolidProf] = dataD;
                PutDoc("[ /ICCBased " + Pdf::toPdf(iccProfileObject) + " 0 R ]");
                writer.endObj(iccColorspace);
                ResCount++;
        }
        if (((!Options.isGrayscale) && (!Options.UseRGB)) && (Options.UseSpotColors))
        {
                doc.getUsedColors(colorsToUse);
                for (auto itf = colorsToUse.begin(); itf != colorsToUse.end(); ++itf)
                {
                        const ScColor& colorToUse = itf.value();
                        if (colorToUse.isProcessColor())
                                continue;
                        CMYKColorF cmykValues;
                        PdfSpotC spotD;
                        ScColorEngine::getCMYKValues(colorToUse, &doc, cmykValues);
                        QByteArray colorDesc = "{\ndup " + FToStr(cmykValues.c) + "\nmul exch dup ";
                        colorDesc += FToStr(cmykValues.m) + "\nmul exch dup ";
                        colorDesc += FToStr(cmykValues.y) + "\nmul exch ";
                        colorDesc += FToStr(cmykValues.k) + " mul }";
                        PdfId separationFunction = writer.newObject();
                        writer.startObj(separationFunction);
                        PutDoc("<<\n/FunctionType 4\n");
                        PutDoc("/Domain [0.0 1.0]\n");
                        PutDoc("/Range [0.0 1.0 0.0 1.0 0.0 1.0 0.0 1.0]\n");
                        PutDoc("/Length " + Pdf::toPdf(colorDesc.length() + 1) + "\n");
                        PutDoc(">>\nstream\n" + EncStream(colorDesc, separationFunction) + "\nendstream");
                        writer.endObj(separationFunction);
                        PdfId separationColorspace = writer.newObject();
                        writer.startObj(separationColorspace);
                        PutDoc("[ /Separation ");
                        if (colorsToUse[itf.key()].isRegistrationColor())
                                PutDoc("/All");
                        else
                                PutDoc(Pdf::toName(itf.key().simplified()));
                        PutDoc(" /DeviceCMYK " + Pdf::toObjRef(separationFunction) + " ]");
                        writer.endObj(separationColorspace);
                        spotD.ResName = spotNam + Pdf::toPdf(spotCount);
                        spotD.ResNum = separationColorspace;
                        spotMap.insert(itf.key(), spotD);
                        spotCount++;
                }
        }
        if ((Options.cropMarks) || (Options.bleedMarks) || (Options.registrationMarks) || (Options.colorMarks) || (Options.docInfoMarks))
        {
                PdfSpotC spotD;
                PdfId registrationColorspace = writer.newObject();
                writer.startObj(registrationColorspace);
                PutDoc("[ /Separation /All /DeviceCMYK\n");
                PutDoc("<<\n/FunctionType 2\n");
                PutDoc("/Domain [0.0 1.0]\n");
                PutDoc("/Range [0.0 1.0 0.0 1.0 0.0 1.0 0.0 1.0]\n");
                PutDoc("/C0 [0 0 0 0] \n");
                PutDoc("/C1 [1 1 1 1] \n");
                PutDoc("/N 1\n");
                PutDoc(">>\n]");
                writer.endObj(registrationColorspace);
                spotD.ResName = spotNam + Pdf::toPdf(spotCount);
                spotD.ResNum = registrationColorspace;
                spotMapReg.insert("Register", spotD);
                spotCount++;
        }
}

void PDFLibCore::PDF_Begin_Layers()
{
        if (Options.exportsLayers())
        {
                ScLayer ll;
                PdfOCGInfo ocg;
                ll.isPrintable = false;
                ll.ID = 0;
                QByteArray ocgNam("oc");
                int docLayersCount = doc.Layers.count();
                for (int la = 0; la < docLayersCount; ++la)
                {
                        PdfId optionalContent = writer.newObject();
                        doc.Layers.levelToLayer(ll, la);
                        ocg.Name = ocgNam + Pdf::toPdf(ll.ID);
                        ocg.ObjNum = optionalContent;
                        ocg.visible = ll.isViewable;
                        OCGEntries.insert(ll.Name, ocg);
                        writer.startObj(optionalContent);
                        PutDoc("<<\n");
                        PutDoc("/Type /OCG\n");
                        PutDoc("/Name ");
                        PutDoc(EncStringUTF16(ll.Name, optionalContent));
                        PutDoc("\n");
                        PutDoc("/Usage <</Print <</PrintState ");
                        if (ll.isPrintable)
                                PutDoc("/ON");
                        else
                                PutDoc("/OFF");
                        PutDoc(">> /View <</ViewState ");
                        if (ll.isViewable)
                                PutDoc("/ON");
                        else
                                PutDoc("/OFF");
                        PutDoc(">>>>");
                        PutDoc("\n");
                        PutDoc(">>");
                        writer.endObj(optionalContent);
                }
        }
}

bool PDFLibCore::PDF_TemplatePage(const ScPage* pag, bool)
{
        QByteArray tmpOut;
        ActPageP = pag;
        PageItem* ite;
        QList<PageItem*> PItems;
        ScLayer ll;
        ll.isPrintable = false;
        ll.ID = 0;

        Content.clear();

        double bLeft, bRight, bBottom, bTop;
        getBleeds(pag, bLeft, bRight, bBottom, bTop);

        pageData.AObjects.clear();

        for (int la = 0; la < doc.Layers.count(); ++la)
        {
                doc.Layers.levelToLayer(ll, la);
                PItems = doc.MasterItems;
                if (!ll.isPrintable && !Options.exportsLayers())
                        continue;

                if (Options.exportsLayers())
                        PutPage("/OC /" + OCGEntries[ll.Name].Name + " BDC\n");
                for (int a = 0; a < PItems.count(); ++a)
                {
                        Content.clear();
                        ite = PItems.at(a);
                        if (ite->m_layerID != ll.ID)
                                continue;
                        double x  = pag->xOffset() - bLeft;
                        double y  = pag->yOffset() - bTop;
                        double w  = pag->width() + bLeft + bRight;
                        double h1 = pag->height() + bBottom + bTop;
                        double ilw= ite->visualLineWidth();
                        double x2 = ite->BoundingX - ilw / 2.0;
                        double y2 = ite->BoundingY - ilw / 2.0;
                        double w2 = ite->BoundingW + ilw;
                        double h2 = ite->BoundingH + ilw;
                        if (!QRectF(x2, y2, w2, h2).intersects(QRectF(x, y, w, h1)))
                                continue;
                        if (ite->ChangedMasterItem)
                                continue;
                        if ((!pag->pageNameEmpty()) && (ite->OwnPage != pag->pageNr()) && (ite->OwnPage != -1))
                                continue;
                        PutPage("q\n");
                        if ((ite->doOverprint) && (!Options.UseRGB))
                        {
                                QByteArray ShName = ResNam + Pdf::toPdf(ResCount);
                                ResCount++;
                                Transpar[ShName] = writeGState("/OP true\n"
                                                                                                "/op true\n"
                                                                                                "/OPM 1\n");
                                PutPage(Pdf::toName(ShName) + " gs\n");
                        }
                        /* Bookmarks on Master Pages do not make any sense */
//                      if ((ite->isBookmark) && (Options.Bookmarks))
//                              PDF_Bookmark(ite, pag->height() - (ite->yPos() - pag->yOffset()));
                        if (!ite->printEnabled() || (ite->isTextContainer() && !pag->pageNameEmpty()))
                        {
                                PutPage("Q\n");
                                continue;
                        }
                        if (ite->fillColor() != CommonStrings::None)
                                PutPage(putColor(ite->fillColor(), ite->fillShade(), true));
                        if (ite->lineColor() != CommonStrings::None)
                                PutPage(putColor(ite->lineColor(), ite->lineShade(), false));
                        Content += FToStr(fabs(ite->lineWidth())) + " w\n";
                        if (ite->DashValues.count() != 0)
                        {
                                PutPage("[ ");
                                for (auto it = ite->DashValues.cbegin(); it != ite->DashValues.cend(); ++it)
                                {
                                        double da = *it;
                                        // #8758: Custom dotted lines don't export properly to pdf
                                        // Null values have to be exported if line end != flat
                                        if ((da != 0) || (ite->lineEnd() != Qt::FlatCap))
                                                PutPage(Pdf::toPdf(da) + " ");
                                }
                                PutPage("] " + Pdf::toPdf(ite->DashOffset) + " d\n");
                        }
                        else
                                PutPage("[" + Pdf::toAscii(getDashString(ite->PLineArt, ite->lineWidth())) + "] 0 d\n");
                        switch (ite->PLineEnd)
                        {
                                case Qt::FlatCap:
                                        PutPage("0 J\n");
                                        break;
                                case Qt::SquareCap:
                                        PutPage("2 J\n");
                                        break;
                                case Qt::RoundCap:
                                        PutPage("1 J\n");
                                        break;
                                default:
                                        PutPage("0 J\n");
                                        break;
                        }
                        switch (ite->PLineJoin)
                        {
                                case Qt::MiterJoin:
                                        PutPage("0 j\n");
                                        break;
                                case Qt::BevelJoin:
                                        PutPage("2 j\n");
                                        break;
                                case Qt::RoundJoin:
                                        PutPage("1 j\n");
                                        break;
                                default:
                                        PutPage("0 j\n");
                                        break;
                        }
                        PutPage("1 0 0 1 " + FToStr(ite->xPos() - pag->xOffset()) + " " + FToStr(pag->height() - (ite->yPos()  - pag->yOffset())) + " cm\n");
                        if (ite->rotation() != 0)
                        {
                                double sr = sin(-ite->rotation()* M_PI / 180.0);
                                double cr = cos(-ite->rotation()* M_PI / 180.0);
                                if ((cr * cr) < 0.000001)
                                        cr = 0;
                                if ((sr * sr) < 0.000001)
                                        sr = 0;
                                PutPage(FToStr(cr) + " " + FToStr(sr) + " " + FToStr(-sr) + " " + FToStr(cr) + " 0 0 cm\n");
                        }
                        PutPage(PDF_PutSoftShadow(ite));
                        switch (ite->itemType())
                        {
                                case PageItem::ImageFrame:
                                case PageItem::LatexFrame:
                                        PutPage("q\n");
                                        // Same functions as for ImageFrames work for LatexFrames too
                                        if (((ite->GrMask > 0) || (ite->fillTransparency() != 0) || (ite->fillBlendmode() != 0)) && Options.supportsTransparency())
                                        {
                                                PutPage(PDF_TransparenzFill(ite));
                                        }
                                        if ((ite->fillColor() != CommonStrings::None) || (ite->GrType != 0))
                                        {
                                                if (ite->GrType != 0)
                                                {
                                                        if (ite->GrType == Gradient_Pattern)
                                                        {
                                                                if (!PDF_PatternFillStroke(tmpOut, ite))
                                                                        return false;
                                                        }
                                                        else if (ite->GrType == Gradient_Hatch)
                                                        {
                                                                if (!PDF_HatchFill(tmpOut, ite))
                                                                        return false;
                                                        }
                                                        else
                                                        {
                                                                if (!PDF_GradientFillStroke(tmpOut, ite))
                                                                        return false;
                                                        }
                                                        if (!tmpOut.isEmpty())
                                                        {
                                                                PutPage("q\n");
                                                                if (ite->GrType == Gradient_Hatch)
                                                                        PutPage(tmpOut);
                                                                else
                                                                {
                                                                        PutPage(tmpOut);
                                                                        PutPage(SetClipPath(ite));
                                                                        PutPage(ite->fillRule ? "h\nf*\n" : "h\nf\n");
                                                                }
                                                                PutPage("Q\n");
                                                        }
                                                }
                                                else
                                                {
                                                        if (ite->fillColor() != CommonStrings::None)
                                                        {
                                                                PutPage(SetClipPath(ite));
                                                                PutPage(ite->fillRule ? "h\nf*\n" : "h\nf\n");
                                                        }
                                                }
                                        }
                                        PutPage("q\n");
                                        PutPage(SetPathAndClip(ite, true));
                                        if (ite->imageFlippedH())
                                                PutPage("-1 0 0 1 " + FToStr(ite->width()) + " 0 cm\n");
                                        if (ite->imageFlippedV())
                                                PutPage("1 0 0 -1 0 " + FToStr(-ite->height()) + " cm\n");
                                        if (!ite->imageClip.empty())
                                                PutPage(SetImagePathAndClip(ite));
                                        if ((ite->imageIsAvailable) && (!ite->Pfile.isEmpty()))
                                        {
                                                if (!PDF_Image(ite, ite->Pfile, ite->imageXScale(), ite->imageYScale(), ite->imageXOffset(), -ite->imageYOffset(), false, ite->ImageProfile, ite->UseEmbedded, ite->ImageIntent, &tmpOut))
                                                        return false;
                                                PutPage(tmpOut);
                                        }
                                        PutPage("Q\n");
                                        PutPage("Q\n");
                                        if (((ite->lineColor() != CommonStrings::None) || (!ite->NamedLStyle.isEmpty()) || (!ite->strokePattern().isEmpty()) || (ite->GrTypeStroke > 0)))
                                        {
                                                if (((ite->lineTransparency() != 0) || (ite->lineBlendmode() != 0)) && Options.supportsTransparency())
                                                        PutPage(PDF_TransparenzStroke(ite));
                                                if (ite->NamedLStyle.isEmpty())
                                                {
                                                        ScPattern* strokePattern = doc.checkedPattern(ite->strokePattern());
                                                        if (strokePattern)
                                                        {
                                                                if (ite->patternStrokePath)
                                                                {
                                                                        QPainterPath path = ite->PoLine.toQPainterPath(false);
                                                                        PutPage(handleBrushPattern(ite, path, pag, pag->pageNr()));
                                                                }
                                                                else
                                                                {
                                                                        if (!PDF_PatternFillStroke(tmpOut, ite, 1))
                                                                                return false;
                                                                        PutPage(SetClipPath(ite));
                                                                        PutPage(tmpOut);
                                                                        PutPage("h\nS\n");
                                                                }
                                                        }
                                                        else if (ite->GrTypeStroke > 0)
                                                        {
                                                                if (!PDF_GradientFillStroke(tmpOut, ite, true))
                                                                        return false;
                                                                PutPage("q\n");
                                                                PutPage(tmpOut);
                                                                PutPage(SetClipPath(ite));
                                                                PutPage("h\nS\n");
                                                                PutPage("Q\n");
                                                        }
                                                        else if (ite->lineColor() != CommonStrings::None)
                                                        {
                                                                PutPage(SetClipPath(ite));
                                                                PutPage("h\nS\n");
                                                        }
                                                }
                                                else
                                                {
                                                        multiLine ml = doc.docLineStyles[ite->NamedLStyle];
                                                        for (int it = ml.size() - 1; it > -1; it--)
                                                        {
                                                                if ((ml[it].Color != CommonStrings::None) && (ml[it].Width != 0))
                                                                {
                                                                        PutPage(setStrokeMulti(&ml[it]));
                                                                        PutPage(SetClipPath(ite));
                                                                        PutPage("h\nS\n");
                                                                }
                                                        }
                                                }
                                        }
                                        break;
                                case PageItem::TextFrame:
                                        break;
                                case PageItem::Line:
                                        if (((ite->lineTransparency() != 0) || (ite->lineBlendmode() != 0)) && Options.supportsTransparency())
                                                PutPage(PDF_TransparenzStroke(ite));
                                        if (ite->NamedLStyle.isEmpty())
                                        {
                                                ScPattern* strokePattern = doc.checkedPattern(ite->strokePattern());
                                                if (strokePattern)
                                                {
                                                        if (ite->patternStrokePath)
                                                        {
                                                                QPainterPath path;
                                                                path.moveTo(0, 0);
                                                                path.lineTo(ite->width(), 0);
                                                                PutPage(handleBrushPattern(ite, path, pag, pag->pageNr()));
                                                        }
                                                        else
                                                        {
                                                                if (!PDF_PatternFillStroke(tmpOut, ite, 1))
                                                                        return false;
                                                                PutPage(tmpOut);
                                                                PutPage("0 0 m\n");
                                                                PutPage(FToStr(ite->width()) + " 0 l\n");
                                                                PutPage("S\n");
                                                        }
                                                }
                                                else if (ite->GrTypeStroke > 0)
                                                {
                                                        if (!PDF_GradientFillStroke(tmpOut, ite, true))
                                                                return false;
                                                        PutPage("q\n");
                                                        PutPage(tmpOut);
                                                        PutPage("0 0 m\n");
                                                        PutPage(FToStr(ite->width()) + " 0 l\n");
                                                        PutPage("S\nQ\n");
                                                }
                                                else if (ite->lineColor() != CommonStrings::None)
                                                {
                                                        PutPage("0 0 m\n");
                                                        PutPage(FToStr(ite->width()) + " 0 l\n");
                                                        PutPage("S\n");
                                                }
                                        }
                                        else
                                        {
                                                multiLine ml = doc.docLineStyles[ite->NamedLStyle];
                                                for (int it = ml.size() - 1; it > -1; it--)
                                                {
                                                        if ((ml[it].Color != CommonStrings::None) && (ml[it].Width != 0))
                                                        {
                                                                PutPage(setStrokeMulti(&ml[it]));
                                                                PutPage("0 0 m\n");
                                                                PutPage(FToStr(ite->width()) + " 0 l\n");
                                                                PutPage("S\n");
                                                        }
                                                }
                                        }
                                        if (ite->startArrowIndex() != 0)
                                        {
                                                QTransform arrowTrans;
                                                arrowTrans.scale(-1,1);
                                                arrowTrans.scale(ite->startArrowScale() / 100.0, ite->startArrowScale() / 100.0);
                                                PutPage(drawArrow(ite, arrowTrans, ite->startArrowIndex()));
                                        }
                                        if (ite->endArrowIndex() != 0)
                                        {
                                                QTransform arrowTrans;
                                                arrowTrans.translate(ite->width(), 0);
                                                arrowTrans.scale(ite->endArrowScale() / 100.0, ite->endArrowScale() / 100.0);
                                                PutPage(drawArrow(ite, arrowTrans, ite->endArrowIndex()));
                                        }
                                        break;
                                case PageItem::ItemType1:
                                case PageItem::ItemType3:
                                case PageItem::Polygon:
                                case PageItem::RegularPolygon:
                                case PageItem::Arc:
                                        PutPage("q\n");
                                        if (((ite->GrMask > 0) || (ite->fillTransparency() != 0) || (ite->fillBlendmode() != 0)) && Options.supportsTransparency())
                                        {
                                                PutPage(PDF_TransparenzFill(ite));
                                        }
                                        if (ite->GrType != 0)
                                        {
                                                if (ite->GrType == Gradient_Pattern)
                                                {
                                                        if (!PDF_PatternFillStroke(tmpOut, ite))
                                                                return false;
                                                }
                                                else if (ite->GrType == Gradient_Hatch)
                                                {
                                                        if (!PDF_HatchFill(tmpOut, ite))
                                                                return false;
                                                }
                                                else
                                                {
                                                        if (!PDF_GradientFillStroke(tmpOut, ite))
                                                                return false;
                                                }
                                                if (!tmpOut.isEmpty())
                                                {
                                                        if (ite->GrType == Gradient_Hatch)
                                                                PutPage(tmpOut);
                                                        else
                                                        {
                                                                PutPage(tmpOut);
                                                                PutPage(SetClipPath(ite));
                                                                PutPage(ite->fillRule ? "h\nf*\n" : "h\nf\n");
                                                        }
                                                }
                                        }
                                        else
                                        {
                                                if (ite->fillColor() != CommonStrings::None)
                                                {
                                                        PutPage(SetClipPath(ite));
                                                        PutPage(ite->fillRule ? "h\nf*\n" : "h\nf\n");
                                                }
                                        }
                                        PutPage("Q\n");
                                        if ((ite->lineColor() != CommonStrings::None) || (!ite->NamedLStyle.isEmpty()) || (!ite->strokePattern().isEmpty()) || (ite->GrTypeStroke > 0))
                                        {
                                                if (((ite->lineTransparency() != 0) || (ite->lineBlendmode() != 0)) && Options.supportsTransparency())
                                                        PutPage(PDF_TransparenzStroke(ite));
                                                if ((ite->NamedLStyle.isEmpty()) && (ite->lineWidth() != 0.0))
                                                {
                                                        ScPattern* strokePattern = doc.checkedPattern(ite->strokePattern());
                                                        if (strokePattern)
                                                        {
                                                                if (ite->patternStrokePath)
                                                                {
                                                                        QPainterPath path = ite->PoLine.toQPainterPath(false);
                                                                        PutPage(handleBrushPattern(ite, path, pag, pag->pageNr()));
                                                                }
                                                                else
                                                                {
                                                                        if (!PDF_PatternFillStroke(tmpOut, ite, 1))
                                                                                return false;
                                                                        PutPage(tmpOut);
                                                                        PutPage("h\nS\n");
                                                                }
                                                        }
                                                        else if (ite->GrTypeStroke > 0)
                                                        {
                                                                if (!PDF_GradientFillStroke(tmpOut, ite, true))
                                                                        return false;
                                                                PutPage("q\n");
                                                                PutPage(tmpOut);
                                                                PutPage(SetClipPath(ite));
                                                                PutPage("h\nS\n");
                                                                PutPage("Q\n");
                                                        }
                                                        else if (ite->lineColor() != CommonStrings::None)
                                                        {
                                                                PutPage(SetClipPath(ite));
                                                                PutPage("h\nS\n");
                                                        }
                                                }
                                                else
                                                {
                                                        multiLine ml = doc.docLineStyles[ite->NamedLStyle];
                                                        for (int it = ml.size() - 1; it > -1; it--)
                                                        {
                                                                if ((ml[it].Color != CommonStrings::None) && (ml[it].Width != 0))
                                                                {
                                                                        PutPage(setStrokeMulti(&ml[it]));
                                                                        PutPage(SetClipPath(ite));
                                                                        PutPage("h\nS\n");
                                                                }
                                                        }
                                                }
                                        }
                                        break;
                                case PageItem::PolyLine:
                                case PageItem::Spiral:
                                        if (ite->PoLine.size() > 3) // && ((ite->PoLine.point(0) != ite->PoLine.point(1)) || (ite->PoLine.point(2) != ite->PoLine.point(3))))
                                        {
                                                PutPage("q\n");
                                                if (((ite->GrMask > 0) || (ite->fillTransparency() != 0) || (ite->fillBlendmode() != 0)) && Options.supportsTransparency())
                                                {
                                                        PutPage(PDF_TransparenzFill(ite));
                                                }
                                                if (ite->GrType != 0)
                                                {
                                                        if (ite->GrType == Gradient_Pattern)
                                                        {
                                                                if (!PDF_PatternFillStroke(tmpOut, ite))
                                                                        return false;
                                                        }
                                                        else if (ite->GrType == Gradient_Hatch)
                                                        {
                                                                if (!PDF_HatchFill(tmpOut, ite))
                                                                        return false;
                                                        }
                                                        else
                                                        {
                                                                if (!PDF_GradientFillStroke(tmpOut, ite))
                                                                        return false;
                                                        }
                                                        if (!tmpOut.isEmpty())
                                                        {
                                                                if (ite->GrType == Gradient_Hatch)
                                                                        PutPage(tmpOut);
                                                                else
                                                                {
                                                                        PutPage(tmpOut);
                                                                        PutPage(SetClipPath(ite));
                                                                        PutPage(ite->fillRule ? "h\nf*\n" : "h\nf\n");
                                                                }
                                                        }
                                                }
                                                else
                                                {
                                                        if (ite->fillColor() != CommonStrings::None)
                                                        {
                                                                PutPage(SetClipPath(ite));
                                                                PutPage(ite->fillRule ? "h\nf*\n" : "h\nf\n");
                                                        }
                                                }
                                                PutPage("Q\n");
                                        }
                                        if ((ite->lineColor() != CommonStrings::None) || (!ite->NamedLStyle.isEmpty()) || (!ite->strokePattern().isEmpty()) || (ite->GrTypeStroke > 0))
                                        {
                                                if (((ite->lineTransparency() != 0) || (ite->lineBlendmode() != 0)) && Options.supportsTransparency())
                                                        PutPage(PDF_TransparenzStroke(ite));
                                                if (ite->NamedLStyle.isEmpty())
                                                {
                                                        ScPattern* strokePattern = doc.checkedPattern(ite->strokePattern());
                                                        if (strokePattern)
                                                        {
                                                                if (ite->patternStrokePath)
                                                                {
                                                                        QPainterPath path = ite->PoLine.toQPainterPath(false);
                                                                        PutPage(handleBrushPattern(ite, path, pag, pag->pageNr()));
                                                                }
                                                                else
                                                                {
                                                                        if (!PDF_PatternFillStroke(tmpOut, ite, 1))
                                                                                return false;
                                                                        PutPage(tmpOut);
                                                                        PutPage(SetClipPath(ite, false));
                                                                        PutPage("S\n");
                                                                }
                                                        }
                                                        else if (ite->GrTypeStroke > 0)
                                                        {
                                                                if (!PDF_GradientFillStroke(tmpOut, ite, true))
                                                                        return false;
                                                                PutPage("q\n");
                                                                PutPage(tmpOut);
                                                                PutPage(SetClipPath(ite, false));
                                                                PutPage("S\n");
                                                                PutPage("Q\n");
                                                        }
                                                        else if (ite->lineColor() != CommonStrings::None)
                                                        {
                                                                PutPage(SetClipPath(ite, false));
                                                                PutPage("S\n");
                                                        }
                                                }
                                                else
                                                {
                                                        multiLine ml = doc.docLineStyles[ite->NamedLStyle];
                                                        for (int it = ml.size() - 1; it > -1; it--)
                                                        {
                                                                if ((ml[it].Color != CommonStrings::None) && (ml[it].Width != 0))
                                                                {
                                                                        PutPage(setStrokeMulti(&ml[it]));
                                                                        PutPage(SetClipPath(ite, false));
                                                                        PutPage("S\n");
                                                                }
                                                        }
                                                }
                                        }
                                        if (ite->startArrowIndex() != 0)
                                        {
                                                FPoint Start = ite->PoLine.point(0);
                                                for (int xx = 1; xx < ite->PoLine.size(); xx += 2)
                                                {
                                                        FPoint Vector = ite->PoLine.point(xx);
                                                        if ((Start.x() != Vector.x()) || (Start.y() != Vector.y()))
                                                        {
                                                                double r = atan2(Start.y() - Vector.y(), Start.x() - Vector.x()) * (180.0 / M_PI);
                                                                QTransform arrowTrans;
                                                                arrowTrans.translate(Start.x(), Start.y());
                                                                arrowTrans.rotate(r);
                                                                arrowTrans.scale(ite->startArrowScale() / 100.0, ite->startArrowScale() / 100.0);
                                                                PutPage(drawArrow(ite, arrowTrans, ite->startArrowIndex()));
                                                                break;
                                                        }
                                                }
                                        }
                                        if (ite->endArrowIndex() != 0)
                                        {
                                                FPoint End = ite->PoLine.point(ite->PoLine.size() - 2);
                                                for (uint xx = ite->PoLine.size() - 1; xx > 0; xx -= 2)
                                                {
                                                        FPoint Vector = ite->PoLine.point(xx);
                                                        if ((End.x() != Vector.x()) || (End.y() != Vector.y()))
                                                        {
                                                                double r = atan2(End.y() - Vector.y(), End.x() - Vector.x()) * (180.0 / M_PI);
                                                                QTransform arrowTrans;
                                                                arrowTrans.translate(End.x(), End.y());
                                                                arrowTrans.rotate(r);
                                                                arrowTrans.scale(ite->endArrowScale() / 100.0, ite->endArrowScale() / 100.0);
                                                                PutPage(drawArrow(ite, arrowTrans, ite->endArrowIndex()));
                                                                break;
                                                        }
                                                }
                                        }
                                        break;
                                case PageItem::PathText:
                                case PageItem::OSGFrame:
                                case PageItem::Table:
                                        break;
                                case PageItem::Symbol:
                                        if (doc.docPatterns.contains(ite->pattern()))
                                        {
                                                QByteArray tmpD;
                                                ScPattern pat = doc.docPatterns[ite->pattern()];
                                                PutPage("q\n");
                                                PutPage(SetPathAndClip(ite));
                                                if (ite->imageFlippedH())
                                                        PutPage("-1 0 0 1 " + FToStr(ite->width()) + " 0 cm\n");
                                                if (ite->imageFlippedV())
                                                        PutPage("1 0 0 -1 0 " + FToStr(-ite->height()) + " cm\n");
                                                QTransform trans;
                                                trans.scale(ite->width() / pat.width, ite->height() / pat.height);
                                                trans.translate(0.0, -ite->height());
                                //              trans.translate(pat.items.at(0)->gXpos, -pat.items.at(0)->gYpos);
                                                PutPage(TransformToStr(trans) + " cm\n");
                                                groupStackPos.push(QPointF(0, ite->height()));
                                                for (int em = 0; em < pat.items.count(); ++em)
                                                {
                                                        PageItem* embedded = pat.items.at(em);
                                                        tmpD += "q\n";
                                                        tmpD +=  "1 0 0 1 " + FToStr(embedded->gXpos) + " " + FToStr(ite->height() - embedded->gYpos) + " cm\n";
                                                        QByteArray output;
                                                        if (!PDF_ProcessItem(output, embedded, pag, pag->pageNr(), true))
                                                                return false;
                                                        tmpD += output;
                                                        tmpD += "Q\n";
                                                }
                                                groupStackPos.pop();
                                                if (Options.supportsTransparency())
                                                        PutPage(Write_TransparencyGroup(ite->fillTransparency(), ite->fillBlendmode(), tmpD, ite));
                                                else
                                                        PutPage(Write_FormXObject(tmpD, ite));
                                                PutPage("Q\n");
                                        }
                                        break;
                                case PageItem::Group:
                                        if (ite->groupItemList.count() > 0)
                                        {
                                                QByteArray tmpD;
                                                PutPage("q\n");
                                                if (ite->groupClipping())
                                                        PutPage(SetPathAndClip(ite));
                                                if (ite->imageFlippedH())
                                                        PutPage("-1 0 0 1 " + FToStr(ite->width()) + " 0 cm\n");
                                                if (ite->imageFlippedV())
                                                        PutPage("1 0 0 -1 0 " + FToStr(-ite->height()) + " cm\n");
                                                QTransform trans;
                                                trans.scale(ite->width() / ite->groupWidth, ite->height() / ite->groupHeight);
                                                trans.translate(0.0, -ite->height());
                                                PutPage(TransformToStr(trans) + " cm\n");
                                                groupStackPos.push(QPointF(ite->xPos(), ite->height()));
                                                for (int em = 0; em < ite->groupItemList.count(); ++em)
                                                {
                                                        PageItem* embedded = ite->groupItemList.at(em);
                                                        tmpD += "q\n";
                                                        tmpD +=  "1 0 0 1 " + FToStr(embedded->gXpos) + " " + FToStr(ite->height() - embedded->gYpos) + " cm\n";
                                                        QByteArray output;
                                                        patternStackPos.push(QPointF(embedded->gXpos, ite->height() - embedded->gYpos));
                                                        inPattern++; // We are not really exporting a pattern, but that fix gradient export
                                                        if (!PDF_ProcessItem(output, embedded, pag, pag->pageNr(), true, true))
                                                                return false;
                                                        inPattern--;
                                                        patternStackPos.pop();
                                                        tmpD += output;
                                                        tmpD += "Q\n";
                                                }
                                                groupStackPos.pop();
                                                if (Options.supportsTransparency())
                                                        PutPage(Write_TransparencyGroup(ite->fillTransparency(), ite->fillBlendmode(), tmpD, ite));
                                                else
                                                        PutPage(Write_FormXObject(tmpD, ite));
                                                PutPage("Q\n");
                                        }
                                        break;
                                case PageItem::Multiple:
                                        Q_ASSERT(false);
                                        break;
                                default:
                                        break;
                        }
                        PutPage("Q\n");
                        PdfId templateObject = writer.newObject();
                        writer.startObj(templateObject);
                        PutDoc("<<\n/Type /XObject\n/Subtype /Form\n/FormType 1\n");
                        double bleedRight = 0.0;
                        double bleedLeft  = 0.0;
                        getBleeds(ActPageP, bleedLeft, bleedRight);
                        double maxBoxX = ActPageP->width() + bleedRight + bleedLeft;
                        double maxBoxY = ActPageP->height() + Options.bleeds.top() + Options.bleeds.bottom();
                        PutDoc("/BBox [ " + FToStr(-bleedLeft) + " " + FToStr(-Options.bleeds.bottom()) + " " + FToStr(maxBoxX) + " " + FToStr(maxBoxY) + " ]\n");
//                              PutDoc("/BBox [ 0 0 " + FToStr(ActPageP->width()) + " " + FToStr(ActPageP->height()) + " ]\n");
                               
                        Pdf::ResourceDictionary dict;
                        dict.XObject.unite(pageData.ImgObjects);
                        dict.XObject.unite(pageData.XObjects);
                        dict.Font = pageData.FObjects;
                        dict.Shading = Shadings;
                        dict.Pattern = Patterns;
                        dict.ExtGState = Transpar;
                        dict.ColorSpace.append(asColorSpace(ICCProfiles.values()));
                        dict.ColorSpace.append(asColorSpace(spotMap.values()));
                        writer.write("/Resources ");
                        writer.write(dict);
                               
                        if (Options.Compress)
                                Content = CompressArray(Content);
                        PutDoc("/Length " + Pdf::toPdf(Content.length() + 1));
                        if (Options.Compress)
                                PutDoc("\n/Filter /FlateDecode");
                        PutDoc(" >>\nstream\n" + EncStream(Content, templateObject) + "\nendstream");
                        writer.endObj(templateObject);
                               
                        int pIndex = doc.MasterPages.indexOf((ScPage* const) pag) + 1;
                        QByteArray name = QByteArray("master_page_obj_%1_%2")
                                                        .replace("%1", Pdf::toPdf(pIndex))
                                                        .replace("%2", Pdf::toPdf(qHash(ite)));
                        pageData.XObjects[name] = templateObject;
                }
                if (Options.exportsLayers())
                        PutPage("EMC\n");
        }
        return true;
}



void PDFLibCore::PDF_Begin_Page(const ScPage* pag, const QImage& thumb)
{
        ActPageP = pag;
        Content.clear();
        pageData.AObjects.clear();
        pageData.radioButtonList.clear();
        if (Options.Thumbnails)
        {
                ScImage img(thumb);
                bool compDataAvail = false;
                QByteArray array = img.ImageToArray();
                if (Options.Compress)
                {
                        QByteArray compArray = CompressArray(array);
                        if (compArray.size() > 0)
                        {
                                array = compArray;
                                compDataAvail = true;
                        }
                }
                PdfId thumbnail = writer.newObject();
                writer.startObj(thumbnail);
                PutDoc("<<\n/Width " + Pdf::toPdf(img.width()) + "\n");
                PutDoc("/Height " + Pdf::toPdf(img.height()) + "\n");
                PutDoc("/ColorSpace /DeviceRGB\n/BitsPerComponent 8\n");

                PutDoc(