Subversion Repositories Scribus

Rev

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

#include "pdfoptionsio.h"
#include "scribusstructs.h"

#include "qapplication.h"

const int PDFOptionsIO::formatVersion = 1300;

PDFOptionsIO::PDFOptionsIO(PDFOptions& opts) :
        m_doc(),
        m_root(),
        m_includePasswords(false),
        m_error()
{
        this->m_opts = &opts;
}

// writeTo(QString) is implemented separately to writeTo(QTextStream)
// because we don't want to clobber the output file until we know the
// data has been generated ok, and we can't avoid clobbering the file
// to create a QTextStream().
bool PDFOptionsIO::writeTo(QString outFileName, bool includePasswords)
{
        m_includePasswords = includePasswords;
        QString xml = buildXMLString();
        if (xml == QString::null)
                return false;
        QFile f(outFileName);
        if (!f.open(IO_WriteOnly|IO_Truncate))
        {
                m_error = QObject::tr("Could not open output file %1")
                        .arg(qApp->translate("QFile",f.errorString()));
                return false;
        }
        QTextStream ts(&f);
        ts.setEncoding(QTextStream::UnicodeUTF8);
        ts << xml;
        m_includePasswords = false; // just to be paranoid
        m_error = QString::null;
        return true;
}

bool PDFOptionsIO::writeTo(QIODevice& outDevice, bool includePasswords)
{
        m_includePasswords = includePasswords;
        if (!outDevice.isWritable())
        {
                m_error = QObject::tr("Output stream not writeable");
                return false;
        }
        QString xml = buildXMLString();
        if (xml == QString::null)
                return false;
        QTextStream ts(&outDevice);
        ts.setEncoding(QTextStream::UnicodeUTF8);
        ts << xml;
        m_includePasswords = false; // just to be paranoid
        m_error = QString::null;
        return true;
}

// Construct and return an XML string representing the settings
QString PDFOptionsIO::buildXMLString()
{
        // Verify to make sure our settings are sane
        QString vrfyError;
        // Make sure the options are sane
        PDFOptions::VerifyResults vr = m_opts->verify(&vrfyError);
        if (vr != PDFOptions::Verify_NoError)
        {
                m_error = QObject::tr("Verification of settings failed: %1").arg(vrfyError);
                return QString::null;
        }
        // Build the document. Initial implementation uses QDom.
        m_doc = QDomDocument();
        m_root = m_doc.createElement("ScribusPDFOptions");
        m_root.setAttribute("version", formatVersion);
        m_doc.appendChild(m_root);
        // Fill the guts of the document
        buildSettings();
        // We're done - return a string containing the document XML
        QString xml = m_doc.toString();
        xml.prepend(
                "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
                "<!DOCTYPE ScribusPDFOptions SYSTEM 'scribuspdfoptions.dtd'>\n"
                );
        return xml;
}

