summaryrefslogtreecommitdiffstats
path: root/tools/kfile-plugins/ooo/kfile_ooo.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tools/kfile-plugins/ooo/kfile_ooo.cpp')
-rw-r--r--tools/kfile-plugins/ooo/kfile_ooo.cpp588
1 files changed, 588 insertions, 0 deletions
diff --git a/tools/kfile-plugins/ooo/kfile_ooo.cpp b/tools/kfile-plugins/ooo/kfile_ooo.cpp
new file mode 100644
index 000000000..4f16a0af5
--- /dev/null
+++ b/tools/kfile-plugins/ooo/kfile_ooo.cpp
@@ -0,0 +1,588 @@
+/* This file is part of the KDE project
+ * Copyright (C) 2003 Pierre Souchay <pierre@souchay.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 version 2.
+ *
+ * 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; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+/**
+ * CHANGES
+ * v1.7 to 1.9
+ * Added the editing duration analysis.
+ * v1.6 to 1.7
+ * Changed the incorrect meta:date to dc:date
+ * Now parse date instead of simply print the ISO date
+ * Fixes bug with keywords, now properly handled
+ * Changed the deprecated upload method to the new one.
+ * v1.5 to 1.6
+ * Correction for bug 68112: http://bugs.kde.org/show_bug.cgi?id=68112
+ * OOo 1.1 does not support garbages in zip files. We now recreate the zip
+ * file from scratch. It is slower, but we don't have the choice :(
+ * v1.1 to v1.2
+ * Added support for KFileMimeTypeInfo::Hints (Name, Author, Description)
+ * Added some Advanced Attributes and Statistics according to DTD of OOo
+ * No more duplicated strings of same contents to facilitate changes
+ */
+
+#include <config.h>
+#include "kfile_ooo.h"
+
+#include <klocale.h>
+#include <kgenericfactory.h>
+#include <KoStore.h>
+#include <KoStoreDevice.h>
+#include <kzip.h>
+#include <ktempfile.h>
+#include <qptrstack.h>
+
+#include <qdom.h>
+#include <qfile.h>
+#include <qdatetime.h>
+#include <qvalidator.h>
+#include <kdebug.h>
+#include <kio/netaccess.h>
+
+typedef KGenericFactory<KOfficePlugin> KOfficeFactory;
+
+K_EXPORT_COMPONENT_FACTORY(kfile_ooo, KOfficeFactory( "kfile_ooo" ))
+
+static const char * const mimetypes[] =
+{ "application/vnd.sun.xml.calc", "application/vnd.sun.xml.calc.template",
+ "application/vnd.sun.xml.draw", "application/vnd.sun.xml.draw.template",
+ "application/vnd.sun.xml.impress","application/vnd.sun.xml.impress.template",
+ "application/vnd.sun.xml.writer", "application/vnd.sun.xml.writer.global",
+ "application/vnd.sun.xml.writer.math",
+ "application/vnd.sun.xml.writer.template",
+ "application/vnd.oasis.opendocument.chart",
+ "application/vnd.oasis.opendocument.formula",
+ "application/vnd.oasis.opendocument.graphics",
+ "application/vnd.oasis.opendocument.graphics-template",
+ "application/vnd.oasis.opendocument.presentation",
+ "application/vnd.oasis.opendocument.presentation-template",
+ "application/vnd.oasis.opendocument.spreadsheet",
+ "application/vnd.oasis.opendocument.spreadsheet-template",
+ "application/vnd.oasis.opendocument.text",
+ "application/vnd.oasis.opendocument.text-template",
+ 0};
+
+static const char * const Advanced[] =
+ {
+ "meta:printed-by" , I18N_NOOP("Printed By"),
+ "meta:print-date" , I18N_NOOP("Print Date"),
+ "dc:date" , I18N_NOOP("Date"),
+ "meta:creation-date" , I18N_NOOP("Creation Date"),
+ "meta:initial-creator", I18N_NOOP("Creator"),
+ "meta:generator" , I18N_NOOP("Generator"),
+ "meta:editing-cycles" , I18N_NOOP("Editing Cycles"),
+ "meta:editing-duration" , I18N_NOOP("Editing Duration"),
+ 0};
+
+static const char * dclanguage = "dc:language";
+
+static const char * const Information[] =
+ {"dc:title" , I18N_NOOP("Title") ,
+ "dc:creator" , I18N_NOOP("Author") ,
+ "dc:description", I18N_NOOP("Description"),
+ "dc:subject" , I18N_NOOP("Subject") ,
+ dclanguage , I18N_NOOP("Language") ,
+ 0};
+
+static const char * const Statistics[] =
+ {"meta:draw-count" , I18N_NOOP("Draws"),
+ "meta:table-count" , I18N_NOOP("Tables"),
+ "meta:image-count" , I18N_NOOP("Images"),
+ "meta:object-count" , I18N_NOOP("Objects"),
+ "meta:ole-object-count", I18N_NOOP("OLE Objects"),
+ "meta:page-count" , I18N_NOOP("Pages"),
+ "meta:paragraph-count" , I18N_NOOP("Paragraphs"),
+ "meta:word-count" , I18N_NOOP("Words"),
+ "meta:cell-count" , I18N_NOOP("Cells"),
+ "meta:character-count" , I18N_NOOP("Characters"),
+ "meta:row-count" , I18N_NOOP("Rows"),
+ 0};
+
+static const char * metakeywords = "meta:keywords";
+static const char * metakeyword = "meta:keyword" ;
+static const char * DocumentInfo = "DocumentInfo" ;
+static const char * UserDefined = "UserDefined" ;
+static const char * DocAdvanced = "Advanced" ;
+static const char * DocStatistics = "Statistics" ;
+static const char * metadocstat = "meta:document-statistic";
+static const char * metaname = "meta:name" ;
+static const char * metauserdef = "meta:user-defined";
+static const char * metafile = "meta.xml" ;
+KOfficePlugin::KOfficePlugin(QObject *parent, const char *name,
+ const QStringList &args)
+
+ : KFilePlugin(parent, name, args)
+{
+ int i = 0;
+ while (mimetypes[i])
+ makeMimeTypeInfo( mimetypes[i++] );
+}
+
+void KOfficePlugin::makeMimeTypeInfo(const QString& mimeType)
+{
+ KFileMimeTypeInfo* info = addMimeTypeInfo( mimeType );
+
+ userdefined = addGroupInfo(info, UserDefined, i18n("User Defined"));
+ addVariableInfo(userdefined, QVariant::String,
+ KFileMimeTypeInfo::Addable |
+ KFileMimeTypeInfo::Removable |
+ KFileMimeTypeInfo::Modifiable);
+
+ KFileMimeTypeInfo::GroupInfo* group = 0L;
+ group = addGroupInfo(info, DocumentInfo, i18n("Document Information"));
+ KFileMimeTypeInfo::ItemInfo* item;
+ int i = 0;
+ for (i = 0; Information[i]; i+=2){
+ item = addItemInfo(group, Information[i], i18n(Information[i+1]),
+ QVariant::String);
+ setAttributes(item, KFileMimeTypeInfo::Modifiable);
+ switch (i){
+ case 0:
+ setHint(item, KFileMimeTypeInfo::Name);
+ break;
+ case 1:
+ setHint(item, KFileMimeTypeInfo::Author);
+ break;
+ case 2:
+ setHint(item, KFileMimeTypeInfo::Description);
+ default:;
+ }
+ }
+
+ item = addItemInfo(group, metakeyword, i18n("Keywords"),
+ QVariant::String);
+ setHint(item, KFileMimeTypeInfo::Description);
+ setAttributes(item, KFileMimeTypeInfo::Modifiable);
+
+ group = addGroupInfo(info, DocAdvanced, i18n("Document Advanced"));
+ for (i = 0; Advanced[i]; i+=2){
+ // I should add the isDate property instead of testing the index, but it works well, who cares ? :-)
+ QVariant::Type typ = QVariant::String;
+ if (i > 1 && i < 8)
+ typ = QVariant::DateTime;
+ if (i == 14)
+ typ = QVariant::String;
+ item = addItemInfo(group, Advanced[i], i18n(Advanced[i+1]), typ);
+ setHint(item, KFileMimeTypeInfo::Description);
+ }
+
+ group = addGroupInfo(info, DocStatistics, i18n("Document Statistics"));
+ for (i = 0; Statistics[i]; i+=2){
+ item = addItemInfo(group, Statistics[i], i18n(Statistics[i+1]),
+ QVariant::Int);
+ setHint(item, KFileMimeTypeInfo::Length);
+ }
+}
+
+/**
+ * Gets a number in the string and update the position next character which
+ * is not a number
+ * @param str The string to check
+ * @param the position to start to, updated to the next character NAN
+ * @return the number parsed, 0 if number was not valid
+ */
+int getNumber(QString &str, int * pos){
+ int k = *pos;
+ for (int len = str.length() ;
+ str.at(k).isNumber() && k < len ;
+ k++);
+ bool result = false;
+ int num = str.mid( *pos, k-(*pos)).toInt(&result);
+ *pos = k;
+ if (!result)
+ return 0;
+ return num;
+}
+
+void KOfficePlugin::getEditingTime(KFileMetaInfoGroup group1,
+ const char * labelid, QString & txt){
+ QString t;
+ int days = 0;
+ int hours = 0;
+ int minutes = 0;
+ int seconds = 0;
+ if (txt.at(0) != 'P'){
+ kdDebug(7034) << labelid << "=" << txt <<
+ " does not seems to be a valid duration" << endl;
+ return;
+ }
+ int pos = 1;
+ if (txt.at(pos).isNumber()){
+ days = getNumber(txt, &pos);
+ if (txt.at(pos++)!='D'){
+ days=0;
+ kdDebug(7034) << labelid <<
+ " First arg was not a day in " << txt << endl;
+ }
+ }
+ if (txt.at(pos)!= 'T'){
+ kdDebug(7034) << labelid << "=" << txt <<
+ " does not seems to contain time information" << endl;
+ return;
+ }
+ pos++;
+ int len = txt.length();
+ while (pos < len){
+ int res = getNumber(txt, &pos);
+ if (pos >= len)
+ return;
+ switch (txt.at(pos).latin1()){
+ case 'H':
+ hours = res;
+ break;
+ case 'M':
+ minutes = res;
+ break;
+ case 'S':
+ seconds = res;
+ break;
+ default:
+ kdDebug(7034) << "Unknown unit at pos " << pos << " while parsing " <<
+ labelid << "="<< txt << endl;
+ }
+ pos++;
+ }
+ hours += days * 24;
+ appendItem(group1, labelid,
+ i18n("%1:%2.%3").arg(hours).arg(minutes, 2).arg(seconds,2 ));
+}
+
+void KOfficePlugin::getDateTime(KFileMetaInfoGroup group1,
+ const char * labelid, QString & txt)
+{
+ QDateTime dt = QDateTime::fromString( txt, Qt::ISODate);
+ appendItem( group1, labelid, dt);
+}
+
+bool KOfficePlugin::readInfo( KFileMetaInfo& info, uint /*what*/)
+{
+ if ( info.path().isEmpty() ) // remote file
+ return false;
+
+ KFileMetaInfoGroup group = appendGroup(info, DocumentInfo);
+ QDomDocument doc = getMetaDocument(info.path());
+ if (doc.isNull())
+ return false;
+ QDomElement base = getBaseNode(doc).toElement();
+ if (base.isNull())
+ return false;
+ for (int i = 0; Information[i]; i+=2)
+ appendItem(group, Information[i],
+ stringFromNode(base, Information[i]));
+ // Special case for keyword
+ QDomNodeList keywordList = base.elementsByTagName( metakeyword );
+ QString allKeywords;
+ for (uint i = 0; i < keywordList.length(); i++){
+ QDomNode node = keywordList.item(i);
+ if (node.isElement()){
+ if (i>0)
+ allKeywords += ", ";
+ allKeywords += node.toElement().text();
+ }
+ }
+ appendItem(group, metakeyword,
+ allKeywords);
+
+ KFileMetaInfoGroup group1 = appendGroup(info, DocAdvanced);
+ for (int i = 0; Advanced[i]; i+=2){
+ QString txt = stringFromNode(base, Advanced[i]);
+ if (!txt.isEmpty()){
+ // A silly way to do it...
+ switch (i){
+ case 2:
+ case 4:
+ case 6:
+ getDateTime(group1, Advanced[i], txt);
+ break;
+ case 14:
+ getEditingTime(group1, Advanced[i], txt);
+ break;
+ default:
+ appendItem(group1, Advanced[i], txt);}
+ }
+ }
+
+ QDomNode dstat = base.namedItem(metadocstat);
+
+ KFileMetaInfoGroup group2 = appendGroup(info, DocStatistics);
+ if (!dstat.isNull() && dstat.isElement()){
+ QDomElement dinfo = dstat.toElement();
+ for (int i = 0; Statistics[i]; i+=2)
+ addAttributeInfo(dinfo, group2, Statistics[i] );
+ }
+
+
+ QDomNodeList userList = base.elementsByTagName( metauserdef );
+
+ KFileMetaInfoGroup groupuser = appendGroup(info, UserDefined);
+
+ for (uint i = 0; i < userList.length(); i++){
+ QDomNode node = userList.item(i);
+ if (node.isElement()){
+ appendItem(groupuser,
+ node.toElement().attribute(metaname,
+ QString("User %1").arg(i)),
+ node.toElement().text());
+ }
+ }
+ return true;
+}
+
+QString KOfficePlugin::stringFromNode(const QDomNode &node, const QString &name)
+{
+ QString value = node.namedItem(name).toElement().text();
+ return value.isEmpty() ? QString::null : value;
+}
+
+void KOfficePlugin::addAttributeInfo(const QDomElement & elem, KFileMetaInfoGroup & group, const QString &attributeName)
+{
+ if (!elem.hasAttribute(attributeName)){
+ return;
+ }
+ QString m_attribute = elem.attribute(attributeName, "0");
+ if (m_attribute == "0")
+ return;
+ appendItem(group, attributeName, m_attribute);
+}
+
+bool KOfficePlugin::writeTextNode(QDomDocument & doc,
+ QDomNode & parentNode,
+ const QString &nodeName,
+ const QString &value) const
+{
+ if (parentNode.toElement().isNull()){
+ kdDebug(7034) << "Parent node is Null or not an Element, cannot write node "
+ << nodeName << endl;
+ return false;
+ }
+
+ // If the node does not exist, we create it...
+ if (parentNode.namedItem(nodeName).isNull())
+ QDomNode ex = parentNode.appendChild(doc.createElement(nodeName));
+
+ // Now, we are sure we have a node
+ QDomElement nodeA = parentNode.namedItem(nodeName).toElement();
+
+ // Ooops... existing node were not of the good type...
+ if (nodeA.isNull()){
+ kdDebug(7034) << "Wrong type of node " << nodeName << ", should be Element"
+ << endl;
+ return false;
+ }
+
+ QDomText txtNode = doc.createTextNode(value);
+
+ // If the node has already Text Child, we replace it.
+ if (nodeA.firstChild().isNull())
+ nodeA.appendChild(txtNode);
+ else
+ nodeA.replaceChild( txtNode, nodeA.firstChild());
+ return true;
+}
+
+bool KOfficePlugin::writeInfo( const KFileMetaInfo& info) const
+{
+ bool no_errors = true;
+ QDomDocument doc = getMetaDocument(info.path());
+ QDomElement base = getBaseNode(doc).toElement();
+ if (base.isNull())
+ return false;
+ for (int i = 0; Information[i]; i+=2)
+ no_errors = no_errors &&
+ writeTextNode(doc, base, Information[i],
+ info[DocumentInfo][Information[i]].value().toString());
+ // If we need a meta:keywords container, we create it.
+ if (base.namedItem(metakeywords).isNull())
+ base.appendChild(doc.createElement(metakeywords));
+ QDomNode metaKeyNode = base.namedItem(metakeywords);
+
+ QDomNodeList childs = doc.elementsByTagName(metakeyword);
+ for (int i = childs.length(); i >= 0; --i){
+ metaKeyNode.removeChild( childs.item(i) );
+ }
+ QStringList keywordList = QStringList::split(",", info[DocumentInfo][metakeyword].value().toString().stripWhiteSpace(), false);
+ for ( QStringList::Iterator it = keywordList.begin(); it != keywordList.end(); ++it ) {
+ QDomElement elem = doc.createElement(metakeyword);
+ metaKeyNode.appendChild(elem);
+ elem.appendChild(doc.createTextNode((*it).stripWhiteSpace()));
+ }
+
+ // Now, we store the user-defined data
+ QDomNodeList theElements = base.elementsByTagName(metauserdef);
+ for (uint i = 0; i < theElements.length(); i++)
+ {
+ QDomElement el = theElements.item(i).toElement();
+ if (el.isNull()){
+ kdDebug(7034) << metauserdef << " is not an Element" << endl;
+ no_errors = false;
+ }
+
+ QString s = info[UserDefined][el.attribute(metaname)].value().toString();
+ if (s != el.text()){
+ QDomText txt = doc.createTextNode(s);
+ if (!el.firstChild().isNull())
+ el.replaceChild(txt, el.firstChild());
+ else
+ el.appendChild(txt);
+ }
+ }
+
+ if (!no_errors){
+ kdDebug(7034) << "Errors were found while building " << metafile
+ << " for file " << info.path() << endl;
+ // It is safer to avoid to manipulate meta.xml if errors, we abort.
+ return false;
+ }
+ writeMetaData(info.path(), doc);
+ return true;
+}
+
+/**
+ * This function recreate a zip in dest with all files from src
+ * except meta.xml
+ */
+bool copyZipToZip( const KZip * src, KZip * dest)
+{
+ KArchiveDirectory * src_dir;
+ KArchiveFile * input_file;
+ QPtrStack<KArchiveDirectory> src_dirStack ;
+ QStringList dirEntries;
+ QStringList curDirName;
+ QStringList::Iterator it;
+ KArchiveEntry* curEntry;
+ QString filename = QString::null;
+
+ src_dirStack.push ( src->directory() );
+
+ do {
+ src_dir = src_dirStack.pop();
+ curDirName.append(src_dir->name());
+ dirEntries = src_dir->entries();
+
+ /* We now iterate over all entries and create entries in dest */
+ for ( it = dirEntries.begin(); it != dirEntries.end(); ++it ) {
+ if ( *it == metafile )
+ continue;
+ curEntry = src_dir->entry( *it );
+
+ if (curEntry->isFile()) {
+ input_file = dynamic_cast<KArchiveFile*>( curEntry );
+ QByteArray b = input_file->data();
+ if (curDirName.isEmpty() || src_dir->name()=="/"){
+ filename = *it;
+ } else {
+ filename = curDirName.join("/") + "/" + *it;
+ }
+ dest->writeFile(filename , QString::null, QString::null, b.count(), b.data() );
+ } else
+ if (curEntry->isDirectory()) {
+ src_dirStack.push( dynamic_cast<KArchiveDirectory*>( curEntry ) );
+ }
+ else {
+ kdDebug(7034) << *it << " is a unknown type. Aborting." << endl;
+ return false;
+ }
+ }
+ curDirName.pop_back();
+ } while ( ! src_dirStack.isEmpty() );
+ return true;
+}
+
+bool KOfficePlugin::writeMetaData(const QString & path,
+ const QDomDocument &doc) const
+{
+ KTempFile tmp_file;
+ tmp_file.setAutoDelete(true);
+ KZip * m_zip = new KZip(tmp_file.name());
+ KZip * current= new KZip(path);
+ /* To correct problem with OOo 1.1, we have to recreate the file from scratch */
+ if (!m_zip->open(IO_WriteOnly) || !current->open(IO_ReadOnly) )
+ return false;
+ QCString text = doc.toCString();
+ m_zip->setCompression(KZip::DeflateCompression);
+ if (!copyZipToZip(current, m_zip))
+ return false;
+ m_zip->writeFile(metafile, QString::null, QString::null,text.length(),
+ text);
+ delete current;
+ delete m_zip;
+ // NULL as third parameter is not good, but I don't know the Window ID
+ // That is only to avoid the deprecated warning at compile time
+ if (!KIO::NetAccess::upload( tmp_file.name(), KURL(path), NULL)){
+ kdDebug(7034) << "Error while saving " << tmp_file.name() << " as " << path << endl;
+ return false;
+ }
+ return true;
+}
+
+
+QIODevice* KOfficePlugin::getData(KArchive &m_zip, int fileMode) const
+{
+
+ if ( !m_zip.open(fileMode) || !m_zip.directory())
+ return 0;
+
+ const KArchiveEntry* entry = m_zip.directory()->entry( metafile );
+ if (!entry || entry->isDirectory())
+ return 0;
+
+ const KZipFileEntry* f = static_cast<const KZipFileEntry *>(entry);
+ if (!f)
+ return 0;
+ return f->device();
+}
+
+QDomDocument KOfficePlugin::getMetaDocument(const QString &path) const
+{
+ QDomDocument doc;
+ KZip m_zip(path);
+ QIODevice * io = getData(m_zip, IO_ReadOnly);
+ if (!io || !io->isReadable())
+ return doc;
+ QString errorMsg;
+ int errorLine, errorColumn;
+ if ( !doc.setContent( io, &errorMsg, &errorLine, &errorColumn ) ){
+ kdDebug(7034) << "Error " << errorMsg.latin1()
+ << "while getting XML content at line "
+ << errorLine << ", column "<< errorColumn << endl;
+ delete io;
+ return doc;
+ }
+ delete io;
+ return doc;
+}
+
+QDomNode KOfficePlugin::getBaseNode(const QDomDocument &doc) const
+{
+ return
+ doc.namedItem("office:document-meta").namedItem("office:meta");
+}
+
+QValidator * KOfficePlugin::createValidator(const QString &, /* mimetype */
+ const QString & , /* group */
+ const QString &key,
+ QObject * parent,
+ const char * name ) const
+{
+ if (key == dclanguage)
+ return new QRegExpValidator(QRegExp("[a-zA-Z-]{1,5}"),
+ parent, name);
+ return 0;
+}
+
+#include "kfile_ooo.moc"