| /* poppler-document.cc: qt interface to poppler |
| * Copyright (C) 2005, Net Integration Technologies, Inc. |
| * Copyright (C) 2005, 2008, Brad Hards <bradh@frogmouth.net> |
| * Copyright (C) 2005-2010, 2012, 2013, 2015, 2017-2022, Albert Astals Cid <aacid@kde.org> |
| * Copyright (C) 2006-2010, Pino Toscano <pino@kde.org> |
| * Copyright (C) 2010, 2011 Hib Eris <hib@hiberis.nl> |
| * Copyright (C) 2012 Koji Otani <sho@bbr.jp> |
| * Copyright (C) 2012, 2013 Thomas Freitag <Thomas.Freitag@alfa.de> |
| * Copyright (C) 2012 Fabio D'Urso <fabiodurso@hotmail.it> |
| * Copyright (C) 2014, 2018, 2020 Adam Reichold <adam.reichold@t-online.de> |
| * Copyright (C) 2015 William Bader <williambader@hotmail.com> |
| * Copyright (C) 2016 Jakub Alba <jakubalba@gmail.com> |
| * Copyright (C) 2017, 2021 Adrian Johnson <ajohnson@redneon.com> |
| * Copyright (C) 2017 Suzuki Toshiya <mpsuzuki@hiroshima-u.ac.jp> |
| * Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, <info@kdab.com>. Work sponsored by the LiMux project of the city of Munich |
| * Copyright (C) 2019-2021 Oliver Sander <oliver.sander@tu-dresden.de> |
| * Copyright (C) 2019 Alexander Volkov <a.volkov@rusbitech.ru> |
| * Copyright (C) 2020 Philipp Knechtges <philipp-dev@knechtges.com> |
| * Copyright (C) 2020 Katarina Behrens <Katarina.Behrens@cib.de> |
| * Copyright (C) 2020 Thorsten Behrens <Thorsten.Behrens@CIB.de> |
| * Copyright (C) 2021 Mahmoud Khalil <mahmoudkhalil11@gmail.com> |
| * Copyright (C) 2021 Hubert Figuiere <hub@figuiere.net> |
| * |
| * 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, or (at your option) |
| * any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. |
| */ |
| |
| #include "poppler-qt6.h" |
| |
| #include <config.h> |
| #include <poppler-config.h> |
| #include <ErrorCodes.h> |
| #include <GlobalParams.h> |
| #include <Outline.h> |
| #include <PDFDoc.h> |
| #include <Stream.h> |
| #include <Catalog.h> |
| #include <ViewerPreferences.h> |
| #include <DateInfo.h> |
| #include <GfxState.h> |
| |
| #include <QtCore/QDebug> |
| #include <QtCore/QFile> |
| #include <QtCore/QByteArray> |
| |
| #include "poppler-form.h" |
| #include "poppler-private.h" |
| #include "poppler-page-private.h" |
| #include "poppler-outline-private.h" |
| |
| #if defined(USE_CMS) |
| # include <lcms2.h> |
| #endif |
| |
| namespace Poppler { |
| |
| std::unique_ptr<Document> Document::load(const QString &filePath, const QByteArray &ownerPassword, const QByteArray &userPassword) |
| { |
| DocumentData *doc = new DocumentData(filePath, GooString(ownerPassword.data()), GooString(userPassword.data())); |
| return DocumentData::checkDocument(doc); |
| } |
| |
| std::unique_ptr<Document> Document::load(QIODevice *device, const QByteArray &ownerPassword, const QByteArray &userPassword) |
| { |
| DocumentData *doc = new DocumentData(device, GooString(ownerPassword.data()), GooString(userPassword.data())); |
| return DocumentData::checkDocument(doc); |
| } |
| |
| std::unique_ptr<Document> Document::loadFromData(const QByteArray &fileContents, const QByteArray &ownerPassword, const QByteArray &userPassword) |
| { |
| // create stream |
| DocumentData *doc = new DocumentData(fileContents, GooString(ownerPassword.data()), GooString(userPassword.data())); |
| return DocumentData::checkDocument(doc); |
| } |
| |
| std::unique_ptr<Document> DocumentData::checkDocument(DocumentData *doc) |
| { |
| if (doc->doc->isOk() || doc->doc->getErrorCode() == errEncrypted) { |
| auto pdoc = std::unique_ptr<Document>(new Document(doc)); |
| if (doc->doc->getErrorCode() == errEncrypted) { |
| pdoc->m_doc->locked = true; |
| } else { |
| pdoc->m_doc->locked = false; |
| pdoc->m_doc->fillMembers(); |
| } |
| return pdoc; |
| } else { |
| delete doc; |
| } |
| return nullptr; |
| } |
| |
| Document::Document(DocumentData *dataA) |
| { |
| m_doc = dataA; |
| } |
| |
| Document::~Document() |
| { |
| delete m_doc; |
| } |
| |
| std::unique_ptr<Page> Document::page(int index) const |
| { |
| // Cannot call std::make_unique, because the constructor of Page is private |
| auto page = std::unique_ptr<Page>(new Page(m_doc, index)); |
| if (page->m_page->page == nullptr) { |
| page.reset(); |
| } |
| |
| return page; |
| } |
| |
| bool Document::isLocked() const |
| { |
| return m_doc->locked; |
| } |
| |
| bool Document::unlock(const QByteArray &ownerPassword, const QByteArray &userPassword) |
| { |
| if (m_doc->locked) { |
| /* racier then it needs to be */ |
| DocumentData *doc2; |
| if (!m_doc->fileContents.isEmpty()) { |
| doc2 = new DocumentData(m_doc->fileContents, GooString(ownerPassword.data()), GooString(userPassword.data())); |
| } else if (m_doc->m_device) { |
| doc2 = new DocumentData(m_doc->m_device, GooString(ownerPassword.data()), GooString(userPassword.data())); |
| } else { |
| doc2 = new DocumentData(m_doc->m_filePath, GooString(ownerPassword.data()), GooString(userPassword.data())); |
| } |
| if (!doc2->doc->isOk()) { |
| delete doc2; |
| } else { |
| delete m_doc; |
| m_doc = doc2; |
| m_doc->locked = false; |
| m_doc->fillMembers(); |
| } |
| } |
| return m_doc->locked; |
| } |
| |
| Document::PageMode Document::pageMode() const |
| { |
| switch (m_doc->doc->getCatalog()->getPageMode()) { |
| case Catalog::pageModeNone: |
| return UseNone; |
| case Catalog::pageModeOutlines: |
| return UseOutlines; |
| case Catalog::pageModeThumbs: |
| return UseThumbs; |
| case Catalog::pageModeFullScreen: |
| return FullScreen; |
| case Catalog::pageModeOC: |
| return UseOC; |
| case Catalog::pageModeAttach: |
| return UseAttach; |
| default: |
| return UseNone; |
| } |
| } |
| |
| Document::PageLayout Document::pageLayout() const |
| { |
| switch (m_doc->doc->getCatalog()->getPageLayout()) { |
| case Catalog::pageLayoutNone: |
| return NoLayout; |
| case Catalog::pageLayoutSinglePage: |
| return SinglePage; |
| case Catalog::pageLayoutOneColumn: |
| return OneColumn; |
| case Catalog::pageLayoutTwoColumnLeft: |
| return TwoColumnLeft; |
| case Catalog::pageLayoutTwoColumnRight: |
| return TwoColumnRight; |
| case Catalog::pageLayoutTwoPageLeft: |
| return TwoPageLeft; |
| case Catalog::pageLayoutTwoPageRight: |
| return TwoPageRight; |
| default: |
| return NoLayout; |
| } |
| } |
| |
| Qt::LayoutDirection Document::textDirection() const |
| { |
| if (!m_doc->doc->getCatalog()->getViewerPreferences()) { |
| return Qt::LayoutDirectionAuto; |
| } |
| |
| switch (m_doc->doc->getCatalog()->getViewerPreferences()->getDirection()) { |
| case ViewerPreferences::directionL2R: |
| return Qt::LeftToRight; |
| case ViewerPreferences::directionR2L: |
| return Qt::RightToLeft; |
| default: |
| return Qt::LayoutDirectionAuto; |
| } |
| } |
| |
| int Document::numPages() const |
| { |
| return m_doc->doc->getNumPages(); |
| } |
| |
| QList<FontInfo> Document::fonts() const |
| { |
| QList<FontInfo> ourList; |
| FontIterator it(0, m_doc); |
| while (it.hasNext()) { |
| ourList += it.next(); |
| } |
| return ourList; |
| } |
| |
| QList<EmbeddedFile *> Document::embeddedFiles() const |
| { |
| return m_doc->m_embeddedFiles; |
| } |
| |
| std::unique_ptr<FontIterator> Document::newFontIterator(int startPage) const |
| { |
| // Cannot use std::make_unique, because the FontIterator constructor is private |
| return std::unique_ptr<FontIterator>(new FontIterator(startPage, m_doc)); |
| } |
| |
| QByteArray Document::fontData(const FontInfo &fi) const |
| { |
| QByteArray result; |
| if (fi.isEmbedded()) { |
| XRef *xref = m_doc->doc->getXRef()->copy(); |
| |
| Object refObj(fi.m_data->embRef); |
| Object strObj = refObj.fetch(xref); |
| if (strObj.isStream()) { |
| int c; |
| strObj.streamReset(); |
| while ((c = strObj.streamGetChar()) != EOF) { |
| result.append((char)c); |
| } |
| strObj.streamClose(); |
| } |
| delete xref; |
| } |
| return result; |
| } |
| |
| QString Document::info(const QString &type) const |
| { |
| if (m_doc->locked) { |
| return QString(); |
| } |
| |
| std::unique_ptr<GooString> goo(m_doc->doc->getDocInfoStringEntry(type.toLatin1().constData())); |
| return UnicodeParsedString(goo.get()); |
| } |
| |
| bool Document::setInfo(const QString &key, const QString &val) |
| { |
| if (m_doc->locked) { |
| return false; |
| } |
| |
| GooString *goo = QStringToUnicodeGooString(val); |
| m_doc->doc->setDocInfoStringEntry(key.toLatin1().constData(), goo); |
| return true; |
| } |
| |
| QString Document::title() const |
| { |
| if (m_doc->locked) { |
| return QString(); |
| } |
| |
| std::unique_ptr<GooString> goo(m_doc->doc->getDocInfoTitle()); |
| return UnicodeParsedString(goo.get()); |
| } |
| |
| bool Document::setTitle(const QString &val) |
| { |
| if (m_doc->locked) { |
| return false; |
| } |
| |
| m_doc->doc->setDocInfoTitle(QStringToUnicodeGooString(val)); |
| return true; |
| } |
| |
| QString Document::author() const |
| { |
| if (m_doc->locked) { |
| return QString(); |
| } |
| |
| std::unique_ptr<GooString> goo(m_doc->doc->getDocInfoAuthor()); |
| return UnicodeParsedString(goo.get()); |
| } |
| |
| bool Document::setAuthor(const QString &val) |
| { |
| if (m_doc->locked) { |
| return false; |
| } |
| |
| m_doc->doc->setDocInfoAuthor(QStringToUnicodeGooString(val)); |
| return true; |
| } |
| |
| QString Document::subject() const |
| { |
| if (m_doc->locked) { |
| return QString(); |
| } |
| |
| std::unique_ptr<GooString> goo(m_doc->doc->getDocInfoSubject()); |
| return UnicodeParsedString(goo.get()); |
| } |
| |
| bool Document::setSubject(const QString &val) |
| { |
| if (m_doc->locked) { |
| return false; |
| } |
| |
| m_doc->doc->setDocInfoSubject(QStringToUnicodeGooString(val)); |
| return true; |
| } |
| |
| QString Document::keywords() const |
| { |
| if (m_doc->locked) { |
| return QString(); |
| } |
| |
| std::unique_ptr<GooString> goo(m_doc->doc->getDocInfoKeywords()); |
| return UnicodeParsedString(goo.get()); |
| } |
| |
| bool Document::setKeywords(const QString &val) |
| { |
| if (m_doc->locked) { |
| return false; |
| } |
| |
| m_doc->doc->setDocInfoKeywords(QStringToUnicodeGooString(val)); |
| return true; |
| } |
| |
| QString Document::creator() const |
| { |
| if (m_doc->locked) { |
| return QString(); |
| } |
| |
| std::unique_ptr<GooString> goo(m_doc->doc->getDocInfoCreator()); |
| return UnicodeParsedString(goo.get()); |
| } |
| |
| bool Document::setCreator(const QString &val) |
| { |
| if (m_doc->locked) { |
| return false; |
| } |
| |
| m_doc->doc->setDocInfoCreator(QStringToUnicodeGooString(val)); |
| return true; |
| } |
| |
| QString Document::producer() const |
| { |
| if (m_doc->locked) { |
| return QString(); |
| } |
| |
| std::unique_ptr<GooString> goo(m_doc->doc->getDocInfoProducer()); |
| return UnicodeParsedString(goo.get()); |
| } |
| |
| bool Document::setProducer(const QString &val) |
| { |
| if (m_doc->locked) { |
| return false; |
| } |
| |
| m_doc->doc->setDocInfoProducer(QStringToUnicodeGooString(val)); |
| return true; |
| } |
| |
| bool Document::removeInfo() |
| { |
| if (m_doc->locked) { |
| return false; |
| } |
| |
| m_doc->doc->removeDocInfo(); |
| return true; |
| } |
| |
| QStringList Document::infoKeys() const |
| { |
| QStringList keys; |
| |
| if (m_doc->locked) { |
| return QStringList(); |
| } |
| |
| QScopedPointer<XRef> xref(m_doc->doc->getXRef()->copy()); |
| if (!xref) { |
| return QStringList(); |
| } |
| Object info = xref->getDocInfo(); |
| if (!info.isDict()) { |
| return QStringList(); |
| } |
| |
| Dict *infoDict = info.getDict(); |
| // somehow iterate over keys in infoDict |
| keys.reserve(infoDict->getLength()); |
| for (int i = 0; i < infoDict->getLength(); ++i) { |
| keys.append(QString::fromLatin1(infoDict->getKey(i))); |
| } |
| |
| return keys; |
| } |
| |
| QDateTime Document::date(const QString &type) const |
| { |
| if (m_doc->locked) { |
| return QDateTime(); |
| } |
| |
| std::unique_ptr<GooString> goo(m_doc->doc->getDocInfoStringEntry(type.toLatin1().constData())); |
| QString str = UnicodeParsedString(goo.get()); |
| return Poppler::convertDate(str.toLatin1().constData()); |
| } |
| |
| bool Document::setDate(const QString &key, const QDateTime &val) |
| { |
| if (m_doc->locked) { |
| return false; |
| } |
| |
| m_doc->doc->setDocInfoStringEntry(key.toLatin1().constData(), QDateTimeToUnicodeGooString(val)); |
| return true; |
| } |
| |
| QDateTime Document::creationDate() const |
| { |
| if (m_doc->locked) { |
| return QDateTime(); |
| } |
| |
| std::unique_ptr<GooString> goo(m_doc->doc->getDocInfoCreatDate()); |
| QString str = UnicodeParsedString(goo.get()); |
| return Poppler::convertDate(str.toLatin1().constData()); |
| } |
| |
| bool Document::setCreationDate(const QDateTime &val) |
| { |
| if (m_doc->locked) { |
| return false; |
| } |
| |
| m_doc->doc->setDocInfoCreatDate(QDateTimeToUnicodeGooString(val)); |
| return true; |
| } |
| |
| QDateTime Document::modificationDate() const |
| { |
| if (m_doc->locked) { |
| return QDateTime(); |
| } |
| |
| std::unique_ptr<GooString> goo(m_doc->doc->getDocInfoModDate()); |
| QString str = UnicodeParsedString(goo.get()); |
| return Poppler::convertDate(str.toLatin1().constData()); |
| } |
| |
| bool Document::setModificationDate(const QDateTime &val) |
| { |
| if (m_doc->locked) { |
| return false; |
| } |
| |
| m_doc->doc->setDocInfoModDate(QDateTimeToUnicodeGooString(val)); |
| return true; |
| } |
| |
| bool Document::isEncrypted() const |
| { |
| return m_doc->doc->isEncrypted(); |
| } |
| |
| bool Document::isLinearized() const |
| { |
| return m_doc->doc->isLinearized(); |
| } |
| |
| bool Document::okToPrint() const |
| { |
| return m_doc->doc->okToPrint(); |
| } |
| |
| bool Document::okToPrintHighRes() const |
| { |
| return m_doc->doc->okToPrintHighRes(); |
| } |
| |
| bool Document::okToChange() const |
| { |
| return m_doc->doc->okToChange(); |
| } |
| |
| bool Document::okToCopy() const |
| { |
| return m_doc->doc->okToCopy(); |
| } |
| |
| bool Document::okToAddNotes() const |
| { |
| return m_doc->doc->okToAddNotes(); |
| } |
| |
| bool Document::okToFillForm() const |
| { |
| return m_doc->doc->okToFillForm(); |
| } |
| |
| bool Document::okToCreateFormFields() const |
| { |
| return (okToFillForm() && okToChange()); |
| } |
| |
| bool Document::okToExtractForAccessibility() const |
| { |
| return m_doc->doc->okToAccessibility(); |
| } |
| |
| bool Document::okToAssemble() const |
| { |
| return m_doc->doc->okToAssemble(); |
| } |
| |
| Document::PdfVersion Document::getPdfVersion() const |
| { |
| return PdfVersion { m_doc->doc->getPDFMajorVersion(), m_doc->doc->getPDFMinorVersion() }; |
| } |
| |
| std::unique_ptr<Page> Document::page(const QString &label) const |
| { |
| GooString label_g(label.toLatin1().data()); |
| int index; |
| |
| if (!m_doc->doc->getCatalog()->labelToIndex(&label_g, &index)) { |
| std::unique_ptr<GooString> label_ug(QStringToUnicodeGooString(label)); |
| if (!m_doc->doc->getCatalog()->labelToIndex(label_ug.get(), &index)) { |
| return nullptr; |
| } |
| } |
| |
| return page(index); |
| } |
| |
| bool Document::hasEmbeddedFiles() const |
| { |
| return (!(0 == m_doc->doc->getCatalog()->numEmbeddedFiles())); |
| } |
| |
| QVector<OutlineItem> Document::outline() const |
| { |
| QVector<OutlineItem> result; |
| |
| if (::Outline *outline = m_doc->doc->getOutline()) { |
| if (const std::vector<::OutlineItem *> *items = outline->getItems()) { |
| for (void *item : *items) { |
| result.push_back(OutlineItem { new OutlineItemData { static_cast<::OutlineItem *>(item), m_doc } }); |
| } |
| } |
| } |
| |
| return result; |
| } |
| |
| std::unique_ptr<LinkDestination> Document::linkDestination(const QString &name) |
| { |
| GooString *namedDest = QStringToGooString(name); |
| LinkDestinationData ldd(nullptr, namedDest, m_doc, false); |
| auto ld = std::make_unique<LinkDestination>(ldd); |
| delete namedDest; |
| return ld; |
| } |
| |
| void Document::setPaperColor(const QColor &color) |
| { |
| m_doc->setPaperColor(color); |
| } |
| |
| void Document::setColorDisplayProfile(void *outputProfileA) |
| { |
| #if defined(USE_CMS) |
| if (m_doc->m_sRGBProfile && m_doc->m_sRGBProfile.get() == outputProfileA) { |
| // Catch the special case that the user passes the sRGB profile |
| m_doc->m_displayProfile = m_doc->m_sRGBProfile; |
| return; |
| } |
| if (m_doc->m_displayProfile && m_doc->m_displayProfile.get() == outputProfileA) { |
| // Catch the special case that the user passes the display profile |
| return; |
| } |
| m_doc->m_displayProfile = make_GfxLCMSProfilePtr(outputProfileA); |
| #else |
| Q_UNUSED(outputProfileA); |
| #endif |
| } |
| |
| void Document::setColorDisplayProfileName(const QString &name) |
| { |
| #if defined(USE_CMS) |
| void *rawprofile = cmsOpenProfileFromFile(name.toLocal8Bit().constData(), "r"); |
| m_doc->m_displayProfile = make_GfxLCMSProfilePtr(rawprofile); |
| #else |
| Q_UNUSED(name); |
| #endif |
| } |
| |
| void *Document::colorRgbProfile() const |
| { |
| #if defined(USE_CMS) |
| if (!m_doc->m_sRGBProfile) { |
| m_doc->m_sRGBProfile = make_GfxLCMSProfilePtr(cmsCreate_sRGBProfile()); |
| } |
| return m_doc->m_sRGBProfile.get(); |
| #else |
| return nullptr; |
| #endif |
| } |
| |
| void *Document::colorDisplayProfile() const |
| { |
| #if defined(USE_CMS) |
| return m_doc->m_displayProfile.get(); |
| #else |
| return nullptr; |
| #endif |
| } |
| |
| QColor Document::paperColor() const |
| { |
| return m_doc->paperColor; |
| } |
| |
| void Document::setRenderBackend(Document::RenderBackend backend) |
| { |
| // no need to delete the outputdev as for the moment we always create a splash one |
| // as the QPainter one does not allow "precaching" due to its signature |
| // delete m_doc->m_outputDev; |
| // m_doc->m_outputDev = NULL; |
| m_doc->m_backend = backend; |
| } |
| |
| Document::RenderBackend Document::renderBackend() const |
| { |
| return m_doc->m_backend; |
| } |
| |
| QSet<Document::RenderBackend> Document::availableRenderBackends() |
| { |
| QSet<Document::RenderBackend> ret; |
| ret << Document::SplashBackend; |
| ret << Document::QPainterBackend; |
| return ret; |
| } |
| |
| void Document::setRenderHint(Document::RenderHint hint, bool on) |
| { |
| const bool touchesOverprinting = hint & Document::OverprintPreview; |
| |
| int hintForOperation = hint; |
| if (touchesOverprinting && !isOverprintPreviewAvailable()) { |
| hintForOperation = hintForOperation & ~(int)Document::OverprintPreview; |
| } |
| |
| if (on) { |
| m_doc->m_hints |= hintForOperation; |
| } else { |
| m_doc->m_hints &= ~hintForOperation; |
| } |
| } |
| |
| Document::RenderHints Document::renderHints() const |
| { |
| return Document::RenderHints(m_doc->m_hints); |
| } |
| |
| std::unique_ptr<PSConverter> Document::psConverter() const |
| { |
| // Cannot use std::make_unique, because the PSConverter constructor is private |
| return std::unique_ptr<PSConverter>(new PSConverter(m_doc)); |
| } |
| |
| std::unique_ptr<PDFConverter> Document::pdfConverter() const |
| { |
| // Cannot use std::make_unique, because the PDFConverter constructor is private |
| return std::unique_ptr<PDFConverter>(new PDFConverter(m_doc)); |
| } |
| |
| QString Document::metadata() const |
| { |
| QString result; |
| Catalog *catalog = m_doc->doc->getCatalog(); |
| if (catalog && catalog->isOk()) { |
| std::unique_ptr<GooString> s = catalog->readMetadata(); |
| if (s) { |
| result = UnicodeParsedString(s.get()); |
| } |
| } |
| return result; |
| } |
| |
| bool Document::hasOptionalContent() const |
| { |
| return (m_doc->doc->getOptContentConfig() && m_doc->doc->getOptContentConfig()->hasOCGs()); |
| } |
| |
| OptContentModel *Document::optionalContentModel() |
| { |
| if (m_doc->m_optContentModel.isNull()) { |
| m_doc->m_optContentModel = new OptContentModel(m_doc->doc->getOptContentConfig(), nullptr); |
| } |
| return (OptContentModel *)m_doc->m_optContentModel; |
| } |
| |
| QStringList Document::scripts() const |
| { |
| Catalog *catalog = m_doc->doc->getCatalog(); |
| const int numScripts = catalog->numJS(); |
| QStringList scripts; |
| for (int i = 0; i < numScripts; ++i) { |
| GooString *s = catalog->getJS(i); |
| if (s) { |
| scripts.append(UnicodeParsedString(s)); |
| delete s; |
| } |
| } |
| return scripts; |
| } |
| |
| bool Document::getPdfId(QByteArray *permanentId, QByteArray *updateId) const |
| { |
| GooString gooPermanentId; |
| GooString gooUpdateId; |
| |
| if (!m_doc->doc->getID(permanentId ? &gooPermanentId : nullptr, updateId ? &gooUpdateId : nullptr)) { |
| return false; |
| } |
| |
| if (permanentId) { |
| *permanentId = gooPermanentId.c_str(); |
| } |
| if (updateId) { |
| *updateId = gooUpdateId.c_str(); |
| } |
| |
| return true; |
| } |
| |
| Document::FormType Document::formType() const |
| { |
| switch (m_doc->doc->getCatalog()->getFormType()) { |
| case Catalog::NoForm: |
| return Document::NoForm; |
| case Catalog::AcroForm: |
| return Document::AcroForm; |
| case Catalog::XfaForm: |
| return Document::XfaForm; |
| } |
| |
| return Document::NoForm; // make gcc happy |
| } |
| |
| QVector<int> Document::formCalculateOrder() const |
| { |
| Form *form = m_doc->doc->getCatalog()->getForm(); |
| if (!form) { |
| return {}; |
| } |
| |
| QVector<int> result; |
| const std::vector<Ref> &calculateOrder = form->getCalculateOrder(); |
| for (Ref r : calculateOrder) { |
| FormWidget *w = form->findWidgetByRef(r); |
| if (w) { |
| result << w->getID(); |
| } |
| } |
| |
| return result; |
| } |
| |
| std::vector<std::unique_ptr<FormFieldSignature>> Document::signatures() const |
| { |
| std::vector<std::unique_ptr<FormFieldSignature>> result; |
| |
| const std::vector<::FormFieldSignature *> pSignatures = m_doc->doc->getSignatureFields(); |
| |
| for (::FormFieldSignature *pSignature : pSignatures) { |
| ::FormWidget *fw = pSignature->getCreateWidget(); |
| ::Page *p = m_doc->doc->getPage(fw->getWidgetAnnotation()->getPageNum()); |
| result.push_back(std::make_unique<FormFieldSignature>(m_doc, p, static_cast<FormWidgetSignature *>(fw))); |
| } |
| |
| return result; |
| } |
| |
| bool Document::xrefWasReconstructed() const |
| { |
| return m_doc->xrefReconstructed; |
| } |
| |
| void Document::setXRefReconstructedCallback(const std::function<void()> &callback) |
| { |
| m_doc->xrefReconstructedCallback = callback; |
| } |
| |
| QDateTime convertDate(const char *dateString) |
| { |
| int year, mon, day, hour, min, sec, tzHours, tzMins; |
| char tz; |
| |
| GooString date(dateString); |
| if (parseDateString(&date, &year, &mon, &day, &hour, &min, &sec, &tz, &tzHours, &tzMins)) { |
| QDate d(year, mon, day); |
| QTime t(hour, min, sec); |
| if (d.isValid() && t.isValid()) { |
| QDateTime dt(d, t, Qt::UTC); |
| if (tz) { |
| // then we have some form of timezone |
| if ('Z' == tz) { |
| // We are already at UTC |
| } else if ('+' == tz) { |
| // local time is ahead of UTC |
| dt = dt.addSecs(-1 * ((tzHours * 60) + tzMins) * 60); |
| } else if ('-' == tz) { |
| // local time is behind UTC |
| dt = dt.addSecs(((tzHours * 60) + tzMins) * 60); |
| } else { |
| qWarning("unexpected tz val"); |
| } |
| } |
| return dt; |
| } |
| } |
| return QDateTime(); |
| } |
| |
| bool isCmsAvailable() |
| { |
| #if defined(USE_CMS) |
| return true; |
| #else |
| return false; |
| #endif |
| } |
| |
| bool isOverprintPreviewAvailable() |
| { |
| return true; |
| } |
| |
| } |