/* 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. */ #include "pdfoptionsio.h" #include "scribusstructs.h" #include #include #include #include "scclocale.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.isNull()) return false; QFile f(outFileName); if (!f.open(QIODevice::WriteOnly|QIODevice::Truncate)) { m_error = QObject::tr("Could not open output file %1") .arg(qApp->translate("QFile",f.errorString().toLatin1().constData())); return false; } QTextStream ts(&f); ts.setCodec("UTF-8"); 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.isNull()) return false; QTextStream ts(&outDevice); ts.setCodec("UTF-8"); 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( "\n" "\n" ); return xml; } // Build up the stored settings into the passed DOM objects void PDFOptionsIO::buildSettings() { addElem(m_root, "firstUse", m_opts->firstUse); 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); addElem(m_root, "embedPDF", m_opts->embedPDF); 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->fileName); 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); addElem(m_root, "useSpotColors", m_opts->UseSpotColors); addElem(m_root, "doMultiFile", m_opts->doMultiFile); 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->bleeds.Top); addElem(m_root, "bleedLeft", m_opts->bleeds.Left); addElem(m_root, "bleedRight", m_opts->bleeds.Right); addElem(m_root, "bleedBottom", m_opts->bleeds.Bottom); 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 or QStringList as a list of // elements void PDFOptionsIO::addList(QDomElement& addTo, QString name, QList& value) { // List base element has no attributes, only children QDomElement listbase = m_doc.createElement(name); addTo.appendChild(listbase); QList::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 // // XML structure will be like this: // // // // // // // // // // // ... // // ... // QDomElement presentationSettings = m_doc.createElement("presentationSettings"); m_root.appendChild(presentationSettings); QList::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 // // XML structure will be like this: // // // // // // // // ... // // ... // QDomElement lpiSettings = m_doc.createElement("lpiSettings"); m_root.appendChild(lpiSettings); QMap::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(QIODevice::ReadOnly)) { m_error = QObject::tr("Could not open input file %1") .arg(qApp->translate("QFile",f.errorString().toLatin1().constData())); 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, "firstUse", &m_opts->firstUse)) return false; 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", (int*) &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; if (!readElem(m_root, "embedPDF", &m_opts->embedPDF)) m_opts->embedPDF = 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->fileName)) 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 (!readElem(m_root, "useSpotColors", &m_opts->UseSpotColors)) return false; if (!readElem(m_root, "doMultiFile", &m_opts->doMultiFile)) 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->bleeds.Top)) return false; if (!readElem(m_root, "bleedLeft", &m_opts->bleeds.Left)) return false; if (!readElem(m_root, "bleedRight", &m_opts->bleeds.Right)) return false; if (!readElem(m_root, "bleedBottom", &m_opts->bleeds.Bottom)) 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(" 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 = ScCLocale::toDoubleC(elem.attribute("value"), &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.isNull(); if (ok) (*value) = result; return ok; } // Read a stringlist saved as a list of child elements bool PDFOptionsIO::readList(QDomElement& parent, QString name, QList* value) { QDomNode basenode = getUniqueNode(parent, name); QDomElement listbase = getValueElement(basenode, name, false); if (listbase.isNull()) return false; QList 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: // // // // // // // // // // // ... // // ... // QDomNode basenode = getUniqueNode(m_root, "presentationSettings"); QDomElement pSettings = getValueElement(basenode, "presentationSettings", false); if (pSettings.isNull()) return false; QList 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: // // // // // // // // ... // // ... // QDomNode basenode = getUniqueNode(m_root, "lpiSettings"); QDomElement lpiSettings = getValueElement(basenode, "lpiSettings", false); if (lpiSettings.isNull()) return false; QMap 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.isNull()) { m_error = QObject::tr("Unable to read settings XML:") .arg(QObject::tr("element 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; }