// Build up the stored settings into the passed DOM objects
void PDFOptionsIO::buildSettings()
{
        addElem(m_root, "thumbnails", m_opts->Thumbnails);
        addElem(m_root, "articles", m_opts->Articles);
        addElem(m_root, "useLayers", m_opts->useLayers);
        addElem(m_root, "compress", m_opts->Compress);
        addElem(m_root, "compressMethod", m_opts->CompressMethod);
        addElem(m_root, "quality", m_opts->Quality);
        addElem(m_root, "recalcPic", m_opts->RecalcPic);
        addElem(m_root, "bookmarks", m_opts->Bookmarks);
        addElem(m_root, "picRes", m_opts->PicRes);
        QString pdfVersString;
        switch (m_opts->Version)
        {
                case PDFOptions::PDFVersion_X3:
                        pdfVersString = "X3";
                        break;
                default:
                        pdfVersString = QString::number(m_opts->Version);
                        break;
        }
        addElem(m_root, "pdfVersion", pdfVersString);
        addElem(m_root, "resolution", m_opts->Resolution);
        addElem(m_root, "binding", m_opts->Binding);
        addList(m_root, "embedFonts", m_opts->EmbedList);
        addList(m_root, "subsetFonts", m_opts->SubsetList);
        addElem(m_root, "mirrorH", m_opts->MirrorH);
        addElem(m_root, "mirrorV", m_opts->MirrorV);
        addElem(m_root, "rotateDegrees", m_opts->RotateDeg);
        addElem(m_root, "presentMode", m_opts->PresentMode);
        addPresentationData();
        addElem(m_root, "filename", m_opts->Datei);
        addElem(m_root, "isGrayscale", m_opts->isGrayscale);
        addElem(m_root, "useRGB", m_opts->UseRGB);
        addElem(m_root, "useProfiles", m_opts->UseProfiles);
        addElem(m_root, "useProfiles2", m_opts->UseProfiles2);
        addElem(m_root, "useLPI", m_opts->UseLPI);
        addLPISettings();
        addElem(m_root, "solidProf", m_opts->SolidProf);
        addElem(m_root, "sComp", m_opts->SComp);
        addElem(m_root, "imageProf", m_opts->ImageProf);
        addElem(m_root, "embeddedI", m_opts->EmbeddedI);
        addElem(m_root, "intent2", m_opts->Intent2);
        addElem(m_root, "printProf", m_opts->PrintProf);
        addElem(m_root, "info", m_opts->Info);
        addElem(m_root, "intent", m_opts->Intent);
        addElem(m_root, "bleedTop", m_opts->BleedTop);
        addElem(m_root, "bleedLeft", m_opts->BleedLeft);
        addElem(m_root, "bleedRight", m_opts->BleedRight);
        addElem(m_root, "bleedBottom", m_opts->BleedBottom);
        addElem(m_root, "encrypt", m_opts->Encrypt);
        addElem(m_root, "passOwner", m_includePasswords ? m_opts->PassOwner : "");
        addElem(m_root, "passUser", m_includePasswords ? m_opts->PassUser : "");
        addElem(m_root, "permissions", m_opts->Permissions);
}

// Convenience functions to add a single-attribute element
// of a particular type to the tree at a given point.

void PDFOptionsIO::addElem(QDomElement& addTo, QString name, bool value)
{
        QDomElement elem = m_doc.createElement(name);
        elem.setAttribute("value", value ? "true" : "false" );
        addTo.appendChild(elem);
}

void PDFOptionsIO::addElem(QDomElement& addTo, QString name, QString value)
{
        QDomElement elem = m_doc.createElement(name);
        elem.setAttribute("value",value);
        addTo.appendChild(elem);
}

void PDFOptionsIO::addElem(QDomElement& addTo, QString name, int value)
{
        QDomElement elem = m_doc.createElement(name);
        elem.setAttribute("value",value);
        addTo.appendChild(elem);
}

void PDFOptionsIO::addElem(QDomElement& addTo, QString name, double value)
{
        QDomElement elem = m_doc.createElement(name);
        elem.setAttribute("value",value);
        addTo.appendChild(elem);
}

// Save a QValueList<String> or QStringList as a list of
// <item value=""> elements
void PDFOptionsIO::addList(QDomElement& addTo, QString name, QValueList<QString>& value)
{
        // List base element has no attributes, only children
        QDomElement listbase = m_doc.createElement(name);
        addTo.appendChild(listbase);
        QValueList<QString>::iterator it;
        for (it = value.begin(); it != value.end(); ++it)
                addElem(listbase, "item", *(it));
}

