From 6cb46acd673b19dcf35126d7c5947be70d07f3b7 Mon Sep 17 00:00:00 2001 From: Jakub Kucharski Date: Tue, 23 Feb 2016 15:36:43 +0100 Subject: [PATCH 1/7] Added XRef modification flag --- poppler/PDFDoc.cc | 12 +----------- poppler/XRef.cc | 3 +++ poppler/XRef.h | 7 +++++++ 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/poppler/PDFDoc.cc b/poppler/PDFDoc.cc index 8287060..b271970 100644 --- a/poppler/PDFDoc.cc +++ b/poppler/PDFDoc.cc @@ -802,17 +802,7 @@ int PDFDoc::saveAs(GooString *name, PDFWriteMode mode) { } int PDFDoc::saveAs(OutStream *outStr, PDFWriteMode mode) { - - // find if we have updated objects - GBool updated = gFalse; - for(int i=0; igetNumObjects(); i++) { - if (xref->getEntry(i)->getFlag(XRefEntry::Updated)) { - updated = gTrue; - break; - } - } - - if (!updated && mode == writeStandard) { + if (!xref->isModified() && mode == writeStandard) { // simply copy the original file saveWithoutChangesAs (outStr); } else if (mode == writeForceRewrite) { diff --git a/poppler/XRef.cc b/poppler/XRef.cc index 1bbbdc5..0cba3fe 100644 --- a/poppler/XRef.cc +++ b/poppler/XRef.cc @@ -24,6 +24,7 @@ // Copyright (C) 2012, 2013 Fabio D'Urso // Copyright (C) 2013, 2014 Adrian Johnson // Copyright (C) 2013 Pino Toscano +// Copyright (C) 2016 Jakub Kucharski // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -286,6 +287,7 @@ void XRef::init() { entries = NULL; capacity = 0; size = 0; + modified = gFalse; streamEnds = NULL; streamEndsLen = 0; objStrs = new PopplerCache(5); @@ -1356,6 +1358,7 @@ void XRef::setModifiedObject (Object* o, Ref r) { e->obj.free(); o->copy(&(e->obj)); e->setFlag(XRefEntry::Updated, gTrue); + setModified(); } Ref XRef::addIndirectObject (Object* o) { diff --git a/poppler/XRef.h b/poppler/XRef.h index 70065d8..f7e01cc 100644 --- a/poppler/XRef.h +++ b/poppler/XRef.h @@ -22,6 +22,7 @@ // Copyright (C) 2012, 2013 Thomas Freitag // Copyright (C) 2012, 2013 Fabio D'Urso // Copyright (C) 2013 Adrian Johnson +// Copyright (C) 2016 Jakub Kucharski // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -175,6 +176,11 @@ public: XRefEntry *getEntry(int i, GBool complainIfMissing = gTrue); Object *getTrailerDict() { return &trailerDict; } + // Was the XRef modified? + GBool isModified() { return modified; } + // Set the modification flag for XRef to true. + void setModified() { modified = gTrue; } + // Write access void setModifiedObject(Object* o, Ref r); Ref addIndirectObject (Object* o); @@ -202,6 +208,7 @@ private: GBool ok; // true if xref table is valid int errCode; // error code (if is false) Object trailerDict; // trailer dictionary + GBool modified; Goffset *streamEnds; // 'endstream' positions - only used in // damaged files int streamEndsLen; // number of valid entries in streamEnds -- 2.7.1 From f72dee9b8cb311ca2e8cd15c489c5e8df8de3570 Mon Sep 17 00:00:00 2001 From: Jakub Kucharski Date: Tue, 23 Feb 2016 16:46:43 +0100 Subject: [PATCH 2/7] Added DocInfo setters & getters --- poppler/Object.h | 7 +++++- poppler/PDFDoc.cc | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ poppler/PDFDoc.h | 42 ++++++++++++++++++++++++++++++++++++ poppler/XRef.cc | 58 +++++++++++++++++++++++++++++++++++++++++++++++++ poppler/XRef.h | 8 +++++++ 5 files changed, 178 insertions(+), 1 deletion(-) diff --git a/poppler/Object.h b/poppler/Object.h index 1b06a3c..35eb0f8 100644 --- a/poppler/Object.h +++ b/poppler/Object.h @@ -21,6 +21,7 @@ // Copyright (C) 2013 Thomas Freitag // Copyright (C) 2013 Adrian Johnson // Copyright (C) 2013 Adrian Perez de Castro +// Copyright (C) 2016 Jakub Kucharski // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -225,6 +226,7 @@ public: int dictGetLength(); void dictAdd(char *key, Object *val); void dictSet(const char *key, Object *val); + void dictRemove(const char *key); GBool dictIs(const char *dictType); Object *dictLookup(const char *key, Object *obj, int recursion = 0); Object *dictLookupNF(const char *key, Object *obj); @@ -308,7 +310,10 @@ inline void Object::dictAdd(char *key, Object *val) { OBJECT_TYPE_CHECK(objDict); dict->add(key, val); } inline void Object::dictSet(const char *key, Object *val) - { OBJECT_TYPE_CHECK(objDict); dict->set(key, val); } + { OBJECT_TYPE_CHECK(objDict); dict->set(key, val); } + +inline void Object::dictRemove(const char *key) + { OBJECT_TYPE_CHECK(objDict); dict->remove(key); } inline GBool Object::dictIs(const char *dictType) { OBJECT_TYPE_CHECK(objDict); return dict->is(dictType); } diff --git a/poppler/PDFDoc.cc b/poppler/PDFDoc.cc index b271970..a7a1040 100644 --- a/poppler/PDFDoc.cc +++ b/poppler/PDFDoc.cc @@ -32,6 +32,7 @@ // Copyright (C) 2013 Adam Reichold // Copyright (C) 2014 Bogdan Cristea // Copyright (C) 2015 Li Junling +// Copyright (C) 2016 Jakub Kucharski // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -537,6 +538,69 @@ GBool PDFDoc::isLinearized(GBool tryingToReconstruct) { } } +void PDFDoc::setDocInfoModified(Object *infoObj) +{ + Object infoObjRef; + getDocInfoNF(&infoObjRef); + xref->setModifiedObject(infoObj, infoObjRef.getRef()); + infoObjRef.free(); +} + +void PDFDoc::setDocInfoStringEntry(const char *key, GooString *value) +{ + GBool removeEntry = !value || value->getLength() == 0; + + Object infoObj; + getDocInfo(&infoObj); + + if (infoObj.isNull() && removeEntry) { + // No info dictionary, so no entry to remove. + return; + } + + createDocInfoIfNoneExists(&infoObj); + + Object gooStrObj; + if (removeEntry) { + gooStrObj.initNull(); + } else { + gooStrObj.initString(value); + } + + // gooStrObj is set to value or null by now. The latter will cause a removal. + infoObj.dictSet(key, &gooStrObj); + + if (infoObj.dictGetLength() == 0) { + // Info dictionary is empty. Remove it altogether. + removeDocInfo(); + } else { + setDocInfoModified(&infoObj); + } + + infoObj.free(); +} + +GooString *PDFDoc::getDocInfoStringEntry(const char *key) { + Object infoObj; + getDocInfo(&infoObj); + + Object entryObj; + infoObj.dictLookup(key, &entryObj); + + GooString *result; + + if (entryObj.isString()) { + result = new GooString(entryObj.getString()); + } else { + result = NULL; + } + + entryObj.free(); + infoObj.free(); + + return result; +} + static GBool get_id (GooString *encodedidstring, GooString *id) { const char *encodedid = encodedidstring->getCString(); diff --git a/poppler/PDFDoc.h b/poppler/PDFDoc.h index 6c40f7b..c223f73 100644 --- a/poppler/PDFDoc.h +++ b/poppler/PDFDoc.h @@ -27,6 +27,7 @@ // Copyright (C) 2013 Adrian Johnson // Copyright (C) 2013 Adam Reichold // Copyright (C) 2013 Adrian Perez de Castro +// Copyright (C) 2016 Jakub Kucharski // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -225,6 +226,44 @@ public: Object *getDocInfo(Object *obj) { return xref->getDocInfo(obj); } Object *getDocInfoNF(Object *obj) { return xref->getDocInfoNF(obj); } + // Create and return the document's Info dictionary if none exists. + // Otherwise return the existing one. + Object *createDocInfoIfNoneExists(Object *obj) { return xref->createDocInfoIfNoneExists(obj); } + Object *createDocInfoIfNoneExistsNF(Object *obj) { return xref->createDocInfoIfNoneExistsNF(obj); } + + // Remove the document's Info dictionary and update the trailer dictionary. + void removeDocInfo() { xref->removeDocInfo(); } + + // Set doc info string entry. NULL or empty value will cause a removal. + // Takes ownership of value. + void setDocInfoStringEntry(const char *key, GooString *value); + + // Set document's properties in document's Info dictionary. + // NULL or empty value will cause a removal. + // Takes ownership of value. + void setDocInfoTitle(GooString *title) { setDocInfoStringEntry("Title", title); } + void setDocInfoAuthor(GooString *author) { setDocInfoStringEntry("Author", author); } + void setDocInfoSubject(GooString *subject) { setDocInfoStringEntry("Subject", subject); } + void setDocInfoKeywords(GooString *keywords) { setDocInfoStringEntry("Keywords", keywords); } + void setDocInfoCreator(GooString *creator) { setDocInfoStringEntry("Creator", creator); } + void setDocInfoProducer(GooString *producer) { setDocInfoStringEntry("Producer", producer); } + void setDocInfoCreatDate(GooString *creatDate) { setDocInfoStringEntry("CreationDate", creatDate); } + void setDocInfoModDate(GooString *modDate) { setDocInfoStringEntry("ModDate", modDate); } + + // Get document's properties from document's Info dictionary. + // Returns NULL on fail. + // Returned GooStrings should be freed by the caller. + GooString *getDocInfoStringEntry(const char *key); + + GooString *getDocInfoTitle() { return getDocInfoStringEntry("Title"); } + GooString *getDocInfoAuthor() { return getDocInfoStringEntry("Author"); } + GooString *getDocInfoSubject() { return getDocInfoStringEntry("Subject"); } + GooString *getDocInfoKeywords() { return getDocInfoStringEntry("Keywords"); } + GooString *getDocInfoCreator() { return getDocInfoStringEntry("Creator"); } + GooString *getDocInfoProducer() { return getDocInfoStringEntry("Producer"); } + GooString *getDocInfoCreatDate() { return getDocInfoStringEntry("CreationDate"); } + GooString *getDocInfoModDate() { return getDocInfoStringEntry("ModDate"); } + // Return the PDF version specified by the file. int getPDFMajorVersion() { return pdfMajorVersion; } int getPDFMinorVersion() { return pdfMinorVersion; } @@ -309,6 +348,9 @@ private: Goffset getMainXRefEntriesOffset(GBool tryingToReconstruct = gFalse); long long strToLongLong(char *s); + // Mark the document's Info dictionary as modified. + void setDocInfoModified(Object *infoObj); + GooString *fileName; #ifdef _WIN32 wchar_t *fileNameU; diff --git a/poppler/XRef.cc b/poppler/XRef.cc index 0cba3fe..ccca2ca 100644 --- a/poppler/XRef.cc +++ b/poppler/XRef.cc @@ -1275,6 +1275,64 @@ Object *XRef::getDocInfoNF(Object *obj) { return trailerDict.dictLookupNF("Info", obj); } +Object *XRef::createDocInfoIfNoneExists(Object *obj) { + getDocInfo(obj); + + if (!obj->isNull()) { + return obj; + } + + obj->initDict(this); + + Ref ref = addIndirectObject(obj); + + Object objRef; + objRef.initRef(ref.num, ref.gen); + + trailerDict.dictSet("Info", &objRef); + setModified(); + + objRef.free(); + + return obj; +} + +Object *XRef::createDocInfoIfNoneExistsNF(Object *obj) { + getDocInfoNF(obj); + + if (!obj->isNull()) { + return obj; + } + + Object infoObj; + infoObj.initDict(this); + + Ref ref = addIndirectObject(&infoObj); + infoObj.free(); + + Object objRef; + objRef.initRef(ref.num, ref.gen); + + trailerDict.dictSet("Info", &objRef); + setModified(); + + *obj = objRef; + objRef.free(); + + return obj; +} + +void XRef::removeDocInfo() { + Object infoObjRef; + getDocInfoNF(&infoObjRef); + + trailerDict.dictRemove("Info"); + setModified(); + + removeIndirectObject(infoObjRef.getRef()); + infoObjRef.free(); +} + GBool XRef::getStreamEnd(Goffset streamStart, Goffset *streamEnd) { int a, b, m; diff --git a/poppler/XRef.h b/poppler/XRef.h index f7e01cc..d211bcd 100644 --- a/poppler/XRef.h +++ b/poppler/XRef.h @@ -150,6 +150,14 @@ public: Object *getDocInfo(Object *obj); Object *getDocInfoNF(Object *obj); + // Create and return the document's Info dictionary if none exists. + // Otherwise return the existing one. + Object *createDocInfoIfNoneExists(Object *obj); + Object *createDocInfoIfNoneExistsNF(Object *obj); + + // Remove the document's Info dictionary and update the trailer dictionary. + void removeDocInfo(); + // Return the number of objects in the xref table. int getNumObjects() { return size; } -- 2.7.1 From b969930e439f8c4cd39038b6710325cf5bf2a99a Mon Sep 17 00:00:00 2001 From: Jakub Kucharski Date: Tue, 23 Feb 2016 18:17:18 +0100 Subject: [PATCH 3/7] qt4: Added document property setters & getters --- qt4/src/poppler-document.cc | 216 +++++++++++++++++++++++++++++++++++--------- qt4/src/poppler-private.cc | 9 ++ qt4/src/poppler-private.h | 3 + qt4/src/poppler-qt4.h | 93 ++++++++++++++++++- 4 files changed, 276 insertions(+), 45 deletions(-) diff --git a/qt4/src/poppler-document.cc b/qt4/src/poppler-document.cc index 94f997d..d09129c 100644 --- a/qt4/src/poppler-document.cc +++ b/qt4/src/poppler-document.cc @@ -9,6 +9,7 @@ * Copyright (C) 2012 Fabio D'Urso * Copyright (C) 2014 Adam Reichold * Copyright (C) 2015 William Bader + * Copyright (C) 2016 Jakub Kucharski * * 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 @@ -278,37 +279,138 @@ namespace Poppler { return result; } - /* borrowed from kpdf */ QString Document::info( const QString & type ) const { - // [Albert] Code adapted from pdfinfo.cc on xpdf - Object info; - if ( m_doc->locked ) + if (m_doc->locked) { return QString(); + } - QScopedPointer xref(m_doc->doc->getXRef()->copy()); - if (!xref) - return QString(); - xref->getDocInfo(&info); - if ( !info.isDict() ) + QScopedPointer goo(m_doc->doc->getDocInfoStringEntry(type.toLatin1().constData())); + return UnicodeParsedString(goo.data()); + } + + void Document::setInfo( const QString & key, const QString & val ) + { + if (m_doc->locked) { + return; + } + + GooString *goo = QStringToUnicodeGooString(val); + m_doc->doc->setDocInfoStringEntry(key.toLatin1().constData(), goo); + } + + QString Document::title() const + { + if (m_doc->locked) { return QString(); + } - QString result; - Object obj; - GooString *s1; - Dict *infoDict = info.getDict(); + QScopedPointer goo(m_doc->doc->getDocInfoTitle()); + return UnicodeParsedString(goo.data()); + } - if ( infoDict->lookup( type.toLatin1().data(), &obj )->isString() ) - { - s1 = obj.getString(); - result = UnicodeParsedString(s1); - obj.free(); - info.free(); - return result; + void Document::setTitle( const QString & val ) + { + if (m_doc->locked) { + return; } - obj.free(); - info.free(); - return QString(); + + m_doc->doc->setDocInfoTitle(QStringToUnicodeGooString(val)); + } + + QString Document::author() const + { + if (m_doc->locked) { + return QString(); + } + + QScopedPointer goo(m_doc->doc->getDocInfoAuthor()); + return UnicodeParsedString(goo.data()); + } + + void Document::setAuthor( const QString & val ) + { + if (m_doc->locked) { + return; + } + + m_doc->doc->setDocInfoAuthor(QStringToUnicodeGooString(val)); + } + + QString Document::subject() const + { + if (m_doc->locked) { + return QString(); + } + + QScopedPointer goo(m_doc->doc->getDocInfoSubject()); + return UnicodeParsedString(goo.data()); + } + + void Document::setSubject( const QString & val ) + { + if (m_doc->locked) { + return; + } + + m_doc->doc->setDocInfoSubject(QStringToUnicodeGooString(val)); + } + + QString Document::keywords() const + { + if (m_doc->locked) { + return QString(); + } + + QScopedPointer goo(m_doc->doc->getDocInfoKeywords()); + return UnicodeParsedString(goo.data()); + } + + void Document::setKeywords( const QString & val ) + { + if (m_doc->locked) { + return; + } + + m_doc->doc->setDocInfoKeywords(QStringToUnicodeGooString(val)); + } + + QString Document::creator() const + { + if (m_doc->locked) { + return QString(); + } + + QScopedPointer goo(m_doc->doc->getDocInfoCreator()); + return UnicodeParsedString(goo.data()); + } + + void Document::setCreator( const QString & val ) + { + if (m_doc->locked) { + return; + } + + m_doc->doc->setDocInfoCreator(QStringToUnicodeGooString(val)); + } + + QString Document::producer() const + { + if (m_doc->locked) { + return QString(); + } + + QScopedPointer goo(m_doc->doc->getDocInfoProducer()); + return UnicodeParsedString(goo.data()); + } + + void Document::setProducer( const QString & val ) + { + if (m_doc->locked) { + return; + } + + m_doc->doc->setDocInfoProducer(QStringToUnicodeGooString(val)); } QStringList Document::infoKeys() const @@ -337,35 +439,61 @@ namespace Poppler { return keys; } - /* borrowed from kpdf */ QDateTime Document::date( const QString & type ) const { - // [Albert] Code adapted from pdfinfo.cc on xpdf - if ( m_doc->locked ) + if (m_doc->locked) { return QDateTime(); + } - Object info; - QScopedPointer xref(m_doc->doc->getXRef()->copy()); - if (!xref) - return QDateTime(); - xref->getDocInfo(&info); - if ( !info.isDict() ) { - info.free(); + QString str = UnicodeParsedString(m_doc->doc->getDocInfoStringEntry(type.toLatin1().constData())); + return Poppler::convertDate(str.toLatin1().constData()); + } + + void Document::setDate( const QString & key, const QDateTime & val ) + { + if (m_doc->locked) { + return; + } + + m_doc->doc->setDocInfoStringEntry(key.toLatin1().constData(), QDateTimeToUnicodeGooString(val)); + } + + QDateTime Document::creationDate() const + { + if (m_doc->locked) { return QDateTime(); } - Object obj; - Dict *infoDict = info.getDict(); - QDateTime result; + QString str = UnicodeParsedString(m_doc->doc->getDocInfoCreatDate()); + return Poppler::convertDate(str.toLatin1().constData()); + } - if ( infoDict->lookup( type.toLatin1().data(), &obj )->isString() ) - { - char *aux = obj.getString()->getCString(); - result = Poppler::convertDate(aux); + void Document::setCreationDate( const QDateTime & val ) + { + if (m_doc->locked) { + return; } - obj.free(); - info.free(); - return result; + + m_doc->doc->setDocInfoCreatDate(QDateTimeToUnicodeGooString(val)); + } + + QDateTime Document::modDate() const + { + if (m_doc->locked) { + return QDateTime(); + } + + QString str = UnicodeParsedString(m_doc->doc->getDocInfoModDate()); + return Poppler::convertDate(str.toLatin1().constData()); + } + + void Document::setModDate( const QDateTime & val ) + { + if (m_doc->locked) { + return; + } + + m_doc->doc->setDocInfoModDate(QDateTimeToUnicodeGooString(val)); } bool Document::isEncrypted() const @@ -652,7 +780,7 @@ namespace Poppler { return Document::NoForm; // make gcc happy } - QDateTime convertDate( char *dateString ) + QDateTime convertDate( const char *dateString ) { int year, mon, day, hour, min, sec, tzHours, tzMins; char tz; diff --git a/qt4/src/poppler-private.cc b/qt4/src/poppler-private.cc index cbf21b1..d64dca6 100644 --- a/qt4/src/poppler-private.cc +++ b/qt4/src/poppler-private.cc @@ -4,6 +4,7 @@ * Copyright (C) 2008, 2010, 2011 by Pino Toscano * Copyright (C) 2013 by Thomas Freitag * Copyright (C) 2013 Adrian Johnson + * Copyright (C) 2016 Jakub Kucharski * Inspired on code by * Copyright (C) 2004 by Albert Astals Cid * Copyright (C) 2004 by Enrico Ros @@ -153,6 +154,14 @@ namespace Debug { return ret; } + GooString *QDateTimeToUnicodeGooString(const QDateTime &dt) { + if (!dt.isValid()) { + return NULL; + } + + return QStringToUnicodeGooString(dt.toUTC().toString("yyyyMMddhhmmss+00'00'")); + } + void linkActionToTocItem( ::LinkAction * a, DocumentData * doc, QDomElement * e ) { if ( !a || !e ) diff --git a/qt4/src/poppler-private.h b/qt4/src/poppler-private.h index 0d11e83..015f18e 100644 --- a/qt4/src/poppler-private.h +++ b/qt4/src/poppler-private.h @@ -7,6 +7,7 @@ * Copyright (C) 2011 Hib Eris * Copyright (C) 2012, 2013 Thomas Freitag * Copyright (C) 2013 Julien Nabet + * Copyright (C) 2016 Jakub Kucharski * Inspired on code by * Copyright (C) 2004 by Albert Astals Cid * Copyright (C) 2004 by Enrico Ros @@ -61,6 +62,8 @@ namespace Poppler { GooString *QStringToGooString(const QString &s); + GooString *QDateTimeToUnicodeGooString(const QDateTime &dt); + void qt4ErrorFunction(int pos, char *msg, va_list args); class LinkDestinationData diff --git a/qt4/src/poppler-qt4.h b/qt4/src/poppler-qt4.h index c0340a4..fe79bd4 100644 --- a/qt4/src/poppler-qt4.h +++ b/qt4/src/poppler-qt4.h @@ -14,6 +14,7 @@ * Copyright (C) 2012, Tobias Koenig * Copyright (C) 2012, 2014, 2015 Adam Reichold * Copyright (C) 2012, 2013 Thomas Freitag + * Copyright (C) 2016 Jakub Kucharski * * 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 @@ -1082,6 +1083,31 @@ QDateTime modified = m_doc->date("ModDate"); QDateTime date( const QString & data ) const; /** + Set the Info dict date entry specified by \param key to \param val + */ + void setDate( const QString & key, const QDateTime & val ); + + /** + The date of the creation of the document + */ + QDateTime creationDate() const; + + /** + Set the creation date of the document to \param val + */ + void setCreationDate( const QDateTime & val ); + + /** + The date of the last change in the document + */ + QDateTime modDate() const; + + /** + Set the modification date of the document to \param val + */ + void setModDate( const QDateTime & val ); + + /** Get specified information associated with the document You would use this method with something like: @@ -1100,6 +1126,71 @@ QString subject = m_doc->info("Subject"); QString info( const QString & data ) const; /** + Set the value of the document's Info dictionary entry specified by \param key to \param val + */ + void setInfo( const QString & key, const QString & val ); + + /** + The title of the document + */ + QString title() const; + + /** + Set the title of the document to \param val + */ + void setTitle( const QString & val ); + + /** + The author of the document + */ + QString author() const; + + /** + Set the author of the document to \param val + */ + void setAuthor( const QString & val ); + + /** + The subject of the document + */ + QString subject() const; + + /** + Set the subject of the document to \param val + */ + void setSubject( const QString & val ); + + /** + The keywords of the document + */ + QString keywords() const; + + /** + Set the keywords of the document to \param val + */ + void setKeywords( const QString & val ); + + /** + The creator of the document + */ + QString creator() const; + + /** + Set the creator of the document to \param val + */ + void setCreator( const QString & val ); + + /** + The producer of the document + */ + QString producer() const; + + /** + Set the producer of the document to \param val + */ + void setProducer( const QString & val ); + + /** Obtain a list of the available string information keys. */ QStringList infoKeys() const; @@ -1698,7 +1789,7 @@ height = dummy.height(); /** Conversion from PDF date string format to QDateTime */ - POPPLER_QT4_EXPORT QDateTime convertDate( char *dateString ); + POPPLER_QT4_EXPORT QDateTime convertDate( const char *dateString ); /** Whether the color management functions are available. -- 2.7.1 From d8d956dbc0ae77364a13c61865267c76bfd7256f Mon Sep 17 00:00:00 2001 From: Jakub Kucharski Date: Tue, 23 Feb 2016 18:18:12 +0100 Subject: [PATCH 4/7] qt5: Added document property setters & getters --- qt5/src/poppler-document.cc | 216 +++++++++++++++++++++++++++++++++++--------- qt5/src/poppler-private.cc | 9 ++ qt5/src/poppler-private.h | 3 + qt5/src/poppler-qt5.h | 93 ++++++++++++++++++- 4 files changed, 276 insertions(+), 45 deletions(-) diff --git a/qt5/src/poppler-document.cc b/qt5/src/poppler-document.cc index 537dca3..7791ac3 100644 --- a/qt5/src/poppler-document.cc +++ b/qt5/src/poppler-document.cc @@ -9,6 +9,7 @@ * Copyright (C) 2012 Fabio D'Urso * Copyright (C) 2014 Adam Reichold * Copyright (C) 2015 William Bader + * Copyright (C) 2016 Jakub Kucharski * * 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 @@ -264,37 +265,138 @@ namespace Poppler { return result; } - /* borrowed from kpdf */ QString Document::info( const QString & type ) const { - // [Albert] Code adapted from pdfinfo.cc on xpdf - Object info; - if ( m_doc->locked ) + if (m_doc->locked) { return QString(); + } - QScopedPointer xref(m_doc->doc->getXRef()->copy()); - if (!xref) - return QString(); - xref->getDocInfo(&info); - if ( !info.isDict() ) + QScopedPointer goo(m_doc->doc->getDocInfoStringEntry(type.toLatin1().constData())); + return UnicodeParsedString(goo.data()); + } + + void Document::setInfo( const QString & key, const QString & val ) + { + if (m_doc->locked) { + return; + } + + GooString *goo = QStringToUnicodeGooString(val); + m_doc->doc->setDocInfoStringEntry(key.toLatin1().constData(), goo); + } + + QString Document::title() const + { + if (m_doc->locked) { return QString(); + } - QString result; - Object obj; - GooString *s1; - Dict *infoDict = info.getDict(); + QScopedPointer goo(m_doc->doc->getDocInfoTitle()); + return UnicodeParsedString(goo.data()); + } - if ( infoDict->lookup( type.toLatin1().data(), &obj )->isString() ) - { - s1 = obj.getString(); - result = UnicodeParsedString(s1); - obj.free(); - info.free(); - return result; + void Document::setTitle( const QString & val ) + { + if (m_doc->locked) { + return; } - obj.free(); - info.free(); - return QString(); + + m_doc->doc->setDocInfoTitle(QStringToUnicodeGooString(val)); + } + + QString Document::author() const + { + if (m_doc->locked) { + return QString(); + } + + QScopedPointer goo(m_doc->doc->getDocInfoAuthor()); + return UnicodeParsedString(goo.data()); + } + + void Document::setAuthor( const QString & val ) + { + if (m_doc->locked) { + return; + } + + m_doc->doc->setDocInfoAuthor(QStringToUnicodeGooString(val)); + } + + QString Document::subject() const + { + if (m_doc->locked) { + return QString(); + } + + QScopedPointer goo(m_doc->doc->getDocInfoSubject()); + return UnicodeParsedString(goo.data()); + } + + void Document::setSubject( const QString & val ) + { + if (m_doc->locked) { + return; + } + + m_doc->doc->setDocInfoSubject(QStringToUnicodeGooString(val)); + } + + QString Document::keywords() const + { + if (m_doc->locked) { + return QString(); + } + + QScopedPointer goo(m_doc->doc->getDocInfoKeywords()); + return UnicodeParsedString(goo.data()); + } + + void Document::setKeywords( const QString & val ) + { + if (m_doc->locked) { + return; + } + + m_doc->doc->setDocInfoKeywords(QStringToUnicodeGooString(val)); + } + + QString Document::creator() const + { + if (m_doc->locked) { + return QString(); + } + + QScopedPointer goo(m_doc->doc->getDocInfoCreator()); + return UnicodeParsedString(goo.data()); + } + + void Document::setCreator( const QString & val ) + { + if (m_doc->locked) { + return; + } + + m_doc->doc->setDocInfoCreator(QStringToUnicodeGooString(val)); + } + + QString Document::producer() const + { + if (m_doc->locked) { + return QString(); + } + + QScopedPointer goo(m_doc->doc->getDocInfoProducer()); + return UnicodeParsedString(goo.data()); + } + + void Document::setProducer( const QString & val ) + { + if (m_doc->locked) { + return; + } + + m_doc->doc->setDocInfoProducer(QStringToUnicodeGooString(val)); } QStringList Document::infoKeys() const @@ -323,35 +425,61 @@ namespace Poppler { return keys; } - /* borrowed from kpdf */ QDateTime Document::date( const QString & type ) const { - // [Albert] Code adapted from pdfinfo.cc on xpdf - if ( m_doc->locked ) + if (m_doc->locked) { return QDateTime(); + } - Object info; - QScopedPointer xref(m_doc->doc->getXRef()->copy()); - if (!xref) - return QDateTime(); - xref->getDocInfo(&info); - if ( !info.isDict() ) { - info.free(); + QString str = UnicodeParsedString(m_doc->doc->getDocInfoStringEntry(type.toLatin1().constData())); + return Poppler::convertDate(str.toLatin1().constData()); + } + + void Document::setDate( const QString & key, const QDateTime & val ) + { + if (m_doc->locked) { + return; + } + + m_doc->doc->setDocInfoStringEntry(key.toLatin1().constData(), QDateTimeToUnicodeGooString(val)); + } + + QDateTime Document::creationDate() const + { + if (m_doc->locked) { return QDateTime(); } - Object obj; - Dict *infoDict = info.getDict(); - QDateTime result; + QString str = UnicodeParsedString(m_doc->doc->getDocInfoCreatDate()); + return Poppler::convertDate(str.toLatin1().constData()); + } - if ( infoDict->lookup( type.toLatin1().data(), &obj )->isString() ) - { - char *aux = obj.getString()->getCString(); - result = Poppler::convertDate(aux); + void Document::setCreationDate( const QDateTime & val ) + { + if (m_doc->locked) { + return; } - obj.free(); - info.free(); - return result; + + m_doc->doc->setDocInfoCreatDate(QDateTimeToUnicodeGooString(val)); + } + + QDateTime Document::modDate() const + { + if (m_doc->locked) { + return QDateTime(); + } + + QString str = UnicodeParsedString(m_doc->doc->getDocInfoModDate()); + return Poppler::convertDate(str.toLatin1().constData()); + } + + void Document::setModDate( const QDateTime & val ) + { + if (m_doc->locked) { + return; + } + + m_doc->doc->setDocInfoModDate(QDateTimeToUnicodeGooString(val)); } bool Document::isEncrypted() const @@ -633,7 +761,7 @@ namespace Poppler { return Document::NoForm; // make gcc happy } - QDateTime convertDate( char *dateString ) + QDateTime convertDate( const char *dateString ) { int year, mon, day, hour, min, sec, tzHours, tzMins; char tz; diff --git a/qt5/src/poppler-private.cc b/qt5/src/poppler-private.cc index 86855fd..55d8972 100644 --- a/qt5/src/poppler-private.cc +++ b/qt5/src/poppler-private.cc @@ -4,6 +4,7 @@ * Copyright (C) 2008, 2010, 2011, 2014 by Pino Toscano * Copyright (C) 2013 by Thomas Freitag * Copyright (C) 2013 Adrian Johnson + * Copyright (C) 2016 Jakub Kucharski * Inspired on code by * Copyright (C) 2004 by Albert Astals Cid * Copyright (C) 2004 by Enrico Ros @@ -153,6 +154,14 @@ namespace Debug { return ret; } + GooString *QDateTimeToUnicodeGooString(const QDateTime &dt) { + if (!dt.isValid()) { + return NULL; + } + + return QStringToUnicodeGooString(dt.toUTC().toString("yyyyMMddhhmmss+00'00'")); + } + void linkActionToTocItem( ::LinkAction * a, DocumentData * doc, QDomElement * e ) { if ( !a || !e ) diff --git a/qt5/src/poppler-private.h b/qt5/src/poppler-private.h index df6290b..1a83bb6 100644 --- a/qt5/src/poppler-private.h +++ b/qt5/src/poppler-private.h @@ -9,6 +9,7 @@ * Copyright (C) 2013 Anthony Granger * Copyright (C) 2014 Bogdan Cristea * Copyright (C) 2014 Aki Koskinen + * Copyright (C) 2016 Jakub Kucharski * Inspired on code by * Copyright (C) 2004 by Albert Astals Cid * Copyright (C) 2004 by Enrico Ros @@ -63,6 +64,8 @@ namespace Poppler { GooString *QStringToGooString(const QString &s); + GooString *QDateTimeToUnicodeGooString(const QDateTime &dt); + void qt5ErrorFunction(int pos, char *msg, va_list args); class LinkDestinationData diff --git a/qt5/src/poppler-qt5.h b/qt5/src/poppler-qt5.h index b159477..13fbc8d 100644 --- a/qt5/src/poppler-qt5.h +++ b/qt5/src/poppler-qt5.h @@ -15,6 +15,7 @@ * Copyright (C) 2012, 2014, 2015 Adam Reichold * Copyright (C) 2012, 2013 Thomas Freitag * Copyright (C) 2013 Anthony Granger + * Copyright (C) 2016 Jakub Kucharski * * 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 @@ -1072,6 +1073,31 @@ QDateTime modified = m_doc->date("ModDate"); QDateTime date( const QString & data ) const; /** + Set the Info dict date entry specified by \param key to \param val + */ + void setDate( const QString & key, const QDateTime & val ); + + /** + The date of the creation of the document + */ + QDateTime creationDate() const; + + /** + Set the creation date of the document to \param val + */ + void setCreationDate( const QDateTime & val ); + + /** + The date of the last change in the document + */ + QDateTime modDate() const; + + /** + Set the modification date of the document to \param val + */ + void setModDate( const QDateTime & val ); + + /** Get specified information associated with the document You would use this method with something like: @@ -1090,6 +1116,71 @@ QString subject = m_doc->info("Subject"); QString info( const QString & data ) const; /** + Set the value of the document's Info dictionary entry specified by \param key to \param val + */ + void setInfo( const QString & key, const QString & val ); + + /** + The title of the document + */ + QString title() const; + + /** + Set the title of the document to \param val + */ + void setTitle( const QString & val ); + + /** + The author of the document + */ + QString author() const; + + /** + Set the author of the document to \param val + */ + void setAuthor( const QString & val ); + + /** + The subject of the document + */ + QString subject() const; + + /** + Set the subject of the document to \param val + */ + void setSubject( const QString & val ); + + /** + The keywords of the document + */ + QString keywords() const; + + /** + Set the keywords of the document to \param val + */ + void setKeywords( const QString & val ); + + /** + The creator of the document + */ + QString creator() const; + + /** + Set the creator of the document to \param val + */ + void setCreator( const QString & val ); + + /** + The producer of the document + */ + QString producer() const; + + /** + Set the producer of the document to \param val + */ + void setProducer( const QString & val ); + + /** Obtain a list of the available string information keys. */ QStringList infoKeys() const; @@ -1659,7 +1750,7 @@ height = dummy.height(); /** Conversion from PDF date string format to QDateTime */ - POPPLER_QT5_EXPORT QDateTime convertDate( char *dateString ); + POPPLER_QT5_EXPORT QDateTime convertDate( const char *dateString ); /** Whether the color management functions are available. -- 2.7.1 From 8016ffaae45411f126119af89aa33b96b5f4891a Mon Sep 17 00:00:00 2001 From: Jakub Kucharski Date: Tue, 23 Feb 2016 18:22:07 +0100 Subject: [PATCH 5/7] glib: Added document property setters & simplified getters --- glib/poppler-document.cc | 326 ++++++++++++++++++++++++++++++++++------------- glib/poppler-document.h | 19 +++ glib/poppler-private.h | 4 +- 3 files changed, 259 insertions(+), 90 deletions(-) diff --git a/glib/poppler-document.cc b/glib/poppler-document.cc index 61d92e8..0bed943 100644 --- a/glib/poppler-document.cc +++ b/glib/poppler-document.cc @@ -1,6 +1,8 @@ /* poppler-document.cc: glib wrapper for poppler * Copyright (C) 2005, Red Hat, Inc. * + * Copyright (C) 2016 Jakub Kucharski + * * 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) @@ -711,6 +713,10 @@ poppler_document_find_dest (PopplerDocument *document, char *_poppler_goo_string_to_utf8(GooString *s) { + if (s == NULL) { + return NULL; + } + char *result; if (s->hasUnicodeMarker()) { @@ -737,39 +743,27 @@ char *_poppler_goo_string_to_utf8(GooString *s) return result; } -static gchar * -info_dict_get_string (Dict *info_dict, const gchar *key) +static GooString * +_poppler_goo_string_from_utf8(const gchar *src) { - Object obj; - GooString *goo_value; - gchar *result; - - if (!info_dict->lookup ((gchar *)key, &obj)->isString ()) { - obj.free (); + if (src == NULL) { return NULL; } - goo_value = obj.getString (); - result = _poppler_goo_string_to_utf8(goo_value); - obj.free (); + gsize outlen; - return result; -} + gchar *utf16 = g_convert (src, -1, "UTF-16BE", "UTF-8", NULL, &outlen, NULL); + GooString *result = new GooString (utf16, outlen); + g_free (utf16); -static time_t -info_dict_get_date (Dict *info_dict, const gchar *key) -{ - Object obj; - time_t result; + assert(result->getLength() != 0); - if (!info_dict->lookup ((gchar *)key, &obj)->isString ()) { - obj.free (); - return (time_t)-1; + if (!result->hasUnicodeMarker()) { + result->insert(0, 0xff); + result->insert(0, 0xfe); } - if (!_poppler_convert_pdf_date_to_gtime (obj.getString (), &result)) - result = (time_t)-1; - obj.free (); + assert((*result).getLength() != 0); return result; } @@ -879,17 +873,31 @@ poppler_document_get_pdf_version (PopplerDocument *document, gchar * poppler_document_get_title (PopplerDocument *document) { - Object obj; - gchar *retval = NULL; - g_return_val_if_fail (POPPLER_IS_DOCUMENT (document), NULL); - document->doc->getDocInfo (&obj); - if (obj.isDict ()) - retval = info_dict_get_string (obj.getDict(), "Title"); - obj.free (); + GooString *goo_title = document->doc->getDocInfoTitle(); + gchar *utf8 = _poppler_goo_string_to_utf8(goo_title); + delete goo_title; - return retval; + return utf8; +} + +/** + * poppler_document_set_title: + * @document: A #PopplerDocument + * @title: A new title + * + * Sets the document's title. If @title is NULL or its length is 0, Title entry + * is removed from the document's Info dictionary. + * + * Since: 0.42 + **/ +void +poppler_document_set_title (PopplerDocument *document, const gchar *title) +{ + g_return_if_fail (POPPLER_IS_DOCUMENT (document)); + GooString *goo_title = _poppler_goo_string_from_utf8(title); + document->doc->setDocInfoTitle(goo_title); } /** @@ -906,17 +914,31 @@ poppler_document_get_title (PopplerDocument *document) gchar * poppler_document_get_author (PopplerDocument *document) { - Object obj; - gchar *retval = NULL; - g_return_val_if_fail (POPPLER_IS_DOCUMENT (document), NULL); - document->doc->getDocInfo (&obj); - if (obj.isDict ()) - retval = info_dict_get_string (obj.getDict(), "Author"); - obj.free (); + GooString *goo_author = document->doc->getDocInfoAuthor(); + gchar *utf8 = _poppler_goo_string_to_utf8(goo_author); + delete goo_author; - return retval; + return utf8; +} + +/** + * poppler_document_set_author: + * @document: A #PopplerDocument + * @author: A new author + * + * Sets the document's author. If @author is NULL or its length is 0, Author + * entry is removed from the document's Info dictionary. + * + * Since: 0.42 + **/ +void +poppler_document_set_author (PopplerDocument *document, const gchar *author) +{ + g_return_if_fail (POPPLER_IS_DOCUMENT (document)); + GooString *goo_author = _poppler_goo_string_from_utf8(author); + document->doc->setDocInfoAuthor(goo_author); } /** @@ -933,17 +955,31 @@ poppler_document_get_author (PopplerDocument *document) gchar * poppler_document_get_subject (PopplerDocument *document) { - Object obj; - gchar *retval = NULL; - g_return_val_if_fail (POPPLER_IS_DOCUMENT (document), NULL); - document->doc->getDocInfo (&obj); - if (obj.isDict ()) - retval = info_dict_get_string (obj.getDict(), "Subject"); - obj.free (); + GooString *goo_subject = document->doc->getDocInfoSubject(); + gchar *utf8 = _poppler_goo_string_to_utf8(goo_subject); + delete goo_subject; - return retval; + return utf8; +} + +/** + * poppler_document_set_subject: + * @document: A #PopplerDocument + * @subject: A new subject + * + * Sets the document's subject. If @subject is NULL or its length is 0, Subject + * entry is removed from the document's Info dictionary. + * + * Since: 0.42 + **/ +void +poppler_document_set_subject (PopplerDocument *document, const gchar *subject) +{ + g_return_if_fail (POPPLER_IS_DOCUMENT (document)); + GooString *goo_subject = _poppler_goo_string_from_utf8(subject); + document->doc->setDocInfoSubject(goo_subject); } /** @@ -960,17 +996,31 @@ poppler_document_get_subject (PopplerDocument *document) gchar * poppler_document_get_keywords (PopplerDocument *document) { - Object obj; - gchar *retval = NULL; - g_return_val_if_fail (POPPLER_IS_DOCUMENT (document), NULL); - document->doc->getDocInfo (&obj); - if (obj.isDict ()) - retval = info_dict_get_string (obj.getDict(), "Keywords"); - obj.free (); + GooString *goo_keywords = document->doc->getDocInfoKeywords(); + gchar *utf8 = _poppler_goo_string_to_utf8(goo_keywords); + delete goo_keywords; - return retval; + return utf8; +} + +/** + * poppler_document_set_keywords: + * @document: A #PopplerDocument + * @keywords: New keywords + * + * Sets the document's keywords. If @keywords is NULL or its length is 0, + * Keywords entry is removed from the document's Info dictionary. + * + * Since: 0.42 + **/ +void +poppler_document_set_keywords (PopplerDocument *document, const gchar *keywords) +{ + g_return_if_fail (POPPLER_IS_DOCUMENT (document)); + GooString *goo_keywords = _poppler_goo_string_from_utf8(keywords); + document->doc->setDocInfoKeywords(goo_keywords); } /** @@ -989,17 +1039,31 @@ poppler_document_get_keywords (PopplerDocument *document) gchar * poppler_document_get_creator (PopplerDocument *document) { - Object obj; - gchar *retval = NULL; - g_return_val_if_fail (POPPLER_IS_DOCUMENT (document), NULL); - document->doc->getDocInfo (&obj); - if (obj.isDict ()) - retval = info_dict_get_string (obj.getDict(), "Creator"); - obj.free (); + GooString *goo_creator = document->doc->getDocInfoCreator(); + gchar *utf8 = _poppler_goo_string_to_utf8(goo_creator); + delete goo_creator; - return retval; + return utf8; +} + +/** + * poppler_document_set_creator: + * @document: A #PopplerDocument + * @creator: A new creator + * + * Sets the document's creator. If @creator is NULL or its length is 0, Creator + * entry is removed from the document's Info dictionary. + * + * Since: 0.42 + **/ +void +poppler_document_set_creator (PopplerDocument *document, const gchar *creator) +{ + g_return_if_fail (POPPLER_IS_DOCUMENT (document)); + GooString *goo_creator = _poppler_goo_string_from_utf8(creator); + document->doc->setDocInfoCreator(goo_creator); } /** @@ -1018,17 +1082,31 @@ poppler_document_get_creator (PopplerDocument *document) gchar * poppler_document_get_producer (PopplerDocument *document) { - Object obj; - gchar *retval = NULL; - g_return_val_if_fail (POPPLER_IS_DOCUMENT (document), NULL); - document->doc->getDocInfo (&obj); - if (obj.isDict ()) - retval = info_dict_get_string (obj.getDict(), "Producer"); - obj.free (); + GooString *goo_producer = document->doc->getDocInfoProducer(); + gchar *utf8 = _poppler_goo_string_to_utf8(goo_producer); + delete goo_producer; - return retval; + return utf8; +} + +/** + * poppler_document_set_producer: + * @document: A #PopplerDocument + * @producer: A new producer + * + * Sets the document's producer. If @producer is NULL or its length is 0, + * Producer entry is removed from the document's Info dictionary. + * + * Since: 0.42 + **/ +void +poppler_document_set_producer (PopplerDocument *document, const gchar *producer) +{ + g_return_if_fail (POPPLER_IS_DOCUMENT (document)); + GooString *goo_producer = _poppler_goo_string_from_utf8(producer); + document->doc->setDocInfoProducer(goo_producer); } /** @@ -1044,17 +1122,37 @@ poppler_document_get_producer (PopplerDocument *document) time_t poppler_document_get_creation_date (PopplerDocument *document) { - Object obj; - time_t retval = (time_t)-1; + g_return_val_if_fail (POPPLER_IS_DOCUMENT (document), -1); - g_return_val_if_fail (POPPLER_IS_DOCUMENT (document), (time_t)-1); + GooString *str = document->doc->getDocInfoCreatDate(); + if (str == NULL) { + return -1; + } - document->doc->getDocInfo (&obj); - if (obj.isDict ()) - retval = info_dict_get_date (obj.getDict(), "CreationDate"); - obj.free (); + time_t date; + gboolean success = _poppler_convert_pdf_date_to_gtime (str, &date); + delete str; - return retval; + return (success) ? date : -1; +} + +/** + * poppler_document_set_creation_date: + * @document: A #PopplerDocument + * @creation_date: A new creation date + * + * Sets the document's creation date. If @creation_date is -1, CreationDate + * entry is removed from the document's Info dictionary. + * + * Since: 0.42 + **/ +void +poppler_document_set_creation_date (PopplerDocument *document, + time_t creation_date) +{ + g_return_if_fail (POPPLER_IS_DOCUMENT (document)); + GooString *str = _poppler_convert_gtime_to_pdf_date (creation_date); + document->doc->setDocInfoCreatDate (str); } /** @@ -1070,17 +1168,52 @@ poppler_document_get_creation_date (PopplerDocument *document) time_t poppler_document_get_modification_date (PopplerDocument *document) { - Object obj; - time_t retval = (time_t)-1; + g_return_val_if_fail (POPPLER_IS_DOCUMENT (document), -1); - g_return_val_if_fail (POPPLER_IS_DOCUMENT (document), (time_t)-1); + GooString *str = document->doc->getDocInfoModDate(); + if (str == NULL) { + return -1; + } - document->doc->getDocInfo (&obj); - if (obj.isDict ()) - retval = info_dict_get_date (obj.getDict(), "ModDate"); - obj.free (); + time_t date; + gboolean success = _poppler_convert_pdf_date_to_gtime (str, &date); + delete str; - return retval; + return (success) ? date : -1; +} + +/** + * poppler_document_set_modification_date: + * @document: A #PopplerDocument + * @modification_date: A new modification date + * + * Sets the document's modification date. If @modification_date is -1, ModDate + * entry is removed from the document's Info dictionary. + * + * Since: 0.42 + **/ +void +poppler_document_set_modification_date (PopplerDocument *document, + time_t modification_date) +{ + g_return_if_fail (POPPLER_IS_DOCUMENT (document)); + GooString *str = _poppler_convert_gtime_to_pdf_date (modification_date); + document->doc->setDocInfoModDate (str); +} + +/** + * poppler_document_remove_doc_info: + * @document: A #PopplerDocument + * + * Removes the document's Info dictionary + * + * Since: 0.42 + **/ +void +poppler_document_remove_doc_info (PopplerDocument *document) +{ + g_return_if_fail (POPPLER_IS_DOCUMENT (document)); + document->doc->removeDocInfo(); } /** @@ -2698,6 +2831,23 @@ poppler_document_get_form_field (PopplerDocument *document, return NULL; } +GooString * +_poppler_convert_gtime_to_pdf_date (time_t gdate) { + if (gdate == -1) { + return NULL; + } + + struct tm tm; + gmtime_r(&gdate, &tm); + + char str[24]; + sprintf(str, "D:%04d%02d%02d%02d%02d%02d+00'00'", + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); + GooString *goo_str = _poppler_goo_string_from_utf8(str); + + return goo_str; +} + gboolean _poppler_convert_pdf_date_to_gtime (GooString *date, time_t *gdate) diff --git a/glib/poppler-document.h b/glib/poppler-document.h index a34e88c..ec35c2b 100644 --- a/glib/poppler-document.h +++ b/glib/poppler-document.h @@ -1,6 +1,8 @@ /* poppler-document.h: glib interface to poppler * Copyright (C) 2004, Red Hat, Inc. * + * Copyright (C) 2016 Jakub Kucharski + * * 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) @@ -201,13 +203,30 @@ void poppler_document_get_pdf_version (PopplerDocument *doc guint *major_version, guint *minor_version); gchar *poppler_document_get_title (PopplerDocument *document); +void poppler_document_set_title (PopplerDocument *document, + const gchar *title); gchar *poppler_document_get_author (PopplerDocument *document); +void poppler_document_set_author (PopplerDocument *document, + const gchar *author); gchar *poppler_document_get_subject (PopplerDocument *document); +void poppler_document_set_subject (PopplerDocument *document, + const gchar *subject); gchar *poppler_document_get_keywords (PopplerDocument *document); +void poppler_document_set_keywords (PopplerDocument *document, + const gchar *keywords); gchar *poppler_document_get_creator (PopplerDocument *document); +void poppler_document_set_creator (PopplerDocument *document, + const gchar *creator); gchar *poppler_document_get_producer (PopplerDocument *document); +void poppler_document_set_producer (PopplerDocument *document, + const gchar *producer); time_t poppler_document_get_creation_date (PopplerDocument *document); +void poppler_document_set_creation_date (PopplerDocument *document, + time_t creation_date); time_t poppler_document_get_modification_date (PopplerDocument *document); +void poppler_document_set_modification_date (PopplerDocument *document, + time_t modification_date); +void poppler_document_remove_doc_info (PopplerDocument *document); gboolean poppler_document_is_linearized (PopplerDocument *document); PopplerPageLayout poppler_document_get_page_layout (PopplerDocument *document); PopplerPageMode poppler_document_get_page_mode (PopplerDocument *document); diff --git a/glib/poppler-private.h b/glib/poppler-private.h index 9abdd7c..7a3ce71 100644 --- a/glib/poppler-private.h +++ b/glib/poppler-private.h @@ -136,8 +136,8 @@ PopplerAnnot *_poppler_annot_circle_new (Annot *annot); PopplerAnnot *_poppler_annot_square_new (Annot *annot); char *_poppler_goo_string_to_utf8(GooString *s); -gboolean _poppler_convert_pdf_date_to_gtime (GooString *date, - time_t *gdate); +GooString *_poppler_convert_gtime_to_pdf_date (time_t gdate); +gboolean _poppler_convert_pdf_date_to_gtime (GooString *date, time_t *gdate); /* * A convenience macro for boxed type implementations, which defines a -- 2.7.1 From 1821b63f63c26fc7acc9ff011cc0ee2c342df59f Mon Sep 17 00:00:00 2001 From: Jakub Kucharski Date: Tue, 23 Feb 2016 18:24:11 +0100 Subject: [PATCH 6/7] cpp: Added functions to save a document --- cpp/poppler-document.cpp | 31 +++++++++++++++++++++++++++++++ cpp/poppler-document.h | 4 ++++ 2 files changed, 35 insertions(+) diff --git a/cpp/poppler-document.cpp b/cpp/poppler-document.cpp index 1c24b47..830cb6d 100644 --- a/cpp/poppler-document.cpp +++ b/cpp/poppler-document.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2009-2011, Pino Toscano + * Copyright (C) 2016 Jakub Kucharski * * 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 @@ -595,6 +596,36 @@ std::vector document::embedded_files() const } /** + Saves the %document to file \p file_name. + + \returns true on success, false on failure + */ +bool document::save(const std::string &file_name) const +{ + if (d->is_locked) { + return false; + } + + GooString fname(file_name.c_str()); + return d->doc->saveAs(&fname) == errNone; +} + +/** + Saves the original version of the %document to file \p file_name. + + \returns true on success, false on failure + */ +bool document::save_a_copy(const std::string &file_name) const +{ + if (d->is_locked) { + return false; + } + + GooString fname(file_name.c_str()); + return d->doc->saveWithoutChangesAs(&fname) == errNone; +} + +/** Tries to load a PDF %document from the specified file. \param file_name the file to open diff --git a/cpp/poppler-document.h b/cpp/poppler-document.h index dfae398..cd0d3bd 100644 --- a/cpp/poppler-document.h +++ b/cpp/poppler-document.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2009-2010, Pino Toscano + * Copyright (C) 2016 Jakub Kucharski * * 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 @@ -81,6 +82,9 @@ public: bool has_embedded_files() const; std::vector embedded_files() const; + bool save(const std::string &filename) const; + bool save_a_copy(const std::string &filename) const; + static document* load_from_file(const std::string &file_name, const std::string &owner_password = std::string(), const std::string &user_password = std::string()); -- 2.7.1 From d29d3bd8e6955daf1f72627a4fae6a0baec5cd1b Mon Sep 17 00:00:00 2001 From: Jakub Kucharski Date: Tue, 23 Feb 2016 18:27:26 +0100 Subject: [PATCH 7/7] cpp: Added document property setters & getters --- cpp/poppler-document.cpp | 448 ++++++++++++++++++++++++++++++++++++++++++++--- cpp/poppler-document.h | 26 +++ cpp/poppler-private.cpp | 27 ++- cpp/poppler-private.h | 3 + 4 files changed, 478 insertions(+), 26 deletions(-) diff --git a/cpp/poppler-document.cpp b/cpp/poppler-document.cpp index 830cb6d..0935322 100644 --- a/cpp/poppler-document.cpp +++ b/cpp/poppler-document.cpp @@ -329,7 +329,7 @@ std::vector document::info_keys() const } /** - Gets the value of the specified \p key of the document information. + Gets the value of the specified \p key of the %document information. \returns the value for the \p key, or an empty string if not available \see info_keys, info_date @@ -340,28 +340,43 @@ ustring document::info_key(const std::string &key) const return ustring(); } - Object info; - if (!d->doc->getDocInfo(&info)->isDict()) { - info.free(); + std::auto_ptr goo_value(d->doc->getDocInfoStringEntry(key.c_str())); + if (!goo_value.get()) { return ustring(); } - Dict *info_dict = info.getDict(); - Object obj; - ustring result; - if (info_dict->lookup(PSTR(key.c_str()), &obj)->isString()) { - result = detail::unicode_GooString_to_ustring(obj.getString()); + return detail::unicode_GooString_to_ustring(goo_value.get()); +} + +/** + Sets the value of the specified \p key of the %document information to \p val. + If \p val is empty, the entry specified by \p key is removed. + + \returns true on success, false on failure + */ +bool document::set_info_key(const std::string &key, const ustring &val) +{ + if (d->is_locked) { + return false; } - obj.free(); - info.free(); - return result; + + GooString *goo_val; + + if (val.empty()) { + goo_val = NULL; + } else { + goo_val = detail::ustring_to_unicode_GooString(val); + } + + d->doc->setDocInfoStringEntry(key.c_str(), goo_val); + return true; } /** - Gets the time_t value value of the specified \p key of the document + Gets the time_type value value of the specified \p key of the %document information. - \returns the time_t value for the \p key + \returns the time_type value for the \p key \see info_keys, info_date */ time_type document::info_date(const std::string &key) const @@ -370,21 +385,404 @@ time_type document::info_date(const std::string &key) const return time_type(-1); } - Object info; - if (!d->doc->getDocInfo(&info)->isDict()) { - info.free(); + std::auto_ptr goo_date(d->doc->getDocInfoStringEntry(key.c_str())); + if (!goo_date.get()) { return time_type(-1); } - Dict *info_dict = info.getDict(); - Object obj; - time_type result = time_type(-1); - if (info_dict->lookup(PSTR(key.c_str()), &obj)->isString()) { - result = detail::convert_date(obj.getString()->getCString()); + return detail::convert_date(goo_date->getCString()); +} + +/** + Sets the time_type value of the specified \p key of the %document information + to \p val. + If \p val == time_type(-1), the entry specified by \p key is removed. + + \returns true on success, false on failure + */ +bool document::set_info_date(const std::string &key, time_type val) +{ + if (d->is_locked) { + return false; } - obj.free(); - info.free(); - return result; + + GooString *goo_date; + + if (val == time_type(-1)) { + goo_date = NULL; + } else { + goo_date = detail::date_to_GooString(val); + } + + d->doc->setDocInfoStringEntry(key.c_str(), goo_date); + return true; +} + +/** + Gets the %document's title. + + \returns the document's title, or an empty string if not available + \see set_title, info_key + */ +ustring document::get_title() const +{ + if (d->is_locked) { + return ustring(); + } + + std::auto_ptr goo_title(d->doc->getDocInfoTitle()); + if (!goo_title.get()) { + return ustring(); + } + + return detail::unicode_GooString_to_ustring(goo_title.get()); +} + +/** + Sets the %document's title to \p title. + If \p title is empty, the %document's title is removed. + + \returns true on success, false on failure + */ +bool document::set_title(const ustring &title) +{ + if (d->is_locked) { + return false; + } + + GooString *goo_title; + + if (title.empty()) { + goo_title = NULL; + } else { + goo_title = detail::ustring_to_unicode_GooString(title); + } + + d->doc->setDocInfoTitle(goo_title); + return true; +} + +/** + Gets the document's author. + + \returns the document's author, or an empty string if not available + \see set_author, info_key + */ +ustring document::get_author() const +{ + if (d->is_locked) { + return ustring(); + } + + std::auto_ptr goo_author(d->doc->getDocInfoAuthor()); + if (!goo_author.get()) { + return ustring(); + } + + return detail::unicode_GooString_to_ustring(goo_author.get()); +} + +/** + Sets the %document's author to \p author. + If \p author is empty, the %document's author is removed. + + \returns true on success, false on failure + */ +bool document::set_author(const ustring &author) +{ + if (d->is_locked) { + return false; + } + + GooString *goo_author; + + if (author.empty()) { + goo_author = NULL; + } else { + goo_author = detail::ustring_to_unicode_GooString(author); + } + + d->doc->setDocInfoAuthor(goo_author); + return true; +} + +/** + Gets the document's subject. + + \returns the document's subject, or an empty string if not available + \see set_subject, info_key + */ +ustring document::get_subject() const +{ + if (d->is_locked) { + return ustring(); + } + + std::auto_ptr goo_subject(d->doc->getDocInfoSubject()); + if (!goo_subject.get()) { + return ustring(); + } + + return detail::unicode_GooString_to_ustring(goo_subject.get()); +} + +/** + Sets the %document's subject to \p subject. + If \p subject is empty, the %document's subject is removed. + + \returns true on success, false on failure + */ +bool document::set_subject(const ustring &subject) +{ + if (d->is_locked) { + return false; + } + + GooString *goo_subject; + + if (subject.empty()) { + goo_subject = NULL; + } else { + goo_subject = detail::ustring_to_unicode_GooString(subject); + } + + d->doc->setDocInfoSubject(goo_subject); + return true; +} + +/** + Gets the document's keywords. + + \returns the document's keywords, or an empty string if not available + \see set_keywords, info_key + */ +ustring document::get_keywords() const +{ + if (d->is_locked) { + return ustring(); + } + + std::auto_ptr goo_keywords(d->doc->getDocInfoKeywords()); + if (!goo_keywords.get()) { + return ustring(); + } + + return detail::unicode_GooString_to_ustring(goo_keywords.get()); +} + +/** + Sets the %document's keywords to \p keywords. + If \p keywords is empty, the %document's keywords are removed. + + \returns true on success, false on failure + */ +bool document::set_keywords(const ustring &keywords) +{ + if (d->is_locked) { + return false; + } + + GooString *goo_keywords; + + if (keywords.empty()) { + goo_keywords = NULL; + } else { + goo_keywords = detail::ustring_to_unicode_GooString(keywords); + } + + d->doc->setDocInfoKeywords(goo_keywords); + return true; +} + +/** + Gets the document's creator. + + \returns the document's creator, or an empty string if not available + \see set_creator, info_key + */ +ustring document::get_creator() const +{ + if (d->is_locked) { + return ustring(); + } + + std::auto_ptr goo_creator(d->doc->getDocInfoCreator()); + if (!goo_creator.get()) { + return ustring(); + } + + return detail::unicode_GooString_to_ustring(goo_creator.get()); +} + +/** + Sets the %document's creator to \p creator. + If \p creator is empty, the %document's creator is removed. + + \returns true on success, false on failure + */ +bool document::set_creator(const ustring &creator) +{ + if (d->is_locked) { + return false; + } + + GooString *goo_creator; + + if (creator.empty()) { + goo_creator = NULL; + } else { + goo_creator = detail::ustring_to_unicode_GooString(creator); + } + + d->doc->setDocInfoCreator(goo_creator); + return true; +} + +/** + Gets the document's producer. + + \returns the document's producer, or an empty string if not available + \see set_producer, info_key + */ +ustring document::get_producer() const +{ + if (d->is_locked) { + return ustring(); + } + + std::auto_ptr goo_producer(d->doc->getDocInfoProducer()); + if (!goo_producer.get()) { + return ustring(); + } + + return detail::unicode_GooString_to_ustring(goo_producer.get()); +} + +/** + Sets the %document's producer to \p producer. + If \p producer is empty, the %document's producer is removed. + + \returns true on success, false on failure + */ +bool document::set_producer(const ustring &producer) +{ + if (d->is_locked) { + return false; + } + + GooString *goo_producer; + + if (producer.empty()) { + goo_producer = NULL; + } else { + goo_producer = detail::ustring_to_unicode_GooString(producer); + } + + d->doc->setDocInfoProducer(goo_producer); + return true; +} + +/** + Gets the document's creation date as a time_type value. + + \returns the document's creation date as a time_type value + \see set_creation_date, info_date + */ +time_type document::get_creation_date() const +{ + if (d->is_locked) { + return time_type(-1); + } + + std::auto_ptr goo_creation_date(d->doc->getDocInfoCreatDate()); + if (!goo_creation_date.get()) { + return time_type(-1); + } + + return detail::GooString_to_date(goo_creation_date.get()); +} + +/** + Sets the %document's creation date to \p creation_date. + If \p creation_date == time_type(-1), the %document's creation date is removed. + + \returns true on success, false on failure + */ +bool document::set_creation_date(time_type creation_date) +{ + if (d->is_locked) { + return false; + } + + GooString *goo_creation_date; + + if (creation_date == time_type(-1)) { + goo_creation_date = NULL; + } else { + goo_creation_date = detail::date_to_GooString(creation_date); + } + + d->doc->setDocInfoCreatDate(goo_creation_date); + return true; +} + +/** + Gets the document's modification date as a time_type value. + + \returns the document's modification date as a time_type value + \see set_modification_date, info_date + */ +time_type document::get_modification_date() const +{ + if (d->is_locked) { + return time_type(-1); + } + + std::auto_ptr goo_modification_date(d->doc->getDocInfoModDate()); + if (!goo_modification_date.get()) { + return time_type(-1); + } + + return detail::GooString_to_date(goo_modification_date.get()); +} + +/** + Sets the %document's modification date to \p mod_date. + If \p mod_date == time_type(-1), the %document's modification date is removed. + + \returns true on success, false on failure + */ +bool document::set_modification_date(time_type mod_date) +{ + if (d->is_locked) { + return false; + } + + GooString *goo_mod_date; + + if (mod_date == time_type(-1)) { + goo_mod_date = NULL; + } else { + goo_mod_date = detail::date_to_GooString(mod_date); + } + + d->doc->setDocInfoModDate(goo_mod_date); + return true; +} + +/** + Removes the %document's Info dictionary. + + \returns true on success, false on failure + */ +bool document::remove_info() +{ + if (d->is_locked) { + return false; + } + + d->doc->removeDocInfo(); + return true; } /** diff --git a/cpp/poppler-document.h b/cpp/poppler-document.h index cd0d3bd..147c5c5 100644 --- a/cpp/poppler-document.h +++ b/cpp/poppler-document.h @@ -23,6 +23,8 @@ #include "poppler-global.h" #include "poppler-font.h" +class GooString; + namespace poppler { @@ -62,8 +64,32 @@ public: page_layout_enum page_layout() const; void get_pdf_version(int *major, int *minor) const; std::vector info_keys() const; + ustring info_key(const std::string &key) const; + bool set_info_key(const std::string &key, const ustring &val); + time_type info_date(const std::string &key) const; + bool set_info_date(const std::string &key, time_type val); + + ustring get_title() const; + bool set_title(const ustring &title); + ustring get_author() const; + bool set_author(const ustring &author); + ustring get_subject() const; + bool set_subject(const ustring &subject); + ustring get_keywords() const; + bool set_keywords(const ustring &keywords); + ustring get_creator() const; + bool set_creator(const ustring &creator); + ustring get_producer() const; + bool set_producer(const ustring &producer); + time_type get_creation_date() const; + bool set_creation_date(time_type creation_date); + time_type get_modification_date() const; + bool set_modification_date(time_type mod_date); + + bool remove_info(); + bool is_encrypted() const; bool is_linearized() const; bool has_permission(permission_enum which) const; diff --git a/cpp/poppler-private.cpp b/cpp/poppler-private.cpp index 3c3fe95..ade9de8 100644 --- a/cpp/poppler-private.cpp +++ b/cpp/poppler-private.cpp @@ -2,6 +2,7 @@ * Copyright (C) 2009-2010, Pino Toscano * Copyright (C) 2013 Adrian Johnson * Copyright (C) 2014, Hans-Peter Deifel + * Copyright (C) 2016 Jakub Kucharski * * 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 @@ -112,7 +113,7 @@ GooString* detail::ustring_to_unicode_GooString(const ustring &str) ba[i * 2 + 2] = ((*me >> 8) & 0xff); ba[i * 2 + 3] = (*me & 0xff); } - GooString *goo = new GooString(&ba[0]); + GooString *goo = new GooString(&ba[0], 2*str.size() + 2); return goo; } @@ -138,3 +139,27 @@ time_type detail::convert_date(const char *date) time.tm_isdst = -1; return mktime(&time); } + +time_type detail::GooString_to_date(GooString *str) +{ + ustring s = unicode_GooString_to_ustring(str); + return convert_date(s.to_latin1().c_str()); +} + +GooString *detail::date_to_GooString(time_type t) +{ + time_t date = t; + + if (date == -1) { + return NULL; + } + + struct tm tm; + gmtime_r(&date, &tm); + + char str[24]; + sprintf(str, "D:%04d%02d%02d%02d%02d%02d+00'00'", tm.tm_year + 1900, + tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); + + return detail::ustring_to_unicode_GooString(ustring::from_latin1(str)); +} diff --git a/cpp/poppler-private.h b/cpp/poppler-private.h index 8d315c8..3e2391a 100644 --- a/cpp/poppler-private.h +++ b/cpp/poppler-private.h @@ -2,6 +2,7 @@ * Copyright (C) 2009, Pino Toscano * Copyright (C) 2013 Adrian Johnson * Copyright (C) 2014, Hans-Peter Deifel + * Copyright (C) 2016 Jakub Kucharski * * 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 @@ -51,6 +52,8 @@ ustring unicode_to_ustring(const Unicode *u, int length); GooString* ustring_to_unicode_GooString(const ustring &str); time_type convert_date(const char *date); +time_type GooString_to_date(GooString *str); +GooString* date_to_GooString(time_type date); } -- 2.7.1