// save PDFOptions::PresentVals
void PDFOptionsIO::addPresentationData()
{
        // PDFOptions::PresentVals is a QValueList<PDFPresentationData>
        //
        // XML structure will be like this:
        // <presentationSettings>
        //   <presentationSettingsEntry>
        //     <pageEffectDuration value="0"/>
        //     <pageViewDuration value="0"/>
        //     <effectType value="0"/>
        //     <dm value="0"/>
        //     <m value="0"/>
        //     <di value="0"/>
        //   </presentationSettingsEntry>
        //   <presentationSettingsEntry>
        //     ...
        //   </presentationSettingsEntry>
        //   ...
        // </presentationSettings>
        QDomElement presentationSettings = m_doc.createElement("presentationSettings");
        m_root.appendChild(presentationSettings);
        QValueList<PDFPresentationData>::iterator it;
        for (it = m_opts->PresentVals.begin(); it != m_opts->PresentVals.end(); ++it)
        {
                // Settings entry has no attributes, only children
                QDomElement psEntry = m_doc.createElement("presentationSettingsEntry");
                presentationSettings.appendChild(psEntry);
                // Children:
                addElem(psEntry, "pageEffectDuration", (*it).pageEffectDuration);
                addElem(psEntry, "pageViewDuration", (*it).pageViewDuration);
                addElem(psEntry, "effectType", (*it).effectType);
                addElem(psEntry, "dm", (*it).Dm);
                addElem(psEntry, "m", (*it).M);
                addElem(psEntry, "di", (*it).Di);
        }
}

// Save PDFOptions::LPISettings
void PDFOptionsIO::addLPISettings()
{
        // PDFOptions::LPISettings is a QMap<QString,LPIData>
        //
        // XML structure will be like this:
        // <lpiSettings>
        //   <lpiSettingsEntry name="blah">
        //     <frequency value="0">
        //     <angle value="0">
        //     <spotFunc value="0">
        //   </lpiSettingsEntry>
        //   <lpiSettingsEntry name="blah2">
        //     ...
        //   </lpiSettingsEntry>
        //   ...
        // </lpiSettings>
        QDomElement lpiSettings = m_doc.createElement("lpiSettings");
        m_root.appendChild(lpiSettings);
        QMap<QString,LPIData>::iterator it;
        for (it = m_opts->LPISettings.begin(); it != m_opts->LPISettings.end(); ++it)
        {
                // Settings entry has no attributes, only children
                QDomElement lpiEntry = m_doc.createElement("lpiSettingsEntry");
                lpiEntry.setAttribute("name", it.key());
                lpiSettings.appendChild(lpiEntry);
                // Children:
                addElem(lpiEntry, "frequency", (*it).Frequency);
                addElem(lpiEntry, "angle", (*it).Angle);
                addElem(lpiEntry, "spotFunc", (*it).SpotFunc);
        }

}

// overload of bool readFrom(QTextStream& inStream)
bool PDFOptionsIO::readFrom(QString inFileName)
{
        QFile f(inFileName);
        if (!f.open(IO_ReadOnly))
        {
                m_error = QObject::tr("Could not open input file %1")
                        .arg(qApp->translate("QFile",f.errorString()));
                return false;
        }
        return readFrom(f);
}

bool PDFOptionsIO::readFrom(QIODevice& inDevice)
{
        if (!inDevice.isReadable())
                return false;
        QString domError;
        int errorLine, errorColumn;
        if (!m_doc.setContent(&inDevice, &domError, &errorLine, &errorColumn))
        {
                m_error = QObject::tr("Unable to read settings XML:")
                        .arg(QObject::tr("%1 (line %2 col %3)", "Load PDF settings")
                                .arg(domError).arg(errorLine).arg(errorColumn)
                        );
                return false;
        }
        if (!readSettings())
                // m_error should already be set
                return false;
        m_error = QString::null;
        return true;
}

// Helper for readFrom(...) to read settings from the DOM
bool PDFOptionsIO::readSettings()
{
        // Get the root element
        m_root = m_doc.documentElement();
        if (m_root.isNull())
        {
                m_error = QObject::tr("Unable to read settings XML: %1")
                        .arg(QObject::tr("null root node", "Load PDF settings"));
        }
        // and start processing elements
        if (!readElem(m_root, "thumbnails", &m_opts->Thumbnails))
                return false;
        if (!readElem(m_root, "articles", &m_opts->Articles))
                return false;
        if (!readElem(m_root, "useLayers", &m_opts->useLayers))
                return false;
        if (!readElem(m_root, "compress", &m_opts->Compress))
                return false;
        if (!readElem(m_root, "compressMethod", &m_opts->CompressMethod))
                return false;
        if (!readElem(m_root, "quality", &m_opts->Quality))
                return false;
        if (!readElem(m_root, "recalcPic", &m_opts->RecalcPic))
                return false;
        if (!readElem(m_root, "bookmarks", &m_opts->Bookmarks))
                return false;
        if (!readElem(m_root, "picRes", &m_opts->PicRes))
                return false;
        readPDFVersion();
        if (!readElem(m_root, "resolution", &m_opts->Resolution))
                return false;
        if (!readElem(m_root, "binding", &m_opts->Binding))
                return false;
        if (!readList(m_root, "embedFonts", &m_opts->EmbedList))
                return false;
        if (!readList(m_root, "subsetFonts", &m_opts->SubsetList))
                return false;
        if (!readElem(m_root, "mirrorH", &m_opts->MirrorH))
                return false;
        if (!readElem(m_root, "mirrorV", &m_opts->MirrorV))
                return false;
        if (!readElem(m_root, "rotateDegrees", &m_opts->RotateDeg))
                return false;
        if (!readElem(m_root, "presentMode", &m_opts->PresentMode))
                return false;
        if (!readPresentationData())
                return false;
        if (!readElem(m_root, "filename", &m_opts->Datei))
                return false;
        if (!readElem(m_root, "isGrayscale", &m_opts->isGrayscale))
                return false;
        if (!readElem(m_root, "useRGB", &m_opts->UseRGB))
                return false;
        if (!readElem(m_root, "useProfiles", &m_opts->UseProfiles))
                return false;
        if (!readElem(m_root, "useProfiles2", &m_opts->UseProfiles2))
                return false;
        if (!readElem(m_root, "useLPI", &m_opts->UseLPI))
                return false;
        if (!readLPISettings())
                return false;
        if (!readElem(m_root, "solidProf", &m_opts->SolidProf))
                return false;
        if (!readElem(m_root, "sComp", &m_opts->SComp))
                return false;
        if (!readElem(m_root, "imageProf", &m_opts->ImageProf))
                return false;
        if (!readElem(m_root, "embeddedI", &m_opts->EmbeddedI))
                return false;
        if (!readElem(m_root, "intent2", &m_opts->Intent2))
                return false;
        if (!readElem(m_root, "printProf", &m_opts->PrintProf))
                return false;
        if (!readElem(m_root, "info", &m_opts->Info))
                return false;
        if (!readElem(m_root, "intent", &m_opts->Intent))
                return false;
        if (!readElem(m_root, "bleedTop", &m_opts->BleedTop))
                return false;
        if (!readElem(m_root, "bleedLeft", &m_opts->BleedLeft))
                return false;
        if (!readElem(m_root, "bleedRight", &m_opts->BleedRight))
                return false;
        if (!readElem(m_root, "bleedBottom", &m_opts->BleedBottom))
                return false;
        if (!readElem(m_root, "encrypt", &m_opts->Encrypt))
                return false;
        if (!readElem(m_root, "passOwner", &m_opts->PassOwner))
                return false;
        if (!readElem(m_root, "passUser", &m_opts->PassUser))
                return false;
        if (!readElem(m_root, "permissions", &m_opts->Permissions))
                return false;
        return true;
}

bool PDFOptionsIO::readPDFVersion()
{
        QString pdfVersString;
        if (!readElem(m_root, "pdfVersion", &pdfVersString))
                return false;
        if (pdfVersString == "X3")
        {
                m_opts->Version = PDFOptions::PDFVersion_X3;
                return true;
        }
        else if (pdfVersString == "13")
        {
                m_opts->Version = PDFOptions::PDFVersion_13;
                return true;
        }
        else if (pdfVersString == "14")
        {
                m_opts->Version = PDFOptions::PDFVersion_14;
                return true;
        }
        else if (pdfVersString == "15")
        {
                m_opts->Version = PDFOptions::PDFVersion_15;
                return true;
        }
        else
        {
                m_error = QObject::tr("Unable to read settings XML:")
                        .arg(QObject::tr("<pdfVersion> invalid", "Load PDF settings"));
                return false;
        }
}

// returns a null node on failure
QDomNode PDFOptionsIO::getUniqueNode(QDomElement& parent, QString name)
{
        QDomNodeList nodes = parent.elementsByTagName(name);
        if (nodes.count() != 1)
        {
                m_error = QObject::tr("Unable to read settings XML:")
                        .arg(QObject::tr("found %1 <%2> nodes, need 1.", "Load PDF settings")
                                .arg(nodes.count()).arg(name)
                        );
                return QDomNode();
        }
        return nodes.item(0);
}

// Return the node as a QDomElement iff it is a QDomElement with
// a `value' attribute; otherwise return a null element.
QDomElement PDFOptionsIO::getValueElement(QDomNode& node, QString name, bool isValue)
{
        if (node.isNull())
        {
                m_error = QObject::tr("Unable to read settings XML:")
                        .arg(QObject::tr("unexpected null <%2> node", "Load PDF settings")
                                .arg(name)
                        );
                return QDomNode().toElement();
        }
        if (!node.isElement())
        {
                m_error = QObject::tr("Unable to read settings XML:")
                        .arg(QObject::tr("node <%1> not an element", "Load PDF settings")
                                .arg(name)
                        );
                return QDomNode().toElement();
        }
        QDomElement elem = node.toElement();
        if (elem.tagName() != name)
        {
                m_error = QObject::tr("Unable to read settings XML:")
                        .arg(QString("Internal error: element named <%1> not expected <%2>")
                                        .arg(elem.tagName()).arg(name)
                        );
                return QDomNode().toElement();
        }
        if (isValue)
        {
                // We need to check that it has a `value' attribute
                if (!elem.hasAttribute("value"))
                {
                        m_error = QObject::tr("Unable to read settings XML:")
                                .arg(QObject::tr("element <%1> lacks `value' attribute", "Load PDF settings")
                                        .arg(name)
                                );
                        return QDomNode().toElement();
                }
        }
        return elem;
}

bool PDFOptionsIO::readElem(QDomElement& parent, QString name, bool* value)
{
        QDomNode node = getUniqueNode(parent, name);
        QDomElement elem = getValueElement(node, name);
        if (elem.isNull())
                return false;
        QString elementText = elem.attribute("value");
        if (elementText == "true")
        {
                (*value) = true;
                return true;
        }
        else if (elementText == "false")
        {
                (*value) = false;
                return true;
        }
        else
        {
                m_error = QObject::tr("Unable to read settings XML:")
                        .arg(QObject::tr("element <%1> value must be `true' or `false'", "Load PDF settings")
                                .arg(name)
                        );
                return false;
        }
}

bool PDFOptionsIO::readElem(QDomElement& parent, QString name, int* value)
{
        QDomNode node = getUniqueNode(parent, name);
        QDomElement elem = getValueElement(node, name);
        if (elem.isNull())
                return false;
        bool ok = false;
        int result = elem.attribute("value").toInt(&ok);
        if (ok)
                (*value) = result;
        return ok;
}

bool PDFOptionsIO::readElem(QDomElement& parent, QString name, double* value)
{
        QDomNode node = getUniqueNode(parent, name);
        QDomElement elem = getValueElement(node, name);
        if (elem.isNull())
                return false;
        bool ok = false;
        double result = elem.attribute("value").toDouble(&ok);
        if (ok)
                (*value) = result;
        return ok;
}

bool PDFOptionsIO::readElem(QDomElement& parent, QString name, QString* value)
{
        QDomNode node = getUniqueNode(parent, name);
        QDomElement elem = getValueElement(node, name);
        if (elem.isNull())
                return false;
        QString result = elem.attribute("value");
        bool ok = result != QString::null;
        if (ok)
                (*value) = result;
        return ok;
}

// Read a stringlist saved as a list of child <item value=""> elements
bool PDFOptionsIO::readList(QDomElement& parent, QString name, QValueList<QString>* value)
{
        QDomNode basenode = getUniqueNode(parent, name);
        QDomElement listbase = getValueElement(basenode, name, false);
        if (listbase.isNull())
                return false;
        QValueList<QString> list;
        for (QDomNode node = listbase.firstChild(); !node.isNull(); node = node.nextSibling())
        {
                QDomElement elem = getValueElement(node, "item");
                if (elem.isNull())
                        return false;
                list.append(elem.attribute("value"));
        }
        (*value) = list;
        return true;
}

bool PDFOptionsIO::readPresentationData()
{
        // XML structure will be like this:
        // <presentationSettings>
        //   <presentationSettingsEntry>
        //     <pageEffectDuration value="0"/>
        //     <pageViewDuration value="0"/>
        //     <effectType value="0"/>
        //     <dm value="0"/>
        //     <m value="0"/>
        //     <di value="0"/>
        //   </presentationSettingsEntry>
        //   <presentationSettingsEntry>
        //     ...
        //   </presentationSettingsEntry>
        //   ...
        // </presentationSettings>
        QDomNode basenode = getUniqueNode(m_root, "presentationSettings");
        QDomElement pSettings = getValueElement(basenode, "presentationSettings", false);
        if (pSettings.isNull())
                return false;
        QValueList<PDFPresentationData> pList;
        for (QDomNode node = pSettings.firstChild(); !node.isNull(); node = node.nextSibling())
        {
                QDomElement elem = getValueElement(basenode, "presentationSettingsEntry", false);
                if (elem.isNull())
                        return false;
                PDFPresentationData pres;
                if (!readElem(elem, "pageEffectDuration", &pres.pageEffectDuration))
                        return false;
                if (!readElem(elem, "pageViewDuration", &pres.pageViewDuration))
                        return false;
                if (!readElem(elem, "effectType", &pres.effectType))
                        return false;
                if (!readElem(elem, "dm", &pres.Dm))
                        return false;
                if (!readElem(elem, "m", &pres.M))
                        return false;
                if (!readElem(elem, "di", &pres.Di))
                        return false;
                pList.append(pres);
        }
        return true;
}

bool PDFOptionsIO::readLPISettings()
{
        // XML structure will be like this:
        // <lpiSettings>
        //   <lpiSettingsEntry name="blah">
        //     <frequency value="0">
        //     <angle value="0">
        //     <spotFunc value="0">
        //   </lpiSettingsEntry>
        //   <lpiSettingsEntry name="blah2">
        //     ...
        //   </lpiSettingsEntry>
        //   ...
        // </lpiSettings>
        QDomNode basenode = getUniqueNode(m_root, "lpiSettings");
        QDomElement lpiSettings = getValueElement(basenode, "lpiSettings", false);
        if (lpiSettings.isNull())
                return false;
        QMap<QString,LPIData> lpiMap;
        for (QDomNode node = lpiSettings.firstChild(); !node.isNull(); node = node.nextSibling())
        {
                QDomElement elem = getValueElement(basenode, "lpiSettingsEntry", false);
                if (elem.isNull())
                        return false;
                QString name = elem.attribute("name");
                if (name == QString::null)
                {
                        m_error = QObject::tr("Unable to read settings XML:")
                                .arg(QObject::tr("element <lpiSettingsEntry> lacks `name' attribute", "Load PDF settings")
                                        .arg(name)
                                );
                        return false;
                }
                LPIData lpi;
                if (!readElem(elem, "frequency", &lpi.Frequency))
                        return false;
                if (!readElem(elem, "angle", &lpi.Angle))
                        return false;
                if (!readElem(elem, "spotFunc", &lpi.SpotFunc))
                        return false;
                lpiMap[name] = lpi;
        }
        return true;
}

const QString& PDFOptionsIO::lastError() const
{
        return m_error;
}